更新航线和航点相关API,优化任务列表组件,添加滚动加载功能,修复Cesium初始化问题,调整样式和结构。

This commit is contained in:
MR-ZC 2025-03-23 17:40:02 +08:00
parent 492df1cb5f
commit e015679b95
11 changed files with 232 additions and 41 deletions

View File

@ -4,3 +4,5 @@ globs:
alwaysApply: true alwaysApply: true
--- ---
请使用中文回答我的问题 请使用中文回答我的问题
项目中使用了vue3、element-plus

View File

@ -1,14 +1,13 @@
import http from "@/http" import http from "@/http"
import { to } from "await-to-js" import { to } from "await-to-js"
/**航点数据 */
export const queryPointRoutePreviewParam = (taskId: number) => to<any>( /**获取航线列表 */
http.get("/pro_api/foreign/task/queryPointRoutePreviewParam", { export const queryFlightRoutes = (params: any) => to<any>(
params: { taskId } http.get('/api/flight-routes', { params })
})) )
/**航面数据 */ /**获取航点航线预览参数*/
export const queryRegionalPlanningPreviewParam = (taskId: number) => to<any>( export const queryWaypointRoutePreview= (params: any) => to<any>(
http.get('/pro_api/foreign/task/queryRegionalPlanningPreviewParam', { http.get('/api/waypoint-route-preview', { params })
params: { taskId } )
}))

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="40" height="40" viewBox="0 0 40 40"><defs><clipPath id="master_svg0_61_04608"><rect x="0" y="0" width="40" height="40" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_61_04608)"><g><path d="M5,16.5225Q5,25.2175,20.65,40Q36.305,25.2175,36.305,16.5225C36.305,7.825,31.0875,0,20.65,0C10.217500000000001,0,5,7.825,5,16.5225ZM20.650399999999998,34.9405Q32.6686,23.0079,32.6686,16.5225Q32.6686,10.91,29.6308,7.36641Q26.4331,3.63636,20.65,3.63636Q14.87048,3.63636,11.67385,7.36609Q8.63636,10.9101,8.63636,16.5225Q8.63636,23.0081,20.650399999999998,34.9405Z" fill-rule="evenodd" fill="#0075FF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M9.45458984375,15.63466953125C9.45458684646,21.85796953125,14.49878984375,26.90346953125,20.722489843749997,26.90566953125L20.722489843749997,26.90926953125C26.95028984375,26.91136953125,32.000089843750004,21.86376953125,32.000089843750004,15.63646953125C32.000089843750004,9.409219531249999,26.95028984375,4.36161965125,20.722489843749997,4.36376953125C14.49878984375,4.36591642125,9.45458984375,9.41144953125,9.45458984375,15.63466953125Z" fill="#0075FF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M27,14.98043L26.4726,16.49239Q24.0351,15.54565,22.56689,14.40109Q21.98246,14.83913,21.02741,15.40435Q21.74013,15.58804,22.83059,15.95544Q23.92105,16.32283,24.6338,16.60544L24.1206,17.99022Q23.47917,17.707610000000003,22.29605,17.30489Q21.112940000000002,16.902169999999998,20.28618,16.69022L20.62829,15.61631Q20.029609999999998,15.9413,19.00329,16.40761L18.36184,15.05109Q20.21491,14.288039999999999,21.4693,13.42609Q20.827849999999998,12.76196,20.28618,11.98478Q19.70176,12.77609,19.16009,13.35543L18.36184,12.32391Q19.14583,11.50435,19.87281,10.28206Q20.59978,9.05978,20.9989,8L22.25329,8.339131Q22.13925,8.63587,21.88268,9.22935L25.8169,9.22935L25.8169,10.67065Q24.790599999999998,12.39457,23.66447,13.46848Q25.032899999999998,14.33043,27,14.98043ZM14.555921,13.44022L14.213816,11.98478Q14.399123,11.91413,14.484649,11.83641Q14.570175,11.758700000000001,14.712719,11.575Q14.969298,11.26413,15.51809,10.17609Q16.06689,9.08804,16.45175,8.0423916L17.70614,8.508696Q17.33553,9.39891,16.81524,10.38804Q16.29496,11.37717,15.91009,11.91413L17.10746,11.82935Q17.64912,10.727170000000001,17.94847,10.03478L19.10307,10.57174Q18.61842,11.63152,17.84156,13.030439999999999Q17.06469,14.42935,16.53728,15.26304L18.27632,14.89565L18.30482,16.3087Q18.09101,16.32283,17.27138,16.47826Q16.45175,16.633699999999997,15.79605,16.775Q15.49671,16.831519999999998,15.311399999999999,16.873910000000002L14.855263,16.9587L14.513158,15.48913Q14.826754,15.36196,14.947917,15.270109999999999Q15.06908,15.17826,15.26864,14.90978Q15.724779999999999,14.288039999999999,16.33772,13.228259999999999Q15.61075,13.27065,15.19737,13.327169999999999Q14.826754,13.36957,14.555921,13.44022ZM22.52412,12.59239Q23.393639999999998,11.78696,24.1634,10.642389999999999L21.1557,10.642389999999999L21.07018,10.79783Q21.71162,11.84348,22.52412,12.59239ZM18.80373,18.4707L18.83224,19.247799999999998Q17.93421,19.3891,16.48739,19.636400000000002Q15.04057,19.883699999999997,14.128289,20.0533L14,18.6261L15.98136,18.2728Q18.49013,17.80652,18.78947,17.76413L18.80373,18.4707ZM19.64474,17.77826Q20.6568,18.0467,22.324559999999998,18.5766Q23.99232,19.1065,25.2039,19.5304L24.7621,21Q23.393639999999998,20.4772,21.82566,19.9402Q20.25768,19.4033,19.23136,19.163L19.64474,17.77826Z" fill="#FFFFFF" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="36" height="36" viewBox="0 0 36 36"><defs><clipPath id="master_svg0_61_04678"><rect x="0" y="0" width="36" height="36" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_61_04678)"><g><path d="M4.5,14.8703Q4.5,22.6958,18.585,36Q32.674499999999995,22.6958,32.674499999999995,14.8703C32.674499999999995,7.0425,27.9788,0,18.585,0C9.19575,0,4.5,7.0425,4.5,14.8703ZM18.5853,32.3657Q30.0563,21.0969,30.0563,14.8703Q30.0563,9.57683,27.1647,6.20376Q24.0908,2.61818,18.585,2.61818Q13.08236,2.61818,10.00948,6.20353Q7.118180000000001,9.57699,7.118180000000001,14.8703Q7.118180000000001,21.097,18.5853,32.3657Z" fill-rule="evenodd" fill="#0075FF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M8.5091552734375,14.07104609375C8.5091525758775,19.672046093749998,13.0489352734375,24.21294609375,18.6502552734375,24.21494609375L18.6502552734375,24.21814609375C24.2552552734375,24.22004609375,28.8000552734375,19.67724609375,28.8000552734375,14.07274609375C28.8000552734375,8.46815609375,24.2552552734375,3.92531119375,18.6502552734375,3.92724609375C13.0489352734375,3.92917829375,8.5091552734375,8.47015609375,8.5091552734375,14.07104609375Z" fill="#0075FF" fill-opacity="1" style="mix-blend-mode:passthrough"/></g><g><path d="M20.38158,17.46145Q22.275219999999997,17.46145,24,17.340310000000002L23.7467,18.5639Q21.79276,18.660800000000002,20.11623,18.660800000000002Q19.52522,18.660800000000002,18.29496,18.6366Q16.91996,18.6002,16.09375,18.261Q15.26754,17.921799999999998,14.78509,17.073790000000002Q14.55592,17.885460000000002,14.08553,19L13,18.576Q13.398026,17.6674,13.609101,17.00716Q13.820175,16.34692,13.922697,15.5413Q14.025220000000001,14.73568,14.025220000000001,13.57269L15.171050000000001,13.63326Q15.171050000000001,14.663,15.08662,15.38987Q15.35197,16.17731,15.79825,16.62555L15.79825,13.015419999999999L13.51864,13.015419999999999L13.51864,11.82819L15.56908,11.82819L15.56908,10.53194L13.928728,10.53194L13.928728,9.35683L15.56908,9.35683L15.56908,8L16.78728,8.0484585L16.78728,9.35683L18.29496,9.35683L18.29496,10.53194L16.78728,10.53194L16.78728,11.82819L18.63268,11.82819L18.63268,13.015419999999999L16.98026,13.015419999999999L16.98026,14.43282L18.512059999999998,14.43282L18.512059999999998,15.632159999999999L16.98026,15.632159999999999L16.98026,17.243389999999998Q17.51096,17.38877,18.43969,17.42511Q19.41667,17.46145,20.38158,17.46145ZM21.853070000000002,11.501100000000001L21.853070000000002,9.79295L18.80154,9.79295L18.80154,8.569383L23.0351,8.569383L23.0351,12.71255L20.00768,12.71255L20.00768,14.663Q20.00768,15.0022,20.05592,15.14758Q20.10417,15.292950000000001,20.230809999999998,15.347470000000001Q20.35746,15.40198,20.65899,15.426210000000001Q20.79167,15.43833,21.05702,15.43833Q21.334429999999998,15.43833,21.45504,15.426210000000001Q21.80482,15.38987,21.949559999999998,15.347470000000001Q22.0943,15.30507,22.16667,15.177859999999999Q22.23903,15.05066,22.287280000000003,14.75991Q22.34759,14.43282,22.38377,13.89978L23.5537,14.36013Q23.5417,14.663,23.4331,15.19603Q23.3366,15.813880000000001,23.1738,16.11674Q23.011,16.419600000000003,22.69737,16.54075Q22.38377,16.66189,21.7807,16.69824Q21.55153,16.71035,21.04496,16.71035Q20.53838,16.71035,20.297150000000002,16.69824Q19.7182,16.674010000000003,19.404600000000002,16.510460000000002Q19.09101,16.34692,18.95833,15.98954Q18.82566,15.632159999999999,18.82566,14.96586L18.82566,11.501100000000001L21.853070000000002,11.501100000000001Z" fill="#FFFFFF" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -14,7 +14,7 @@
</template> </template>
</titleView> </titleView>
<div class="list_wrapper"> <div class="list_wrapper">
<el-scrollbar> <el-scrollbar ref="scrollbarRef" @wheel.prevent="handleScroll">
<div class="list"> <div class="list">
<eventItem v-for="i in 20"></eventItem> <eventItem v-for="i in 20"></eventItem>
</div> </div>
@ -28,6 +28,7 @@ import { ref } from "vue"
import titleView from "@/components/zfControlPlatform/titleView/index.vue" import titleView from "@/components/zfControlPlatform/titleView/index.vue"
import selectView from "@/components/selectView/index.vue" import selectView from "@/components/selectView/index.vue"
import eventItem from "@/components/zfControlPlatform/eventList/eventItem.vue" import eventItem from "@/components/zfControlPlatform/eventList/eventItem.vue"
const scrollbarRef = ref()
const eventType = ref() const eventType = ref()
const options = ref([ const options = ref([
{ {
@ -52,6 +53,10 @@ const options = ref([
}, },
]) ])
const handleScroll = (e: any) => {
const scrollbar = scrollbarRef.value;
scrollbar.setScrollLeft(scrollbar.wrapRef.scrollLeft + (-e.wheelDelta))
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -69,7 +74,6 @@ const options = ref([
width: 100%; width: 100%;
flex-shrink: 0; flex-shrink: 0;
flex: 1; flex: 1;
// background-color: rgb(222, 175, 100);
display: flex; display: flex;
align-items: center; align-items: center;
overflow-x: auto; overflow-x: auto;

View File

@ -2,7 +2,7 @@
<div class="ZfControlPlatform"> <div class="ZfControlPlatform">
<div class="container_left"> <div class="container_left">
<div class="task"> <div class="task">
<taskList></taskList> <taskList @load-more="loadMoreFlightRoutes" @select-task="handleSelectTask"></taskList>
</div> </div>
<div class="preview"> <div class="preview">
<positionView></positionView> <positionView></positionView>
@ -29,22 +29,72 @@ import positionView from "@/components/zfControlPlatform/positionView/index.vue"
import eventList from "@/components/zfControlPlatform/eventList/index.vue" import eventList from "@/components/zfControlPlatform/eventList/index.vue"
import taskController from "@/components/zfControlPlatform/taskController/index.vue" import taskController from "@/components/zfControlPlatform/taskController/index.vue"
import mainMap from "@/components/zfControlPlatform/mainMap/index.vue" import mainMap from "@/components/zfControlPlatform/mainMap/index.vue"
import { queryPointRoutePreviewParam, queryRegionalPlanningPreviewParam } from "@/api/task" import { queryFlightRoutes, queryWaypointRoutePreview } from "@/api/task"
onMounted(() => {
getFlightRoutes()
})
// //
const pointRoutePreviewParam = ref<any[]>([]) const pointRoutePreviewParam = ref<any[]>([])
provide("pointRoutePreviewParam", pointRoutePreviewParam) provide("pointRoutePreviewParam", pointRoutePreviewParam)
// 线
const flightRoutes = ref<any[]>([])
provide("flightRoutes", flightRoutes)
// //
async function getPointRoutePreviewParam() { const pageParams = ref({
const [error, res] = await queryPointRoutePreviewParam(1) page: 1,
if (error) return console.log("获取航点数据失败==>>", error); per_page: 20,
pointRoutePreviewParam.value = res.data.waypointList hasMore: true,
total: 0
});
//线
async function getFlightRoutes(isLoadMore = false) {
if (!isLoadMore) {
pageParams.value.page = 1;
flightRoutes.value = [];
} }
onMounted(() => {
getPointRoutePreviewParam() const [error, res] = await queryFlightRoutes({
}) page: pageParams.value.page,
});
if (error) return console.log("获取航线列表失败==>>", error);
const { total, items } = res;
if (isLoadMore) {
flightRoutes.value = [...flightRoutes.value, ...items];
} else {
flightRoutes.value = items;
}
pageParams.value.total = total;
pageParams.value.hasMore = flightRoutes.value.length < total;
}
//
const loadMoreFlightRoutes = async () => {
if (!pageParams.value.hasMore) return;
pageParams.value.page += 1;
await getFlightRoutes(true);
};
//
const handleSelectTask = async (task: any) => {
console.log("点击任务==>>", task)
const [error, res] = await queryWaypointRoutePreview({
airwayId: task.id
});
if (error) return console.log("获取航点航线预览参数失败==>>", error);
console.log("获取航点航线预览参数成功==>>", res)
}
</script> </script>

View File

@ -50,6 +50,10 @@ import { onMounted, onUnmounted, ref } from "vue"
import type { Ref } from "vue" import type { Ref } from "vue"
import "cesium/Build/Cesium/Widgets/widgets.css"; import "cesium/Build/Cesium/Widgets/widgets.css";
import type { PointRoute } from "@/types/map" import type { PointRoute } from "@/types/map"
import markerIcon from "@/assets/img/start_point.svg"
import endMarkerIcon from "@/assets/img/end_point.svg"
// import aircraftMarkerIcon from "@/assets/img/aircraft.svg"
let viewer: Viewer | null = null; let viewer: Viewer | null = null;
const afterRender = new Set<() => void>() const afterRender = new Set<() => void>()
@ -115,6 +119,10 @@ function drawPointRoutePreview(pointRoute: PointRoute[]) {
// //
viewer.entities.removeAll(); viewer.entities.removeAll();
const positions = pointRoute.map(item => (
Cesium.Cartesian3.fromDegrees(item.wayPointLongitude, item.wayPointLatitude, item.wayPointAltitude)
));
// 线 // 线
const polyline = viewer.entities.add({ const polyline = viewer.entities.add({
polyline: { polyline: {
@ -130,6 +138,29 @@ function drawPointRoutePreview(pointRoute: PointRoute[]) {
} }
}); });
// Marker
viewer.entities.add({
position: positions[0],
billboard: {
image: markerIcon,
width: 36,
height: 36,
pixelOffset: new Cesium.Cartesian2(0, -18)
}
});
// Marker
viewer.entities.add({
position: positions[positions.length - 1],
billboard: {
image: endMarkerIcon,
width: 36,
height: 36,
pixelOffset: new Cesium.Cartesian2(0, -18)
}
});
flyToPointRoute(viewer, pointRoute) flyToPointRoute(viewer, pointRoute)
} }

View File

@ -9,18 +9,87 @@
</template> </template>
</titleView> </titleView>
<div class="list"> <div class="list">
<el-scrollbar> <el-scrollbar ref="scrollbarRef" view-style="height: 100%" @scroll="handleScroll">
<ongoingTask></ongoingTask> <!-- <ongoingTask></ongoingTask> -->
<!-- <taskItem v-for="i in 10"></taskItem> --> <taskItem
v-for="item in flightRoutes"
:data="item"
:is-active="selectedTaskId === item.id"
@click="handleTaskClick(item)"
></taskItem>
</el-scrollbar> </el-scrollbar>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject, watch, ref, onMounted, onUnmounted } from 'vue';
import type { Ref } from 'vue';
import taskItem from './taskItem.vue'; import taskItem from './taskItem.vue';
import ongoingTask from './ongoingTask.vue'; import ongoingTask from './ongoingTask.vue';
import titleView from "@/components/zfControlPlatform/titleView/index.vue" import titleView from "@/components/zfControlPlatform/titleView/index.vue"
import type { TaskItem } from "@/types/task"
const flightRoutes = inject<Ref<TaskItem[]>>("flightRoutes", ref<TaskItem[]>([]))
const scrollbarRef = ref();
const emit = defineEmits(['loadMore', 'selectTask']);
const selectedTaskId = ref<string | number>('');
const handleScroll = (e: any) => {
const scrollbar = scrollbarRef.value;
if (!scrollbar) return;
const wrapElement = scrollbar.wrapRef;
if (!wrapElement) return;
const scrollTop = wrapElement.scrollTop;
const clientHeight = wrapElement.clientHeight;
const scrollHeight = wrapElement.scrollHeight;
if (typeof scrollTop === 'number' &&
typeof clientHeight === 'number' &&
typeof scrollHeight === 'number') {
const bottomDistance = scrollHeight - scrollTop - clientHeight;
if (bottomDistance <= 2) {
emit('loadMore');
}
}
};
const handleTaskClick = (item: TaskItem) => {
selectedTaskId.value = item.id;
emit('selectTask', item);
};
onMounted(() => {
// DOM
const wrap = scrollbarRef.value?.wrap;
if (wrap) {
wrap.addEventListener('scroll', handleScroll);
}
});
//
onUnmounted(() => {
const wrap = scrollbarRef.value?.wrap;
if (wrap) {
wrap.removeEventListener('scroll', handleScroll);
}
});
watch(() => flightRoutes, () => {
if (flightRoutes?.value.length > 0) {
console.log("航线列表==>>", flightRoutes?.value)
}
}, {
immediate: true,
deep: true
})
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -52,8 +121,7 @@ import titleView from "@/components/zfControlPlatform/titleView/index.vue"
flex: 1; flex: 1;
height: 0; height: 0;
padding: 16px 20px; padding: 16px 20px;
overflow-y: auto; overflow: hidden;
height: 0;
} }
} }

View File

@ -1,5 +1,9 @@
<template> <template>
<div class="task_item"> <div
class="task-item"
:class="{ 'active': isActive }"
@click="handleClick"
>
<div class="icon"> <div class="icon">
<!-- <img src="@/assets/img/zf_task_path_icon.svg" alt=""> --> <!-- <img src="@/assets/img/zf_task_path_icon.svg" alt=""> -->
<img src="@/assets/img/zf_task_path_activate_icon.svg" alt=""> <img src="@/assets/img/zf_task_path_activate_icon.svg" alt="">
@ -7,22 +11,37 @@
<div class="item_content"> <div class="item_content">
<div> <div>
<span class="label">航线</span> <span class="label">航线</span>
<span>G244平罗段</span> <span>{{ data.name }}</span>
</div> </div>
<div> <!-- <div>
<span class="label">桩号</span> <span class="label">桩号</span>
<span>k21+800-K79+448</span> <span>{{ data.description }}</span>
</div> </div> -->
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { TaskItem } from "@/types/task"
interface Props {
data: TaskItem;
isActive?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
isActive: false
});
const emit = defineEmits(['click']);
const handleClick = () => {
emit('click');
};
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.task_item { .task-item {
height: 60px; height: 60px;
display: flex; display: flex;
align-items: center; align-items: center;
@ -54,5 +73,9 @@
color: rgba(255, 255, 255, 0.65); color: rgba(255, 255, 255, 0.65);
} }
} }
&.active {
background-color: #034da5;
}
} }
</style> </style>

View File

@ -9,14 +9,14 @@ const request = axios.create({
}) })
request.interceptors.request.use((config) => { request.interceptors.request.use((config) => {
config.headers.Authorization = userInfoStore.token config.headers.Authorization = `Bearer ${userInfoStore.token}`
return config return config
}) })
request.interceptors.response.use( request.interceptors.response.use(
(res: AxiosResponse) => { (res: AxiosResponse) => {
if (res.data.code === 200) { if (res.data.code === 200) {
return res.data return res.data.data
} else { } else {
ElMessage({ ElMessage({
type: "error", type: "error",

12
src/types/task.ts Normal file
View File

@ -0,0 +1,12 @@
export interface TaskItem {
description: string;
edit: boolean;
id: string;
loading_name: string;
name: string;
route_id: string;
type: string;
uav_loading_id: string;
update_time: string | null;
username: string | null;
}