。
This commit is contained in:
parent
115f669655
commit
492df1cb5f
6
.cursor/rules/init.mdc
Normal file
6
.cursor/rules/init.mdc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
请使用中文回答我的问题
|
||||||
@ -1 +1 @@
|
|||||||
VITE_BASE_URL = "192.168.100.1"
|
VITE_BASE_URL = "https://169.254.92.7:5173"
|
||||||
1102
package-lock.json
generated
1102
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,14 +11,17 @@
|
|||||||
"type-check": "vue-tsc --build"
|
"type-check": "vue-tsc --build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@vitejs/plugin-basic-ssl": "^2.0.0",
|
"@vitejs/plugin-basic-ssl": "^2.0.0",
|
||||||
"await-to-js": "^3.0.0",
|
"await-to-js": "^3.0.0",
|
||||||
"axios": "^1.8.1",
|
"axios": "^1.8.1",
|
||||||
"cesium": "^1.127.0",
|
"cesium": "^1.127.0",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
"element-plus": "^2.9.5",
|
"element-plus": "^2.9.5",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"less": "^4.2.2",
|
"less": "^4.2.2",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
|
"pinia-plugin-persistedstate": "^4.2.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0"
|
||||||
},
|
},
|
||||||
@ -28,6 +31,7 @@
|
|||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"esbuild": "^0.25.1",
|
||||||
"npm-run-all2": "^7.0.2",
|
"npm-run-all2": "^7.0.2",
|
||||||
"typescript": "~5.7.3",
|
"typescript": "~5.7.3",
|
||||||
"vite": "^6.1.0",
|
"vite": "^6.1.0",
|
||||||
|
|||||||
14
src/api/task.ts
Normal file
14
src/api/task.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import http from "@/http"
|
||||||
|
import { to } from "await-to-js"
|
||||||
|
/**航点数据 */
|
||||||
|
export const queryPointRoutePreviewParam = (taskId: number) => to<any>(
|
||||||
|
http.get("/pro_api/foreign/task/queryPointRoutePreviewParam", {
|
||||||
|
params: { taskId }
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
/**航面数据 */
|
||||||
|
export const queryRegionalPlanningPreviewParam = (taskId: number) => to<any>(
|
||||||
|
http.get('/pro_api/foreign/task/queryRegionalPlanningPreviewParam', {
|
||||||
|
params: { taskId }
|
||||||
|
}))
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import request from "@/http";
|
import http from "@/http";
|
||||||
import to from "await-to-js"
|
import to from "await-to-js"
|
||||||
|
|
||||||
/**登录 */
|
/**登录 */
|
||||||
export const login = (data: any) => to(
|
export const login = (data: any) => to<any>(
|
||||||
request.post("/login", {
|
http.post("/api/login", {
|
||||||
|
username: data.username,
|
||||||
}
|
password: data.password
|
||||||
))
|
}))
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
<div class="header_main">
|
<div class="header_main">
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<div v-for="tab in props.tabs" @click="changeType(tab)" class="tab"
|
<div v-for="tab in props.tabs" @click="changeType(tab)" class="tab"
|
||||||
:class="{ checked: tab.id == props.id }">
|
:class="{ checked: tab.id == id }">
|
||||||
<span>{{ tab.label }}</span>
|
<span>{{ tab.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -18,13 +18,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue"
|
// import { ref } from "vue"
|
||||||
|
|
||||||
const props = defineProps(['tabs', 'id'])
|
const props = defineProps(['tabs'])
|
||||||
const emit = defineEmits(["update:id"])
|
const id = defineModel<number>('id')
|
||||||
function changeType({ id, disabled }: { id: number, disabled: boolean }) {
|
function changeType({ id:id_, disabled }: { id: number, disabled: boolean }) {
|
||||||
if (disabled) return;
|
if (disabled) return;
|
||||||
emit("update:id", id)
|
console.log("id_", id_)
|
||||||
|
id.value = id_
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -40,7 +41,6 @@ function changeType({ id, disabled }: { id: number, disabled: boolean }) {
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 57px;
|
height: 57px;
|
||||||
background-color: antiquewhite;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
|||||||
@ -37,7 +37,6 @@
|
|||||||
width: 249px;
|
width: 249px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
display: block;
|
display: block;
|
||||||
background-color: antiquewhite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
@ -51,6 +51,7 @@ const options = ref([
|
|||||||
value: 4
|
value: 4
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@ -10,9 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="container_center">
|
<div class="container_center">
|
||||||
<div class="map_wrap">
|
<div class="map_wrap">
|
||||||
<!-- 悬浮窗 -->
|
<mainMap></mainMap>
|
||||||
<img src="@/assets/img/image@1x.png" alt="">
|
|
||||||
<!-- <mainMap></mainMap> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="event_list_wrap">
|
<div class="event_list_wrap">
|
||||||
<eventList></eventList>
|
<eventList></eventList>
|
||||||
@ -25,11 +23,30 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref, provide, onMounted } from "vue"
|
||||||
import taskList from "@/components/zfControlPlatform/taskList/index.vue"
|
import taskList from "@/components/zfControlPlatform/taskList/index.vue"
|
||||||
import positionView from "@/components/zfControlPlatform/positionView/index.vue"
|
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"
|
||||||
|
|
||||||
|
|
||||||
|
//航点数据
|
||||||
|
const pointRoutePreviewParam = ref<any[]>([])
|
||||||
|
provide("pointRoutePreviewParam", pointRoutePreviewParam)
|
||||||
|
|
||||||
|
//获取航点数据
|
||||||
|
async function getPointRoutePreviewParam() {
|
||||||
|
const [error, res] = await queryPointRoutePreviewParam(1)
|
||||||
|
if (error) return console.log("获取航点数据失败==>>", error);
|
||||||
|
pointRoutePreviewParam.value = res.data.waypointList
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
getPointRoutePreviewParam()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@ -38,30 +38,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div width="600px" height="500px" class="cesiumContainer" id="cesiumContainer"></div>
|
<div class="cesiumContainer" id="cesiumContainer" ref="cesiumContainerRef"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { inject, watch, ref as vueRef, nextTick } from "vue"
|
||||||
import * as Cesium from "cesium"
|
import * as Cesium from "cesium"
|
||||||
import { onMounted, ref } from "vue"
|
import type { Viewer, Property } from "cesium"
|
||||||
|
import { onMounted, onUnmounted, 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"
|
||||||
|
|
||||||
let viewer;
|
let viewer: Viewer | null = null;
|
||||||
let handler;
|
const afterRender = new Set<() => void>()
|
||||||
let polyline;
|
const cesiumContainerRef = vueRef<HTMLElement | null>(null);
|
||||||
let positions = [];
|
const pointRoutePreviewParam = inject<Ref<PointRoute[]>>("pointRoutePreviewParam", ref([]))
|
||||||
let isDrawing = ref(false);
|
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
|
// 确保Cesium的基础URL和访问令牌设置正确
|
||||||
(window as any).CESIUM_BASE_URL = "/assets/Cesium/"
|
(window as any).CESIUM_BASE_URL = "/assets/Cesium/"
|
||||||
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkNWM3YTc2OS1iMjQwLTRiYTUtOGFhNC1lNmFhNDRlN2QyODIiLCJpZCI6MjgxNzc2LCJpYXQiOjE3NDEyNTI2Mjh9.6H_pbpxLPYMBGveKGq9pek2rYlrFvQeWcnHGzVkmb00'
|
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkNWM3YTc2OS1iMjQwLTRiYTUtOGFhNC1lNmFhNDRlN2QyODIiLCJpZCI6MjgxNzc2LCJpYXQiOjE3NDEyNTI2Mjh9.6H_pbpxLPYMBGveKGq9pek2rYlrFvQeWcnHGzVkmb00'
|
||||||
Cesium.Ion.defaultAccessToken = token
|
Cesium.Ion.defaultAccessToken = token
|
||||||
|
|
||||||
viewer = new Cesium.Viewer('cesiumContainer',
|
await nextTick();
|
||||||
{
|
|
||||||
// terrain: Terrain.fromWorldTerrain(),
|
// 再次检查元素是否存在
|
||||||
// terrainProvider: undefined,
|
if (!cesiumContainerRef.value) {
|
||||||
|
console.error('cesiumContainer元素不存在');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检查是否已经有Cesium实例
|
||||||
|
if (viewer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用ref直接引用DOM元素
|
||||||
|
viewer = new Cesium.Viewer(cesiumContainerRef.value, {
|
||||||
selectionIndicator: false, // 禁用选择指示器
|
selectionIndicator: false, // 禁用选择指示器
|
||||||
infoBox: false, // 禁用信息框
|
infoBox: false, // 禁用信息框
|
||||||
navigationHelpButton: false, // 禁用帮助按钮
|
navigationHelpButton: false, // 禁用帮助按钮
|
||||||
@ -72,13 +88,11 @@ async function init() {
|
|||||||
animation: false, // 禁用动画控件
|
animation: false, // 禁用动画控件
|
||||||
geocoder: false, // 禁用地理编码器
|
geocoder: false, // 禁用地理编码器
|
||||||
fullscreenButton: false // 禁用全屏按钮
|
fullscreenButton: false // 禁用全屏按钮
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// 初始化事件处理器
|
// 设置相机视图
|
||||||
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
||||||
viewer.camera.setView({
|
viewer.camera.setView({
|
||||||
destination: Cesium.Cartesian3.fromDegrees(113.35, 23.11, 10000),
|
destination: Cesium.Cartesian3.fromDegrees(106.64219088, 38.75395285, 2000),
|
||||||
orientation: {
|
orientation: {
|
||||||
heading: Cesium.Math.toRadians(0.0),
|
heading: Cesium.Math.toRadians(0.0),
|
||||||
pitch: Cesium.Math.toRadians(-15.0),
|
pitch: Cesium.Math.toRadians(-15.0),
|
||||||
@ -86,32 +100,108 @@ async function init() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// viewer.fullscreenButton()
|
|
||||||
// setTimeout(() => {
|
|
||||||
// viewer.camera.flyTo({
|
|
||||||
// destination: Cartesian3.fromDegrees(113.35, 23.11, 10000),
|
|
||||||
// orientation: {
|
|
||||||
// heading: CesiumMath.toRadians(0.0),
|
|
||||||
// pitch: CesiumMath.toRadians(-15.0),
|
|
||||||
// roll: CesiumMath.toRadians(0.0)
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }, 5000)
|
|
||||||
|
|
||||||
//3d建筑
|
|
||||||
// const buildingTileset = await createOsmBuildingsAsync();
|
|
||||||
// viewer.scene.primitives.add(buildingTileset);
|
|
||||||
|
|
||||||
viewer.scene.mode = Cesium.SceneMode.SCENE2D; // 切换到2D
|
viewer.scene.mode = Cesium.SceneMode.SCENE2D; // 切换到2D
|
||||||
// viewer.scene.mode = SceneMode.SCENE3D; // 切换到3D
|
viewer.scene.screenSpaceCameraController.maximumZoomDistance = 20000;
|
||||||
// viewer.scene.screenSpaceCameraController.minimumZoomDistance = 100;
|
|
||||||
// viewer.scene.screenSpaceCameraController.maximumZoomDistance = 20000;
|
console.log('Cesium初始化成功');
|
||||||
// viewer.zoomTo(300)
|
} catch (error) {
|
||||||
|
console.error('初始化Cesium Viewer时出错:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
/**画出预览航线 */
|
||||||
init()
|
function drawPointRoutePreview(pointRoute: PointRoute[]) {
|
||||||
|
if (!viewer) return
|
||||||
|
// 清除之前的实体
|
||||||
|
viewer.entities.removeAll();
|
||||||
|
|
||||||
|
// 创建新的折线
|
||||||
|
const polyline = viewer.entities.add({
|
||||||
|
polyline: {
|
||||||
|
positions: pointRoute.map(item => {
|
||||||
|
return Cesium.Cartesian3.fromDegrees(item.wayPointLongitude, item.wayPointLatitude, item.wayPointAltitude)
|
||||||
|
}),
|
||||||
|
width: 4,
|
||||||
|
material: new Cesium.PolylineDashMaterialProperty({
|
||||||
|
color: Cesium.Color.fromCssColorString("#3a76ff"),
|
||||||
|
dashLength: 20
|
||||||
|
}),
|
||||||
|
clampToGround: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
flyToPointRoute(viewer, pointRoute)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将地图视角飞行到航线点的范围内
|
||||||
|
* @param viewer Cesium Viewer实例
|
||||||
|
* @param pointRoute 航线点数组
|
||||||
|
*/
|
||||||
|
function flyToPointRoute(viewer: Viewer, pointRoute: PointRoute[]) {
|
||||||
|
// 将航线点转换为经纬度坐标数组
|
||||||
|
const positions = pointRoute.map(item => ({
|
||||||
|
longitude: item.wayPointLongitude,
|
||||||
|
latitude: item.wayPointLatitude
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 根据经纬度坐标数组计算包含所有点的最小矩形范围
|
||||||
|
const rectangle = Cesium.Rectangle.fromCartographicArray(
|
||||||
|
positions.map(pos => Cesium.Cartographic.fromDegrees(pos.longitude, pos.latitude))
|
||||||
|
);
|
||||||
|
|
||||||
|
// 计算矩形的宽度和高度,并扩展矩形边界20%以留出视觉空间
|
||||||
|
const width = rectangle.east - rectangle.west;
|
||||||
|
const height = rectangle.north - rectangle.south;
|
||||||
|
const padding = {
|
||||||
|
x: width * 0.2, // 水平方向扩展20%
|
||||||
|
y: height * 0.2 // 垂直方向扩展20%
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建扩展后的新矩形
|
||||||
|
const paddedRectangle = new Cesium.Rectangle(
|
||||||
|
rectangle.west - padding.x, // 向西扩展
|
||||||
|
rectangle.south - padding.y, // 向南扩展
|
||||||
|
rectangle.east + padding.x, // 向东扩展
|
||||||
|
rectangle.north + padding.y // 向北扩展
|
||||||
|
);
|
||||||
|
|
||||||
|
// 使用动画效果将相机飞行到能够完整显示扩展矩形的位置
|
||||||
|
viewer.camera.flyTo({
|
||||||
|
destination: paddedRectangle, // 目标位置为扩展后的矩形
|
||||||
|
duration: 2 // 飞行动画持续2秒
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => pointRoutePreviewParam.value, () => {
|
||||||
|
if (pointRoutePreviewParam.value.length > 0) {
|
||||||
|
if (viewer) {
|
||||||
|
drawPointRoutePreview(pointRoutePreviewParam.value as PointRoute[])
|
||||||
|
} else {
|
||||||
|
afterRender.add(() => {
|
||||||
|
drawPointRoutePreview(pointRoutePreviewParam.value as PointRoute[])
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onMounted(async () => {
|
||||||
|
// 使用nextTick确保DOM已经渲染
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
// 初始化Cesium
|
||||||
|
await init();
|
||||||
|
|
||||||
|
// 执行渲染后的回调函数
|
||||||
|
afterRender.forEach(fn => fn());
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件卸载时清理Cesium
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (viewer) {
|
||||||
|
viewer.destroy();
|
||||||
|
viewer = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,6 @@ import titleView from "@/components/zfControlPlatform/titleView/index.vue"
|
|||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
background-color: antiquewhite;
|
|
||||||
img{
|
img{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@ -1,49 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="step_view">
|
<div class="step_view">
|
||||||
<div class="step">
|
<div class="step" v-for="item in props.taskProgress">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="time">11:49</div>
|
<div class="time">{{ item.time }}</div>
|
||||||
<div class="circle"></div>
|
<div class="circle"></div>
|
||||||
<div class="label">任务前检测</div>
|
<div class="label">{{ item.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status_icon">
|
<div :class="['status_icon', { loading: item.status === 'loading' }]">
|
||||||
<img src="@/assets/img/circle_check.svg" alt="">
|
<img v-if="item.status === 'success'" src="@/assets/img/circle_check.svg" alt="">
|
||||||
</div>
|
<img v-else src="@/assets/img/zf_task_loading_icon.svg" alt="">
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="step">
|
|
||||||
<div class="content">
|
|
||||||
<div class="left">
|
|
||||||
<div class="time">11:49</div>
|
|
||||||
<div class="circle"></div>
|
|
||||||
<div class="label">任务前检测</div>
|
|
||||||
</div>
|
|
||||||
<div class="status_icon">
|
|
||||||
<img src="@/assets/img/circle_check.svg" alt="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="step">
|
|
||||||
<div class="content">
|
|
||||||
<div class="left">
|
|
||||||
<div class="time">11:49</div>
|
|
||||||
<div class="circle"></div>
|
|
||||||
<div class="label">任务前检测</div>
|
|
||||||
</div>
|
|
||||||
<div class="status_icon">
|
|
||||||
<img src="@/assets/img/zf_task_loading_icon.svg" alt="">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
type TaskProgress = {
|
||||||
|
time: string,
|
||||||
|
label: string,
|
||||||
|
status: string
|
||||||
|
}
|
||||||
|
type Props = {
|
||||||
|
taskProgress: TaskProgress[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
taskProgress: () => []
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.step_view {
|
.step_view {
|
||||||
// background-color: antiquewhite;
|
// background-color: antiquewhite;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -100,6 +103,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status_icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
img {
|
||||||
|
animation: loading 1s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 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"
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="task_main">
|
<div class="task_main">
|
||||||
|
|
||||||
<div class="base_info">
|
<div class="base_info">
|
||||||
<div class="info_item">
|
<div class="info_item">
|
||||||
<span class="label">任务名称:</span>
|
<span class="label">任务名称:</span>
|
||||||
@ -21,9 +20,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="step">
|
<div class="step">
|
||||||
<stepView></stepView>
|
<stepView :taskProgress="taskProgress"></stepView>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -31,6 +29,29 @@
|
|||||||
import { ref } from "vue"
|
import { ref } from "vue"
|
||||||
import stepView from "@/components/zfControlPlatform/stepView/index.vue"
|
import stepView from "@/components/zfControlPlatform/stepView/index.vue"
|
||||||
const progress = ref(50)
|
const progress = ref(50)
|
||||||
|
type TaskProgress = {
|
||||||
|
time: string
|
||||||
|
label: string
|
||||||
|
status: "success" | "loading"
|
||||||
|
}
|
||||||
|
const taskProgress = ref<TaskProgress[]>([
|
||||||
|
{
|
||||||
|
time: "11:49",
|
||||||
|
label: "任务前检测",
|
||||||
|
status: "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: "11:49",
|
||||||
|
label: "任务前检测",
|
||||||
|
status: "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: "11:49",
|
||||||
|
label: "任务前检测",
|
||||||
|
status: "loading"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@ -22,7 +22,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background-color: antiquewhite;
|
|
||||||
border-bottom: 1px #3c7bff solid;
|
border-bottom: 1px #3c7bff solid;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
background: linear-gradient(90deg, #0A5CC8 3%, #053B7B 95%);
|
background: linear-gradient(90deg, #0A5CC8 3%, #053B7B 95%);
|
||||||
|
|||||||
@ -1,17 +1,37 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import type { AxiosResponse, AxiosError } from "axios";
|
||||||
|
import useUserInfoStore from "@/stores/user"
|
||||||
|
import { ElMessage } from "element-plus"
|
||||||
|
const userInfoStore = useUserInfoStore()
|
||||||
|
|
||||||
const request = axios.create({
|
const request = axios.create({
|
||||||
baseURL: import.meta.env.VITE_BASE_URL
|
baseURL: import.meta.env.VITE_BASE_URL
|
||||||
})
|
})
|
||||||
|
|
||||||
request.interceptors.request.use((config) => {
|
request.interceptors.request.use((config) => {
|
||||||
|
config.headers.Authorization = userInfoStore.token
|
||||||
return config
|
return config
|
||||||
})
|
})
|
||||||
|
|
||||||
request.interceptors.response.use((res) => {
|
request.interceptors.response.use(
|
||||||
|
(res: AxiosResponse) => {
|
||||||
return res
|
if (res.data.code === 200) {
|
||||||
|
return res.data
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
type: "error",
|
||||||
|
message: res.data.msg || "请求失败"
|
||||||
})
|
})
|
||||||
|
return Promise.reject(res?.data?.msg || "请求失败")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err: AxiosError) => {
|
||||||
|
ElMessage({
|
||||||
|
type: "error",
|
||||||
|
message: (err.response?.data as any)?.msg || "请求失败"
|
||||||
|
})
|
||||||
|
return Promise.reject((err.response?.data as any)?.msg || "请求失败")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export default request
|
export default request
|
||||||
@ -2,6 +2,7 @@ import './assets/style/base.less'
|
|||||||
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
@ -10,7 +11,10 @@ import 'element-plus/dist/index.css'
|
|||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(createPinia())
|
const pinia = createPinia()
|
||||||
|
pinia.use(piniaPluginPersistedstate)
|
||||||
|
|
||||||
|
app.use(pinia)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(ElementPlus)
|
app.use(ElementPlus)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import HomeView from '../views/index/index.vue'
|
import HomeView from '../views/index/index.vue'
|
||||||
|
import useUserInfoStore from "@/stores/user"
|
||||||
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
@ -22,5 +24,19 @@ const router = createRouter({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
const userInfoStore = useUserInfoStore()
|
||||||
|
console.log("token==>>", userInfoStore.token)
|
||||||
|
if (to.path === "/login") {
|
||||||
|
if (userInfoStore.token) {
|
||||||
|
next({ path: "/" })
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!userInfoStore.token) return next({ path: "/login" })
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,22 @@ import { defineStore } from 'pinia'
|
|||||||
|
|
||||||
const useUserInfoStore = defineStore('userInfo', {
|
const useUserInfoStore = defineStore('userInfo', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
token: ""
|
token: "",
|
||||||
|
user_info: {
|
||||||
|
last_login_at: "",
|
||||||
|
last_login_ip: "",
|
||||||
|
login_count: 0,
|
||||||
|
username: ""
|
||||||
|
}
|
||||||
|
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
||||||
}
|
},
|
||||||
|
persist: true
|
||||||
})
|
})
|
||||||
|
|
||||||
export default useUserInfoStore
|
export default useUserInfoStore
|
||||||
|
|||||||
5
src/types/map.ts
Normal file
5
src/types/map.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type PointRoute = {
|
||||||
|
wayPointLatitude: number,
|
||||||
|
wayPointLongitude: number,
|
||||||
|
wayPointAltitude: number
|
||||||
|
}
|
||||||
12
src/utils/crypto.ts
Normal file
12
src/utils/crypto.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import CryptoJS from 'crypto-js'
|
||||||
|
|
||||||
|
const SECRET_KEY = 'zx_web_secret_key_2024'
|
||||||
|
|
||||||
|
export const encrypt = (text: string): string => {
|
||||||
|
return CryptoJS.AES.encrypt(text, SECRET_KEY).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decrypt = (ciphertext: string): string => {
|
||||||
|
const bytes = CryptoJS.AES.decrypt(ciphertext, SECRET_KEY)
|
||||||
|
return bytes.toString(CryptoJS.enc.Utf8)
|
||||||
|
}
|
||||||
@ -42,11 +42,11 @@ export default [
|
|||||||
label: "智能助手",
|
label: "智能助手",
|
||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
id: 7,
|
// id: 7,
|
||||||
type: "IFRAME",
|
// type: "IFRAME",
|
||||||
label: "测试",
|
// label: "测试",
|
||||||
// disabled: true,
|
// disabled: true,
|
||||||
src:"http://127.0.0.1:5500/iframe%E9%80%9A%E8%AE%AF.html"
|
// src:"http://127.0.0.1:5500/iframe%E9%80%9A%E8%AE%AF.html"
|
||||||
},
|
// },
|
||||||
]
|
]
|
||||||
@ -7,17 +7,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<h2 class="title">交通运输综合执法检查系统</h2>
|
<h2 class="title">交通运输综合执法检查系统</h2>
|
||||||
<form class="form_wrap" @submit="submit">
|
<form class="form_wrap" @submit="submit">
|
||||||
<div class="form_item" :class="{ error: errors.userName }">
|
<div class="form_item" :class="{ error: errors.username }">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<img src="@/assets/img/login_user_icon.png" alt="">
|
<img src="@/assets/img/login_user_icon.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
<input class="input" v-model="loginFormData.userName" type="text" placeholder="请输入账号" @input="clearError('userName')">
|
<input class="input" v-model="loginFormData.username" type="text" placeholder="请输入账号"
|
||||||
|
@input="clearError('username')">
|
||||||
</div>
|
</div>
|
||||||
<div class="form_item" :class="{ error: errors.password }">
|
<div class="form_item" :class="{ error: errors.password }">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<img src="@/assets/img/login_password_icon.png" alt="">
|
<img src="@/assets/img/login_password_icon.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
<input class="input" v-model="loginFormData.password" type="password" placeholder="请输入密码" @input="clearError('password')">
|
<input class="input" v-model="loginFormData.password" type="password" placeholder="请输入密码"
|
||||||
|
@input="clearError('password')">
|
||||||
</div>
|
</div>
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<label class="save_password">
|
<label class="save_password">
|
||||||
@ -38,44 +40,53 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue"
|
import { ref } from "vue"
|
||||||
import { login } from "@/api/user.ts"
|
import { login } from "@/api/user"
|
||||||
import { ElMessage } from "element-plus"
|
import { ElMessage } from "element-plus"
|
||||||
import { useRouter } from "vue-router"
|
import { useRouter } from "vue-router"
|
||||||
|
import useUserInfoStore from "@/stores/user"
|
||||||
|
import { encrypt, decrypt } from "@/utils/crypto"
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const savePasswordFlag = ref(!!(localStorage.getItem('user')))
|
const savePasswordFlag = ref(!!(localStorage.getItem('username') && localStorage.getItem('_k')))
|
||||||
const loginFormData = ref(JSON.parse(localStorage.getItem('user') || '{"userName":"","password":""}'))
|
const loginFormData = ref({
|
||||||
|
username: localStorage.getItem('username') || '',
|
||||||
|
password: localStorage.getItem('_k') ? decrypt(localStorage.getItem('_k')!) : ''
|
||||||
|
})
|
||||||
// 添加错误状态
|
// 添加错误状态
|
||||||
const errors = ref({
|
const errors = ref({
|
||||||
userName: false,
|
username: false,
|
||||||
password: false
|
password: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const userInfoStore = useUserInfoStore()
|
||||||
// 添加监听函数来清除错误
|
// 添加监听函数来清除错误
|
||||||
function clearError(field: 'userName' | 'password') {
|
function clearError(field: 'username' | 'password') {
|
||||||
errors.value[field] = false
|
errors.value[field] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function changSavePassword() {
|
function changSavePassword() {
|
||||||
if (savePasswordFlag.value) {
|
if (savePasswordFlag.value) {
|
||||||
localStorage.setItem("user", JSON.stringify(loginFormData.value))
|
localStorage.setItem("username", loginFormData.value.username)
|
||||||
|
localStorage.setItem("_k", encrypt(loginFormData.value.password))
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem("user")
|
localStorage.removeItem("username")
|
||||||
|
localStorage.removeItem("_k")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 提交表单
|
// 提交表单
|
||||||
function submit(event: Event) {
|
function submit(event: Event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
console.log(loginFormData.value)
|
||||||
changSavePassword()
|
changSavePassword()
|
||||||
if (!validate()) return;
|
if (!validate()) return;
|
||||||
router.push("/")
|
toLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**表单校验 */
|
/**表单校验 */
|
||||||
function validate() {
|
function validate() {
|
||||||
errors.value.userName = !loginFormData.value.userName
|
errors.value.username = !loginFormData.value.username
|
||||||
errors.value.password = !loginFormData.value.password
|
errors.value.password = !loginFormData.value.password
|
||||||
|
if (errors.value.username) {
|
||||||
if (errors.value.userName) {
|
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: "请输入用户名"
|
message: "请输入用户名"
|
||||||
@ -95,7 +106,16 @@ function validate() {
|
|||||||
|
|
||||||
|
|
||||||
async function toLogin() {
|
async function toLogin() {
|
||||||
const [err, res] = await login({})
|
const [err, res] = await login({
|
||||||
|
username: loginFormData.value.username,
|
||||||
|
password: loginFormData.value.password
|
||||||
|
})
|
||||||
|
if (err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userInfoStore.token = res.data.access_token
|
||||||
|
userInfoStore.user_info = res.data.user_info
|
||||||
|
router.push("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -25,5 +25,12 @@ export default defineConfig({
|
|||||||
key: fs.readFileSync(path.resolve(__dirname, 'cert/privkey.pem')),
|
key: fs.readFileSync(path.resolve(__dirname, 'cert/privkey.pem')),
|
||||||
cert: fs.readFileSync(path.resolve(__dirname, 'cert/fullchain.pem')),
|
cert: fs.readFileSync(path.resolve(__dirname, 'cert/fullchain.pem')),
|
||||||
},
|
},
|
||||||
|
proxy:{
|
||||||
|
'/api': {
|
||||||
|
changeOrigin: true,
|
||||||
|
target: 'http://192.168.10.176:6001',
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, '/api')
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
Loading…
x
Reference in New Issue
Block a user