Compare commits

..

5 Commits

Author SHA1 Message Date
2b8e8d3926 增加视频页面跳转 2025-12-23 11:36:29 +08:00
8e2e55e645 fix:增加底部图 2025-12-22 21:30:35 +08:00
a147563b70 完成实时飞行轨迹 2025-12-19 16:15:15 +08:00
baf2af1fc3 fix:优化 2025-12-18 21:25:54 +08:00
f33efef3bd 切图 2025-12-18 21:21:51 +08:00
18 changed files with 366 additions and 366 deletions

View File

@@ -11,7 +11,7 @@
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@element-plus/icons-vue": "^2.3.2", "@element-plus/icons-vue": "^2.3.2",
"DroneCtrl": "1.0.1", "DroneCtrl": "1.0.3",
"axios": "^1.13.2", "axios": "^1.13.2",
"element-plus": "^2.12.0", "element-plus": "^2.12.0",
"gcoord": "^1.0.7", "gcoord": "^1.0.7",

20
pnpm-lock.yaml generated
View File

@@ -18,8 +18,8 @@ importers:
specifier: ^2.3.2 specifier: ^2.3.2
version: 2.3.2(vue@3.5.25) version: 2.3.2(vue@3.5.25)
DroneCtrl: DroneCtrl:
specifier: 1.0.2 specifier: 1.0.3
version: 1.0.2 version: 1.0.3
axios: axios:
specifier: ^1.13.2 specifier: ^1.13.2
version: 1.13.2 version: 1.13.2
@@ -137,28 +137,24 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50':
resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==} resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50':
resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==} resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.0.0-beta.50': '@rolldown/binding-linux-x64-musl@1.0.0-beta.50':
resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==} resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.0.0-beta.50': '@rolldown/binding-openharmony-arm64@1.0.0-beta.50':
resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==} resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==}
@@ -211,7 +207,7 @@ packages:
resolution: {integrity: sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==} resolution: {integrity: sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies: peerDependencies:
vite: npm:rolldown-vite@7.2.5 vite: ^5.0.0 || ^6.0.0 || ^7.0.0
vue: ^3.2.25 vue: ^3.2.25
'@vue/compiler-core@3.5.25': '@vue/compiler-core@3.5.25':
@@ -252,8 +248,8 @@ packages:
'@vueuse/shared@9.13.0': '@vueuse/shared@9.13.0':
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
DroneCtrl@1.0.2: DroneCtrl@1.0.3:
resolution: {integrity: sha512-EQWN1q8S4mN64gMlBQkQyJIYSBVE75YLPsYI+L/67TPypOyGWnI/kxCYMPY1hNVB8sIxJO0Z3DU3wlKxwg4Qiw==} resolution: {integrity: sha512-8SC/nx1byB/dhqSb0nNjamzQTQXi15JKkFRkMUnRErBcKBeNypz0Rjn6k7kGMuC/d0RNYODAgj8xc4NQEKokfw==}
async-validator@4.2.5: async-validator@4.2.5:
resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
@@ -411,28 +407,24 @@ packages:
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2: lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.2: lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.2: lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2: lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
@@ -781,7 +773,7 @@ snapshots:
- '@vue/composition-api' - '@vue/composition-api'
- vue - vue
DroneCtrl@1.0.2: {} DroneCtrl@1.0.3: {}
async-validator@4.2.5: {} async-validator@4.2.5: {}

View File

@@ -23,8 +23,8 @@
<!-- 左侧信息面板覆盖层 --> <!-- 左侧信息面板覆盖层 -->
<aside class="left-panel"> <aside class="left-panel">
<!-- 实时画面 --> <!-- 实时画面 -->
<div class="info-panel"> <div class="info-panel" onclick="window.open('/web/video.html', '_blank')">
<div class="panel-header"> <!-- <div class="panel-header">
<span class="panel-title">实时画面</span> <span class="panel-title">实时画面</span>
<span class="panel-count">35</span> <span class="panel-count">35</span>
</div> </div>
@@ -34,12 +34,12 @@
<span class="video-label">{{ i }}</span> <span class="video-label">{{ i }}</span>
</div> </div>
</div> </div>
</div> </div> -->
</div> </div>
<!-- 巡检航线 --> <!-- 巡检航线 -->
<div class="info-panel"> <div class="info-panel">
<div class="panel-header"> <!-- <div class="panel-header">
<span class="panel-title">巡检航线</span> <span class="panel-title">巡检航线</span>
<span class="panel-count">35</span> <span class="panel-count">35</span>
</div> </div>
@@ -59,12 +59,12 @@
<span class="status-number">11</span> <span class="status-number">11</span>
<span class="status-text">待开始</span> <span class="status-text">待开始</span>
</div> </div>
</div> </div> -->
</div> </div>
<!-- 识别场景 --> <!-- 识别场景 -->
<div class="info-panel"> <div class="info-panel">
<div class="panel-header"> <!-- <div class="panel-header">
<span class="panel-title">识别场景</span> <span class="panel-title">识别场景</span>
<span class="panel-count">35</span> <span class="panel-count">35</span>
</div> </div>
@@ -84,7 +84,7 @@
<span class="status-number">11</span> <span class="status-number">11</span>
<span class="status-text">烟雾</span> <span class="status-text">烟雾</span>
</div> </div>
</div> </div> -->
</div> </div>
</aside> </aside>
@@ -92,22 +92,17 @@
<div class="control-panel" v-show="showControlPanel"> <div class="control-panel" v-show="showControlPanel">
<div class="panel-title">电子围栏控制</div> <div class="panel-title">电子围栏控制</div>
<div>
<!-- 地图类型切换 --> <!-- 地图类型切换 -->
<div class="control-group"> <div class="control-group">
<div class="draw-type-title">地图类型</div> <div class="draw-type-title">地图类型</div>
<div class="draw-buttons"> <div class="draw-buttons">
<el-button <el-button @click="switchMapType('normal')" :type="mapType === 'normal' ? 'primary' : 'default'"
@click="switchMapType('normal')" size="default">
:type="mapType === 'normal' ? 'primary' : 'default'"
size="default"
>
普通地图 普通地图
</el-button> </el-button>
<el-button <el-button @click="switchMapType('satellite')" :type="mapType === 'satellite' ? 'primary' : 'default'"
@click="switchMapType('satellite')" size="default">
:type="mapType === 'satellite' ? 'primary' : 'default'"
size="default"
>
卫星地图 卫星地图
</el-button> </el-button>
</div> </div>
@@ -117,28 +112,16 @@
<div class="control-group"> <div class="control-group">
<div class="draw-type-title">选择绘制类型</div> <div class="draw-type-title">选择绘制类型</div>
<div class="draw-buttons"> <div class="draw-buttons">
<el-button <el-button @click="startDrawPolygon" :type="currentDrawType === 'polygon' ? 'primary' : 'default'"
@click="startDrawPolygon" size="default">
:type="currentDrawType === 'polygon' ? 'primary' : 'default'"
size="default"
>
多边形 多边形
</el-button> </el-button>
<el-button <el-button @click="startDrawRectangle" :type="currentDrawType === 'rectangle' ? 'primary' : 'default'"
@click="startDrawRectangle" size="default">
:type="currentDrawType === 'rectangle' ? 'primary' : 'default'"
size="default"
>
矩形 矩形
</el-button> </el-button>
<el-button <el-button @click="startDrawCircle" :type="currentDrawType === 'circle' ? 'primary' : 'default'"
@click="startDrawCircle" size="default">
:type="currentDrawType === 'circle' ? 'primary' : 'default'"
size="default"
>
圆形 圆形
</el-button> </el-button>
</div> </div>
@@ -155,7 +138,8 @@
</div> </div>
<div class="status-item" v-if="droneData"> <div class="status-item" v-if="droneData">
<span class="status-label">位置:</span> <span class="status-label">位置:</span>
<span class="status-value">{{ droneData.latitude?.toFixed(6) }}, {{ droneData.longitude?.toFixed(6) }}</span> <span class="status-value">{{ droneData.latitude?.toFixed(6) }}, {{ droneData.longitude?.toFixed(6)
}}</span>
</div> </div>
<div class="status-item" v-if="droneData"> <div class="status-item" v-if="droneData">
<span class="status-label">高度:</span> <span class="status-label">高度:</span>
@@ -175,21 +159,12 @@
{{ isInFence ? '围栏内' : '围栏外' }} {{ isInFence ? '围栏内' : '围栏外' }}
</el-tag> </el-tag>
</div> </div>
<el-button <el-button @click="handleTakeoff" :type="isFlying ? 'warning' : 'success'" size="small"
@click="handleTakeoff" style="width: 100%; margin-top: 8px;" :disabled="isFlying">
:type="isFlying ? 'warning' : 'success'"
size="small"
style="width: 100%; margin-top: 8px;"
:disabled="isFlying"
>
{{ isFlying ? '飞行中' : '起飞' }} {{ isFlying ? '飞行中' : '起飞' }}
</el-button> </el-button>
<el-button <el-button @click="toggleWebSocket" :type="wsConnected ? 'danger' : 'primary'" size="small"
@click="toggleWebSocket" style="width: 100%; margin-top: 8px;margin-left: 0;">
:type="wsConnected ? 'danger' : 'primary'"
size="small"
style="width: 100%; margin-top: 8px;margin-left: 0;"
>
{{ wsConnected ? '断开连接' : '开始连接' }} {{ wsConnected ? '断开连接' : '开始连接' }}
</el-button> </el-button>
</div> </div>
@@ -197,58 +172,42 @@
<!-- 围栏列表 --> <!-- 围栏列表 -->
<div class="fence-list" v-if="fences.length > 0"> <div class="fence-list" v-if="fences.length > 0">
<div class="list-title">已绘制围栏 ({{ fences.length }})</div> <div class="list-title">已绘制围栏 ({{ fences.length }})</div>
<div <div v-for="fence in fences" :key="fence.id" class="fence-item">
v-for="fence in fences"
:key="fence.id"
class="fence-item"
>
<span class="fence-info"> <span class="fence-info">
<span class="fence-name">{{ fence.name || '未命名围栏' }}</span> <span class="fence-name">{{ fence.name || '未命名围栏' }}</span>
</span> </span>
<div class="fence-actions"> <div class="fence-actions">
<el-button <el-button @click="uploadFence(fence.id)" type="primary" size="small">
@click="uploadFence(fence.id)"
type="primary"
size="small"
>
上传 上传
</el-button> </el-button>
<el-button <el-button @click="removeFence(fence.id)" type="danger" size="small">
@click="removeFence(fence.id)"
type="danger"
size="small"
>
删除 删除
</el-button> </el-button>
</div> </div>
</div> </div>
<el-button <el-button @click="clearAllFences" type="danger" size="default" style="width: 100%; margin-top: 8px;">
@click="clearAllFences"
type="danger"
size="default"
style="width: 100%; margin-top: 8px;"
>
清除所有围栏 清除所有围栏
</el-button> </el-button>
</div> </div>
</div> </div>
</div>
<!-- 右侧信息面板覆盖层 --> <!-- 右侧信息面板覆盖层 -->
<aside class="right-panel"> <aside class="right-panel">
<!-- 区域管理 --> <!-- 区域管理 -->
<div class="info-panel"> <div class="info-panel">
<div class="panel-header"> <!-- <div class="panel-header">
<span class="panel-title">区域管理</span> <span class="panel-title">区域管理</span>
<span class="panel-count">12</span> <span class="panel-count">12</span>
</div> </div>
<div class="area-preview"> <div class="area-preview">
<img src="./assets/image/regional_management.png" alt="区域管理" class="area-image" /> <img src="./assets/image/regional_management.png" alt="区域管理" class="area-image" />
</div> </div> -->
</div> </div>
<!-- 出勤设备 --> <!-- 出勤设备 -->
<div class="info-panel"> <div class="info-panel">
<div class="panel-header"> <!-- <div class="panel-header">
<span class="panel-title">出勤设备</span> <span class="panel-title">出勤设备</span>
<span class="panel-count">35</span> <span class="panel-count">35</span>
</div> </div>
@@ -268,12 +227,12 @@
<span class="status-number">12</span> <span class="status-number">12</span>
<span class="status-text">充电</span> <span class="status-text">充电</span>
</div> </div>
</div> </div> -->
</div> </div>
<!-- 执行任务 --> <!-- 执行任务 -->
<div class="info-panel"> <div class="info-panel">
<div class="panel-header"> <!-- <div class="panel-header">
<span class="panel-title">执行任务</span> <span class="panel-title">执行任务</span>
<span class="panel-count">12</span> <span class="panel-count">12</span>
</div> </div>
@@ -296,7 +255,7 @@
</div> </div>
<span class="progress-text">60% 待开始</span> <span class="progress-text">60% 待开始</span>
</div> </div>
</div> </div> -->
</div> </div>
</aside> </aside>
</div> </div>
@@ -304,62 +263,17 @@
<!-- 底部导航栏 --> <!-- 底部导航栏 -->
<footer class="bottom-footer"> <footer class="bottom-footer">
<div class="footer-left">
<span class="storage-info">62.3GB</span>
</div>
<div class="footer-center"> <div class="footer-center">
<div class="resolution-info">
<!-- <span class="resolution-text">1109px x 1902px</span> -->
</div>
<div class="footer-icons"> <div class="footer-icons">
<img <img src="./assets/image/d1.png" class="arrow-img" />
:src="icon1" <img src="./assets/image/i1.png" class="icon-img" />
class="footer-icon-img" <img src="./assets/image/i2.png" class="icon-img" />
@click="handleFooterIconClick(1)" <img src="./assets/image/i3.png" class="icon-img" @click="handleFooterIconClick(4)"/>
alt="图标1" <img src="./assets/image/i4.png" class="icon-img" />
/> <img src="./assets/image/i5.png" class="icon-img" />
<img <img src="./assets/image/d2.png" class="arrow-img" />
:src="icon2"
class="footer-icon-img"
@click="handleFooterIconClick(2)"
alt="图标2"
/>
<img
:src="icon3"
class="footer-icon-img"
@click="handleFooterIconClick(3)"
alt="图标3"
/>
<img
:src="icon4"
class="footer-icon-img"
:class="{ 'active': showControlPanel }"
@click="handleFooterIconClick(4)"
alt="图标4"
/>
<img
:src="icon5"
class="footer-icon-img"
@click="handleFooterIconClick(5)"
alt="图标5"
/>
<img
:src="icon6"
class="footer-icon-img"
@click="handleFooterIconClick(6)"
alt="图标6"
/>
<img
:src="icon7"
class="footer-icon-img"
@click="handleFooterIconClick(7)"
alt="图标7"
/>
</div> </div>
</div> </div>
<div class="footer-right">
<span class="uptime-info">120.3 h</span>
</div>
</footer> </footer>
</div> </div>
</template> </template>
@@ -368,15 +282,6 @@
import { ref, onMounted, onUnmounted, computed } from 'vue' import { ref, onMounted, onUnmounted, computed } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader' import AMapLoader from '@amap/amap-jsapi-loader'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import {
Upload,
Connection,
Folder,
Download,
ChatLineRound,
Link,
VideoPlay
} from '@element-plus/icons-vue'
import { WebSocketClient, MockWebSocketDataGenerator } from './utils/websocket.js' import { WebSocketClient, MockWebSocketDataGenerator } from './utils/websocket.js'
import droneImage from './assets/image/wrj.png' import droneImage from './assets/image/wrj.png'
import icon1 from './assets/image/1.png' import icon1 from './assets/image/1.png'
@@ -414,6 +319,8 @@ let mouseTool = null
const mapType = ref('satellite') // 'normal' | 'satellite' const mapType = ref('satellite') // 'normal' | 'satellite'
const dronePath = ref([]) // 无人机轨迹点集合 const dronePath = ref([]) // 无人机轨迹点集合
let droneTrackLine = null let droneTrackLine = null
const drone2Path = ref([]) // 无人机2轨迹点集合
let drone2TrackLine = null
const MAX_TRACK_POINTS = 2000 const MAX_TRACK_POINTS = 2000
// 围栏相关状态 // 围栏相关状态
@@ -638,7 +545,7 @@ const toggleWebSocket = () => {
gcoord.GCJ02, // 目标坐标系 gcoord.GCJ02, // 目标坐标系
); );
console.log(point, point2); console.log(point, point2);
// updateDroneMarker(point[0], point[1], drone1.heading); updateDroneMarker(point[0], point[1], point2[0], point2[1], drone1.heading);
// updateDrone2Marker(point2[0], point2[1], drone2.heading); // updateDrone2Marker(point2[0], point2[1], drone2.heading);
}; };
} }
@@ -667,7 +574,7 @@ const handleDroneData = (data) => {
} }
// 更新无人机marker // 更新无人机marker
const updateDroneMarker = (lng, lat, heading = 0) => { const updateDroneMarker = (lng, lat, lng2, lat2, heading = 0) => {
if (!map || !AMap) return if (!map || !AMap) return
if (!droneMarker.value) { if (!droneMarker.value) {
@@ -684,25 +591,40 @@ const updateDroneMarker = (lng, lat, heading = 0) => {
position: [lng, lat], position: [lng, lat],
icon: icon, icon: icon,
zIndex: 100, zIndex: 100,
title: '无人机', title: '侦查机',
offset: new AMap.Pixel(-20, -20), offset: new AMap.Pixel(-20, -20),
// 优化:禁用动画,减少性能消耗 // 优化:禁用动画,减少性能消耗
animation: 'AMAP_ANIMATION_NONE' animation: 'AMAP_ANIMATION_NONE'
}) })
map.add(droneMarker.value) map.add(droneMarker.value)
drone2Marker.value = new AMap.Marker({
position: [lng, lat],
icon: icon,
zIndex: 100,
title: '投弹机',
offset: new AMap.Pixel(-20, -20),
// 优化:禁用动画,减少性能消耗
animation: 'AMAP_ANIMATION_NONE'
})
map.add(drone2Marker.value)
} else { } else {
// 更新位置 // 更新位置
droneMarker.value.setPosition([lng, lat]) droneMarker.value.setPosition([lng, lat])
drone2Marker.value.setPosition([lng2, lat2])
// 更新旋转角度如果有heading数据 // 更新旋转角度如果有heading数据
if (heading !== undefined) { if (heading !== undefined) {
droneMarker.value.setAngle(heading) droneMarker.value.setAngle(heading)
drone2Marker.value.setAngle(heading)
} }
} }
// 更新飞行轨迹 // 更新飞行轨迹
updateDroneTrack(lng, lat) updateDroneTrack(lng, lat)
updateDrone2Track(lng2, lat2)
// 将地图中心移动到无人机位置(可选,可以注释掉) // 将地图中心移动到无人机位置(可选,可以注释掉)
map.setCenter([lng, lat]) map.setCenter([lng, lat])
@@ -733,6 +655,30 @@ const initDroneTrack = () => {
}) })
} }
const initDrone2Track = () => {
if (!map || !AMap) return
// 如果已有轨迹线,先移除
if (drone2TrackLine) {
map.remove(drone2TrackLine)
}
drone2TrackLine = new AMap.Polyline({
map,
path: [],
showDir: true,
isOutline: true,
outlineColor: 'rgba(0,0,0,0.3)',
borderWeight: 2,
strokeColor: '#00e0ff',
strokeOpacity: 0.8,
strokeWeight: 4,
lineJoin: 'round',
lineCap: 'round',
zIndex: 90
})
}
// 更新无人机轨迹 // 更新无人机轨迹
const updateDroneTrack = (lng, lat) => { const updateDroneTrack = (lng, lat) => {
if (!map || !AMap) return if (!map || !AMap) return
@@ -751,6 +697,24 @@ const updateDroneTrack = (lng, lat) => {
} }
} }
const updateDrone2Track = (lng, lat) => {
if (!map || !AMap) return
if (!drone2TrackLine) {
initDrone2Track()
}
drone2Path.value.push([lng, lat])
if (drone2Path.value.length > MAX_TRACK_POINTS) {
drone2Path.value.shift()
}
if (drone2TrackLine) {
drone2TrackLine.setPath(drone2Path.value)
}
}
// 清空无人机轨迹 // 清空无人机轨迹
const clearDroneTrack = () => { const clearDroneTrack = () => {
dronePath.value = [] dronePath.value = []
@@ -759,6 +723,13 @@ const clearDroneTrack = () => {
} }
} }
const clearDrone2Track = () => {
drone2Path.value = []
if (drone2TrackLine) {
drone2TrackLine.setPath([])
}
}
// 检测点是否在多边形内(射线法) // 检测点是否在多边形内(射线法)
const isPointInPolygon = (point, polygon) => { const isPointInPolygon = (point, polygon) => {
const [x, y] = point const [x, y] = point
@@ -1293,7 +1264,7 @@ const uploadFence = (fenceId) => {
} }
if (pointsData) { if (pointsData) {
const points = pointsData.points.map(point=> { const points = pointsData.points.map(point => {
const wgsPoint = gcoord.transform( const wgsPoint = gcoord.transform(
point, // 原始坐标 point, // 原始坐标
gcoord.GCJ02, // 当前坐标系 gcoord.GCJ02, // 当前坐标系
@@ -1303,7 +1274,7 @@ const uploadFence = (fenceId) => {
}); });
console.log(points) console.log(points)
ctrl.UploadArea(new UploadAreaReq(points.map(point=>({ ctrl.UploadArea(new UploadAreaReq(points.map(point => ({
longitude: point[0], longitude: point[0],
latitude: point[1], latitude: point[1],
})))); }))));
@@ -1478,6 +1449,11 @@ onUnmounted(() => {
wsClient = null wsClient = null
} }
if (conn) {
conn.close()
conn = null
}
// 清理地图 // 清理地图
if (droneMarker.value) { if (droneMarker.value) {
map?.remove(droneMarker.value) map?.remove(droneMarker.value)
@@ -1583,17 +1559,35 @@ onUnmounted(() => {
/* 左侧面板(覆盖层) */ /* 左侧面板(覆盖层) */
.left-panel { .left-panel {
/* background: url('./assets/image/z1.png') no-repeat center center; */
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 320px;
padding: 20px; padding: 20px;
overflow-y: auto; overflow-y: auto;
z-index: 100; z-index: 100;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
:nth-child(1) {
background: url('./assets/image/z1.png') no-repeat center center;
width: 320px;
height: 270px;
}
:nth-child(2) {
background: url('./assets/image/z2.png') no-repeat center center;
width: 320px;
height: 270px;
}
:nth-child(3) {
background: url('./assets/image/z3.png') no-repeat center center;
width: 320px;
height: 270px;
}
} }
/* 右侧面板(覆盖层) */ /* 右侧面板(覆盖层) */
@@ -1602,13 +1596,30 @@ onUnmounted(() => {
right: 0; right: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
width: 320px;
padding: 20px; padding: 20px;
overflow-y: auto; overflow-y: auto;
z-index: 100; z-index: 100;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
:nth-child(1) {
background: url('./assets/image/y1.png') no-repeat center center;
width: 320px;
height: 270px;
}
:nth-child(2) {
background: url('./assets/image/y2.png') no-repeat center center;
width: 320px;
height: 270px;
}
:nth-child(3) {
background: url('./assets/image/y3.png') no-repeat center center;
width: 320px;
height: 270px;
}
} }
/* 信息面板 */ /* 信息面板 */
@@ -1983,14 +1994,12 @@ onUnmounted(() => {
/* 底部导航栏 */ /* 底部导航栏 */
.bottom-footer { .bottom-footer {
height: 55px; background: #0a0e27;
background:#0a0e27;
backdrop-filter: blur(15px); backdrop-filter: blur(15px);
border-top: 1px solid rgba(255, 255, 255, 0.15); border-top: 1px solid rgba(255, 255, 255, 0.15);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0 40px;
z-index: 1000; z-index: 1000;
position: relative; position: relative;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
@@ -2124,4 +2133,3 @@ onUnmounted(() => {
background: rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.5);
} }
</style> </style>

BIN
src/assets/image/d1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/assets/image/d2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
src/assets/image/i1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/assets/image/i2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

BIN
src/assets/image/i3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/assets/image/i4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

BIN
src/assets/image/i5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
src/assets/image/wrj2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/assets/image/y1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/assets/image/y2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
src/assets/image/y3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
src/assets/image/z1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
src/assets/image/z2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
src/assets/image/z3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -97,9 +97,9 @@ function upload(url, params) {
}); });
} }
// export let host = `${window.location.protocol}//${window.location.host}`; export let host = `${window.location.protocol}//${window.location.host}`;
// export let host = "http://192.168.43.98:5678"; // export let host = "http://192.168.43.98:5678";
export let host = "http://192.168.3.81:5678"; // export let host = "http://192.168.3.81:5678";
// export let host = "http://127.0.0.1:5678"; // export let host = "http://127.0.0.1:5678";
export const getConfig = () => { export const getConfig = () => {