|
|
@@ -1,11 +1,14 @@
|
|
|
<template>
|
|
|
- <el-container class="map-sub-container">
|
|
|
- <el-header height="50px" class="operation-header">
|
|
|
- <div class="left-tip">
|
|
|
+ <div class="map-full-container">
|
|
|
+ <div id="container" class="amap-instance" />
|
|
|
+
|
|
|
+ <div class="map-overlay-controls">
|
|
|
+ <div class="control-group tip-group">
|
|
|
<i class="el-icon-info" />
|
|
|
<span>点击地图触发操作</span>
|
|
|
</div>
|
|
|
- <div class="right-actions">
|
|
|
+
|
|
|
+ <div class="control-group button-group">
|
|
|
<el-button
|
|
|
:type="enableAddPath ? 'success' : 'primary'"
|
|
|
size="small"
|
|
|
@@ -17,15 +20,11 @@
|
|
|
<el-button type="info" size="small" icon="el-icon-guide" @click="onPathNavigator">
|
|
|
路径巡航
|
|
|
</el-button>
|
|
|
- <el-button type="danger" size="small" plain icon="el-icon-delete" @click="clearCircle">
|
|
|
- 清除圆形
|
|
|
+ <el-button type="danger" size="small" icon="el-icon-delete" @click="clearCircle">
|
|
|
+ 清除图形
|
|
|
</el-button>
|
|
|
</div>
|
|
|
- </el-header>
|
|
|
-
|
|
|
- <el-main class="map-main-wrapper">
|
|
|
- <div id="container" class="amap-instance" />
|
|
|
- </el-main>
|
|
|
+ </div>
|
|
|
|
|
|
<el-dialog
|
|
|
:visible.sync="showPositionDialog"
|
|
|
@@ -71,31 +70,29 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
- </el-container>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import AMapLoader from '@amap/amap-jsapi-loader'
|
|
|
-import {addGeoPosition, addMyPosition, addPath, getGeoPoint} from '@/api/map'
|
|
|
+import { addGeoPosition, addMyPosition, addPath, getGeoPoint } from '@/api/map'
|
|
|
|
|
|
export default {
|
|
|
name: 'AMap',
|
|
|
data() {
|
|
|
return {
|
|
|
amap: null,
|
|
|
- map: null, // 高德地图实例
|
|
|
- AMap: null, // AMap 构造函数引用
|
|
|
- zoom: 16,
|
|
|
+ map: null,
|
|
|
+ AMap: null,
|
|
|
+ zoom: 12, // 比例尺 10 公里
|
|
|
+ center: [104.068071, 30.576432],
|
|
|
path: [],
|
|
|
mapCircle: null,
|
|
|
circleEditor: null,
|
|
|
showPositionDialog: false,
|
|
|
showInput: false,
|
|
|
- radius: 1000,
|
|
|
- positionForm: {
|
|
|
- lng: null,
|
|
|
- lat: null
|
|
|
- },
|
|
|
+ radius: 1000, // 半径 1 a 公里
|
|
|
+ positionForm: { lng: null, lat: null },
|
|
|
pointArr: [],
|
|
|
mapKeys: {
|
|
|
securityJsCode: '983d6ee43bab3edf3693e91508f94aa9',
|
|
|
@@ -104,7 +101,6 @@ export default {
|
|
|
addPathText: '添加路径',
|
|
|
enableAddPath: false,
|
|
|
pathPointList: [],
|
|
|
- // 新增预览相关的覆盖物数组
|
|
|
previewMarkers: [],
|
|
|
previewPolyline: null
|
|
|
}
|
|
|
@@ -112,11 +108,10 @@ export default {
|
|
|
mounted() {
|
|
|
this.initAMap()
|
|
|
},
|
|
|
- unmounted() {
|
|
|
- this.map?.destroy()
|
|
|
- },
|
|
|
- created() {
|
|
|
- document.title = '地图'
|
|
|
+ beforeDestroy() {
|
|
|
+ if (this.map) {
|
|
|
+ this.map.destroy()
|
|
|
+ }
|
|
|
},
|
|
|
methods: {
|
|
|
initAMap() {
|
|
|
@@ -138,7 +133,9 @@ export default {
|
|
|
this.map = new AMap.Map('container', {
|
|
|
viewMode: '2D',
|
|
|
zoom: this.zoom,
|
|
|
- center: [104.065753, 30.657462]
|
|
|
+ center: this.center,
|
|
|
+ features: ['bg', 'point', 'road'],
|
|
|
+ mapStyle: 'amap://styles/light'
|
|
|
})
|
|
|
|
|
|
this.map.on('click', (e) => {
|
|
|
@@ -146,11 +143,9 @@ export default {
|
|
|
const lat = e.lnglat.getLat()
|
|
|
|
|
|
if (this.enableAddPath) {
|
|
|
- // 1. 记录点数据
|
|
|
const point = { lng: lng, lat: lat }
|
|
|
this.pathPointList.push(point)
|
|
|
|
|
|
- // 2. 添加预览 Marker
|
|
|
const marker = new AMap.Marker({
|
|
|
position: e.lnglat,
|
|
|
map: this.map,
|
|
|
@@ -161,7 +156,6 @@ export default {
|
|
|
})
|
|
|
this.previewMarkers.push(marker)
|
|
|
|
|
|
- // 3. 更新预览折线 Polyline
|
|
|
const pathArr = this.pathPointList.map(item => [item.lng, item.lat])
|
|
|
if (this.previewPolyline) {
|
|
|
this.previewPolyline.setPath(pathArr)
|
|
|
@@ -175,12 +169,12 @@ export default {
|
|
|
map: this.map
|
|
|
})
|
|
|
}
|
|
|
- this.$message.info('已添加第 ' + this.pathPointList.length + ' 个预览点')
|
|
|
} else {
|
|
|
this.pointArr = [lng, lat]
|
|
|
this.positionForm.lng = lng
|
|
|
this.positionForm.lat = lat
|
|
|
this.showPositionDialog = true
|
|
|
+ this.showInput = false // 重置输入状态
|
|
|
}
|
|
|
})
|
|
|
}).catch((e) => {
|
|
|
@@ -188,44 +182,32 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
|
|
|
- // 修改 addPath 逻辑,包含清除预览的逻辑
|
|
|
addPath() {
|
|
|
if (!this.enableAddPath) {
|
|
|
- // 进入添加模式
|
|
|
this.enableAddPath = true
|
|
|
this.addPathText = '完成添加'
|
|
|
- this.clearPreviewOverlays() // 开启前清空旧预览
|
|
|
+ this.clearPreviewOverlays()
|
|
|
this.pathPointList = []
|
|
|
} else {
|
|
|
- // 结束添加模式并提交
|
|
|
if (this.pathPointList.length < 2) {
|
|
|
- this.$message.warning('请至少在地图上点击两个点以形成路径')
|
|
|
+ this.$message.warning('请至少点击两个点')
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
this.enableAddPath = false
|
|
|
this.addPathText = '添加路径'
|
|
|
-
|
|
|
addPath(this.pathPointList).then(resp => {
|
|
|
if (resp.code === 0) {
|
|
|
this.$message.success('路径保存成功')
|
|
|
- // 将当前添加成功的路径加入本地 path 列表
|
|
|
- const pathTrail = {
|
|
|
- path: this.pathPointList.map(p => [p.lng, p.lat])
|
|
|
- }
|
|
|
- this.path.push(pathTrail)
|
|
|
}
|
|
|
}).finally(() => {
|
|
|
- // 清除预览效果
|
|
|
this.clearPreviewOverlays()
|
|
|
this.pathPointList = []
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 清除预览 Marker 和 Polyline 的私有方法
|
|
|
clearPreviewOverlays() {
|
|
|
- if (this.previewMarkers && this.previewMarkers.length > 0) {
|
|
|
+ if (this.previewMarkers.length > 0) {
|
|
|
this.map.remove(this.previewMarkers)
|
|
|
this.previewMarkers = []
|
|
|
}
|
|
|
@@ -236,28 +218,21 @@ export default {
|
|
|
},
|
|
|
|
|
|
drawCircle(pointArr, radius) {
|
|
|
- var center = new this.AMap.LngLat(pointArr[0], pointArr[1])
|
|
|
- var circle = new this.AMap.Circle({
|
|
|
+ const center = new this.AMap.LngLat(pointArr[0], pointArr[1])
|
|
|
+ const circle = new this.AMap.Circle({
|
|
|
center: center,
|
|
|
radius: radius,
|
|
|
- borderWeight: 3,
|
|
|
strokeColor: '#ff3333',
|
|
|
- strokeOpacity: 1,
|
|
|
- strokeWeight: 1,
|
|
|
- fillOpacity: 0.4,
|
|
|
- strokeStyle: 'line',
|
|
|
- strokeDasharray: [10, 10],
|
|
|
fillColor: '#1791fc',
|
|
|
+ fillOpacity: 0.4,
|
|
|
zIndex: 50
|
|
|
})
|
|
|
-
|
|
|
this.map.add(circle)
|
|
|
this.map.setFitView([circle])
|
|
|
this.mapCircle = circle
|
|
|
-
|
|
|
const that = this
|
|
|
this.map.plugin(['AMap.CircleEditor'], function() {
|
|
|
- var circleEditor = new that.AMap.CircleEditor(that.map, circle)
|
|
|
+ const circleEditor = new that.AMap.CircleEditor(that.map, circle)
|
|
|
circleEditor.open()
|
|
|
that.circleEditor = circleEditor
|
|
|
})
|
|
|
@@ -266,29 +241,17 @@ export default {
|
|
|
onPathNavigator() {
|
|
|
getGeoPoint().then(resp => {
|
|
|
if (resp.code === 0) {
|
|
|
- const pathList = resp.data
|
|
|
- if (pathList.length === 0) {
|
|
|
- this.$message.warning('暂无路径数据')
|
|
|
- return
|
|
|
- }
|
|
|
- this.path = pathList
|
|
|
- // eslint-disable-next-line no-undef
|
|
|
+ this.path = resp.data
|
|
|
+ if (this.path.length === 0) return this.$message.warning('暂无路径')
|
|
|
AMapUI.load(['ui/misc/PathSimplifier'], (PathSimplifier) => {
|
|
|
- if (!PathSimplifier.supportCanvas) {
|
|
|
- alert('当前环境不支持 Canvas!')
|
|
|
- return
|
|
|
- }
|
|
|
- var pathSimplifierIns = new PathSimplifier({
|
|
|
+ const pathSimplifierIns = new PathSimplifier({
|
|
|
map: this.map,
|
|
|
zIndex: 100,
|
|
|
data: this.path,
|
|
|
getPath: (pathData) => pathData.path
|
|
|
})
|
|
|
- var pathNavigator = pathSimplifierIns.createPathNavigator(0, {
|
|
|
- loop: false,
|
|
|
- speed: 3600
|
|
|
- })
|
|
|
- pathNavigator.start()
|
|
|
+ const navg = pathSimplifierIns.createPathNavigator(0, { loop: false, speed: 3600 })
|
|
|
+ navg.start()
|
|
|
})
|
|
|
}
|
|
|
})
|
|
|
@@ -297,142 +260,118 @@ export default {
|
|
|
onSavePosition() {
|
|
|
this.showPositionDialog = false
|
|
|
addGeoPosition(this.positionForm).then(resp => {
|
|
|
- if (resp.code === 0) {
|
|
|
- this.$message.success('坐标保存成功')
|
|
|
- }
|
|
|
+ if (resp.code === 0) this.$message.success('保存成功')
|
|
|
})
|
|
|
},
|
|
|
|
|
|
onSaveMyPosition() {
|
|
|
this.showPositionDialog = false
|
|
|
addMyPosition(this.positionForm).then(resp => {
|
|
|
- if (resp.code === 0) {
|
|
|
- this.$message.success('设置成功')
|
|
|
- }
|
|
|
- }).catch(error => {
|
|
|
- this.$notify({ message: error.message, type: 'warning', duration: 1000 })
|
|
|
+ if (resp.code === 0) this.$message.success('设置成功')
|
|
|
})
|
|
|
},
|
|
|
|
|
|
- onDrawCircle() {
|
|
|
- this.showInput = true
|
|
|
- },
|
|
|
+ onDrawCircle() { this.showInput = true },
|
|
|
|
|
|
handleNumChange() {
|
|
|
- if (this.radius < 10) {
|
|
|
- this.$message.error('半径至少应大于 10m')
|
|
|
- return
|
|
|
- }
|
|
|
+ if (this.radius < 10) return this.$message.error('半径过小')
|
|
|
this.showInput = false
|
|
|
this.showPositionDialog = false
|
|
|
this.drawCircle(this.pointArr, this.radius)
|
|
|
},
|
|
|
|
|
|
clearCircle() {
|
|
|
- if (this.mapCircle !== null) {
|
|
|
- this.map.remove(this.mapCircle)
|
|
|
- this.mapCircle = null
|
|
|
- }
|
|
|
- if (this.circleEditor !== null) {
|
|
|
- this.circleEditor.close()
|
|
|
- this.circleEditor = null
|
|
|
- }
|
|
|
- this.$message.info('已清除地图图形')
|
|
|
- }
|
|
|
+ if (this.mapCircle) this.map.remove(this.mapCircle)
|
|
|
+ if (this.circleEditor) this.circleEditor.close()
|
|
|
+ this.mapCircle = null
|
|
|
+ this.circleEditor = null
|
|
|
+ this.$message.info('已清除图形')
|
|
|
+ },
|
|
|
+ goBack() { this.$router.back() }
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-// 保持原有样式不变
|
|
|
-.map-sub-container {
|
|
|
- height: 100%;
|
|
|
- background-color: #fff;
|
|
|
- border-radius: 8px;
|
|
|
+/* 核心:撑满全屏 */
|
|
|
+.map-full-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100vh; /* 撑满视口高度 */
|
|
|
+ position: relative;
|
|
|
overflow: hidden;
|
|
|
- box-shadow: 0 2px 12px 0 rgba(0,0,0,0.05);
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
}
|
|
|
|
|
|
-.operation-header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- background-color: #fcfcfc;
|
|
|
- border-bottom: 1px solid #ebeef5;
|
|
|
- padding: 0 15px !important;
|
|
|
+.amap-instance {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%; /* 继承父级高度 */
|
|
|
+}
|
|
|
|
|
|
- .left-tip {
|
|
|
+/* 悬浮控件样式优化 */
|
|
|
+.map-overlay-controls {
|
|
|
+ position: absolute;
|
|
|
+ top: 20px;
|
|
|
+ right: 20px;
|
|
|
+ z-index: 100;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-end;
|
|
|
+ gap: 12px;
|
|
|
+
|
|
|
+ .tip-group {
|
|
|
+ background: rgba(255, 255, 255, 0.9);
|
|
|
+ padding: 8px 16px;
|
|
|
+ border-radius: 50px;
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
color: #f56c6c;
|
|
|
font-size: 13px;
|
|
|
+ backdrop-filter: blur(8px);
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.5);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- i { margin-right: 5px; }
|
|
|
+ i { margin-right: 6px; }
|
|
|
}
|
|
|
|
|
|
- .right-actions {
|
|
|
+ .button-group {
|
|
|
+ background: rgba(255, 255, 255, 0.85);
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
display: flex;
|
|
|
- gap: 10px;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.6);
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ margin: 0 !important;
|
|
|
+ width: 120px;
|
|
|
+ font-weight: 500;
|
|
|
+ border-radius: 8px;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.map-main-wrapper {
|
|
|
- padding: 0 !important;
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-
|
|
|
-.amap-instance {
|
|
|
- width: 100%;
|
|
|
- height: calc(100vh - 180px);
|
|
|
-}
|
|
|
-
|
|
|
+/* 弹窗及其他样式 */
|
|
|
::v-deep .custom-map-dialog {
|
|
|
- border-radius: 12px;
|
|
|
- .el-dialog__header {
|
|
|
- padding: 20px;
|
|
|
- border-bottom: 1px solid #f2f6fc;
|
|
|
- }
|
|
|
- .el-dialog__body {
|
|
|
- padding: 10px 25px 30px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.dialog-title {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- gap: 8px;
|
|
|
- font-weight: bold;
|
|
|
- color: #303133;
|
|
|
+ border-radius: 16px;
|
|
|
+ overflow: hidden;
|
|
|
+ .el-dialog__header { padding: 20px; background: #fbfcfd; }
|
|
|
}
|
|
|
|
|
|
.coord-display {
|
|
|
background: #f0f7ff;
|
|
|
- padding: 15px;
|
|
|
- border-radius: 8px;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 12px;
|
|
|
text-align: center;
|
|
|
- .label { font-size: 12px; color: #79bbff; margin-bottom: 5px; }
|
|
|
- .value { font-family: 'Courier New', Courier, monospace; font-weight: bold; color: #409EFF; font-size: 16px; }
|
|
|
+ .value { font-family: 'Monaco', monospace; font-size: 18px; color: #409EFF; }
|
|
|
}
|
|
|
|
|
|
.action-grid {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- gap: 10px;
|
|
|
- .el-button {
|
|
|
- margin-left: 0 !important;
|
|
|
- justify-content: flex-start;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.radius-setter {
|
|
|
- .input-tip { font-size: 13px; color: #909399; margin-bottom: 10px; }
|
|
|
-}
|
|
|
-
|
|
|
-.el-button {
|
|
|
- transition: all 0.2s ease;
|
|
|
- &:hover {
|
|
|
- transform: translateY(-1px);
|
|
|
- box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
|
- }
|
|
|
+ gap: 12px;
|
|
|
+ .el-button { margin: 0 !important; text-align: left; padding: 12px 20px; }
|
|
|
}
|
|
|
</style>
|