。
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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@vitejs/plugin-basic-ssl": "^2.0.0",
|
||||
"await-to-js": "^3.0.0",
|
||||
"axios": "^1.8.1",
|
||||
"cesium": "^1.127.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-plus": "^2.9.5",
|
||||
"js-cookie": "^3.0.5",
|
||||
"less": "^4.2.2",
|
||||
"pinia": "^3.0.1",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
@ -28,6 +31,7 @@
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"esbuild": "^0.25.1",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"typescript": "~5.7.3",
|
||||
"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"
|
||||
|
||||
/**登录 */
|
||||
export const login = (data: any) => to(
|
||||
request.post("/login", {
|
||||
|
||||
}
|
||||
))
|
||||
export const login = (data: any) => to<any>(
|
||||
http.post("/api/login", {
|
||||
username: data.username,
|
||||
password: data.password
|
||||
}))
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<div class="header_main">
|
||||
<div class="tabs">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -18,13 +18,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
// import { ref } from "vue"
|
||||
|
||||
const props = defineProps(['tabs', 'id'])
|
||||
const emit = defineEmits(["update:id"])
|
||||
function changeType({ id, disabled }: { id: number, disabled: boolean }) {
|
||||
const props = defineProps(['tabs'])
|
||||
const id = defineModel<number>('id')
|
||||
function changeType({ id:id_, disabled }: { id: number, disabled: boolean }) {
|
||||
if (disabled) return;
|
||||
emit("update:id", id)
|
||||
console.log("id_", id_)
|
||||
id.value = id_
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -40,7 +41,6 @@ function changeType({ id, disabled }: { id: number, disabled: boolean }) {
|
||||
z-index: -1;
|
||||
display: flex;
|
||||
height: 57px;
|
||||
background-color: antiquewhite;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
|
||||
@ -37,7 +37,6 @@
|
||||
width: 249px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
background-color: antiquewhite;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
@ -51,6 +51,7 @@ const options = ref([
|
||||
value: 4
|
||||
},
|
||||
])
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@ -10,9 +10,7 @@
|
||||
</div>
|
||||
<div class="container_center">
|
||||
<div class="map_wrap">
|
||||
<!-- 悬浮窗 -->
|
||||
<img src="@/assets/img/image@1x.png" alt="">
|
||||
<!-- <mainMap></mainMap> -->
|
||||
<mainMap></mainMap>
|
||||
</div>
|
||||
<div class="event_list_wrap">
|
||||
<eventList></eventList>
|
||||
@ -25,11 +23,30 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, provide, onMounted } from "vue"
|
||||
import taskList from "@/components/zfControlPlatform/taskList/index.vue"
|
||||
import positionView from "@/components/zfControlPlatform/positionView/index.vue"
|
||||
import eventList from "@/components/zfControlPlatform/eventList/index.vue"
|
||||
import taskController from "@/components/zfControlPlatform/taskController/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>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@ -38,30 +38,46 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div width="600px" height="500px" class="cesiumContainer" id="cesiumContainer"></div>
|
||||
<div class="cesiumContainer" id="cesiumContainer" ref="cesiumContainerRef"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject, watch, ref as vueRef, nextTick } from "vue"
|
||||
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 type { PointRoute } from "@/types/map"
|
||||
|
||||
let viewer;
|
||||
let handler;
|
||||
let polyline;
|
||||
let positions = [];
|
||||
let isDrawing = ref(false);
|
||||
let viewer: Viewer | null = null;
|
||||
const afterRender = new Set<() => void>()
|
||||
const cesiumContainerRef = vueRef<HTMLElement | null>(null);
|
||||
const pointRoutePreviewParam = inject<Ref<PointRoute[]>>("pointRoutePreviewParam", ref([]))
|
||||
|
||||
async function init() {
|
||||
// 确保Cesium的基础URL和访问令牌设置正确
|
||||
(window as any).CESIUM_BASE_URL = "/assets/Cesium/"
|
||||
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkNWM3YTc2OS1iMjQwLTRiYTUtOGFhNC1lNmFhNDRlN2QyODIiLCJpZCI6MjgxNzc2LCJpYXQiOjE3NDEyNTI2Mjh9.6H_pbpxLPYMBGveKGq9pek2rYlrFvQeWcnHGzVkmb00'
|
||||
Cesium.Ion.defaultAccessToken = token
|
||||
|
||||
viewer = new Cesium.Viewer('cesiumContainer',
|
||||
{
|
||||
// terrain: Terrain.fromWorldTerrain(),
|
||||
// terrainProvider: undefined,
|
||||
await nextTick();
|
||||
|
||||
// 再次检查元素是否存在
|
||||
if (!cesiumContainerRef.value) {
|
||||
console.error('cesiumContainer元素不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查是否已经有Cesium实例
|
||||
if (viewer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用ref直接引用DOM元素
|
||||
viewer = new Cesium.Viewer(cesiumContainerRef.value, {
|
||||
selectionIndicator: false, // 禁用选择指示器
|
||||
infoBox: false, // 禁用信息框
|
||||
navigationHelpButton: false, // 禁用帮助按钮
|
||||
@ -72,13 +88,11 @@ async function init() {
|
||||
animation: false, // 禁用动画控件
|
||||
geocoder: false, // 禁用地理编码器
|
||||
fullscreenButton: false // 禁用全屏按钮
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// 初始化事件处理器
|
||||
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
||||
// 设置相机视图
|
||||
viewer.camera.setView({
|
||||
destination: Cesium.Cartesian3.fromDegrees(113.35, 23.11, 10000),
|
||||
destination: Cesium.Cartesian3.fromDegrees(106.64219088, 38.75395285, 2000),
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(0.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 = SceneMode.SCENE3D; // 切换到3D
|
||||
// viewer.scene.screenSpaceCameraController.minimumZoomDistance = 100;
|
||||
// viewer.scene.screenSpaceCameraController.maximumZoomDistance = 20000;
|
||||
// viewer.zoomTo(300)
|
||||
viewer.scene.screenSpaceCameraController.maximumZoomDistance = 20000;
|
||||
|
||||
console.log('Cesium初始化成功');
|
||||
} 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>
|
||||
|
||||
|
||||
@ -30,7 +30,6 @@ import titleView from "@/components/zfControlPlatform/titleView/index.vue"
|
||||
.content {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
background-color: antiquewhite;
|
||||
img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@ -1,49 +1,52 @@
|
||||
<template>
|
||||
<div class="step_view">
|
||||
<div class="step">
|
||||
<div class="step" v-for="item in props.taskProgress">
|
||||
<div class="content">
|
||||
<div class="left">
|
||||
<div class="time">11:49</div>
|
||||
<div class="time">{{ item.time }}</div>
|
||||
<div class="circle"></div>
|
||||
<div class="label">任务前检测</div>
|
||||
<div class="label">{{ item.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/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 :class="['status_icon', { loading: item.status === 'loading' }]">
|
||||
<img v-if="item.status === 'success'" src="@/assets/img/circle_check.svg" alt="">
|
||||
<img v-else src="@/assets/img/zf_task_loading_icon.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
|
||||
<style lang="less" scoped>
|
||||
@keyframes loading {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.step_view {
|
||||
// background-color: antiquewhite;
|
||||
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>
|
||||
|
||||
<script setup lang="ts">
|
||||
// import taskItem from './taskItem.vue';
|
||||
import taskItem from './taskItem.vue';
|
||||
import ongoingTask from './ongoingTask.vue';
|
||||
import titleView from "@/components/zfControlPlatform/titleView/index.vue"
|
||||
</script>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="task_main">
|
||||
|
||||
<div class="base_info">
|
||||
<div class="info_item">
|
||||
<span class="label">任务名称:</span>
|
||||
@ -21,9 +20,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<stepView></stepView>
|
||||
<stepView :taskProgress="taskProgress"></stepView>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -31,6 +29,29 @@
|
||||
import { ref } from "vue"
|
||||
import stepView from "@/components/zfControlPlatform/stepView/index.vue"
|
||||
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>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@ -22,7 +22,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: antiquewhite;
|
||||
border-bottom: 1px #3c7bff solid;
|
||||
padding: 0 20px;
|
||||
background: linear-gradient(90deg, #0A5CC8 3%, #053B7B 95%);
|
||||
|
||||
@ -1,17 +1,37 @@
|
||||
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({
|
||||
baseURL: import.meta.env.VITE_BASE_URL
|
||||
})
|
||||
|
||||
request.interceptors.request.use((config) => {
|
||||
|
||||
config.headers.Authorization = userInfoStore.token
|
||||
return config
|
||||
})
|
||||
|
||||
request.interceptors.response.use((res) => {
|
||||
|
||||
return res
|
||||
})
|
||||
request.interceptors.response.use(
|
||||
(res: AxiosResponse) => {
|
||||
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
|
||||
@ -2,6 +2,7 @@ import './assets/style/base.less'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
@ -10,7 +11,10 @@ import 'element-plus/dist/index.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
app.use(pinia)
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
app.mount('#app')
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/index/index.vue'
|
||||
import useUserInfoStore from "@/stores/user"
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
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
|
||||
|
||||
|
||||
@ -2,14 +2,22 @@ import { defineStore } from 'pinia'
|
||||
|
||||
const useUserInfoStore = defineStore('userInfo', {
|
||||
state: () => ({
|
||||
token: ""
|
||||
token: "",
|
||||
user_info: {
|
||||
last_login_at: "",
|
||||
last_login_ip: "",
|
||||
login_count: 0,
|
||||
username: ""
|
||||
}
|
||||
|
||||
}),
|
||||
getters: {
|
||||
|
||||
},
|
||||
actions: {
|
||||
|
||||
}
|
||||
},
|
||||
persist: true
|
||||
})
|
||||
|
||||
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: "智能助手",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
type: "IFRAME",
|
||||
label: "测试",
|
||||
// {
|
||||
// id: 7,
|
||||
// type: "IFRAME",
|
||||
// label: "测试",
|
||||
// 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>
|
||||
<h2 class="title">交通运输综合执法检查系统</h2>
|
||||
<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">
|
||||
<img src="@/assets/img/login_user_icon.png" alt="">
|
||||
</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 class="form_item" :class="{ error: errors.password }">
|
||||
<div class="icon">
|
||||
<img src="@/assets/img/login_password_icon.png" alt="">
|
||||
</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 class="options">
|
||||
<label class="save_password">
|
||||
@ -38,44 +40,53 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import { login } from "@/api/user.ts"
|
||||
import { login } from "@/api/user"
|
||||
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 savePasswordFlag = ref(!!(localStorage.getItem('user')))
|
||||
const loginFormData = ref(JSON.parse(localStorage.getItem('user') || '{"userName":"","password":""}'))
|
||||
const savePasswordFlag = ref(!!(localStorage.getItem('username') && localStorage.getItem('_k')))
|
||||
const loginFormData = ref({
|
||||
username: localStorage.getItem('username') || '',
|
||||
password: localStorage.getItem('_k') ? decrypt(localStorage.getItem('_k')!) : ''
|
||||
})
|
||||
// 添加错误状态
|
||||
const errors = ref({
|
||||
userName: false,
|
||||
username: false,
|
||||
password: false
|
||||
})
|
||||
|
||||
const userInfoStore = useUserInfoStore()
|
||||
// 添加监听函数来清除错误
|
||||
function clearError(field: 'userName' | 'password') {
|
||||
function clearError(field: 'username' | 'password') {
|
||||
errors.value[field] = false
|
||||
}
|
||||
|
||||
function changSavePassword() {
|
||||
if (savePasswordFlag.value) {
|
||||
localStorage.setItem("user", JSON.stringify(loginFormData.value))
|
||||
localStorage.setItem("username", loginFormData.value.username)
|
||||
localStorage.setItem("_k", encrypt(loginFormData.value.password))
|
||||
} else {
|
||||
localStorage.removeItem("user")
|
||||
localStorage.removeItem("username")
|
||||
localStorage.removeItem("_k")
|
||||
}
|
||||
}
|
||||
// 提交表单
|
||||
function submit(event: Event) {
|
||||
event.preventDefault()
|
||||
console.log(loginFormData.value)
|
||||
changSavePassword()
|
||||
if (!validate()) return;
|
||||
router.push("/")
|
||||
toLogin()
|
||||
}
|
||||
|
||||
/**表单校验 */
|
||||
function validate() {
|
||||
errors.value.userName = !loginFormData.value.userName
|
||||
errors.value.username = !loginFormData.value.username
|
||||
errors.value.password = !loginFormData.value.password
|
||||
|
||||
if (errors.value.userName) {
|
||||
if (errors.value.username) {
|
||||
ElMessage({
|
||||
type: "error",
|
||||
message: "请输入用户名"
|
||||
@ -95,7 +106,16 @@ function validate() {
|
||||
|
||||
|
||||
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>
|
||||
|
||||
@ -25,5 +25,12 @@ export default defineConfig({
|
||||
key: fs.readFileSync(path.resolve(__dirname, 'cert/privkey.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