| | |
| | | <script setup lang="tsx"> |
| | | import { ref, onMounted } from 'vue' |
| | | import { useScriptTag } from '@vueuse/core' |
| | | import { NButton, NSpace, NTag } from 'naive-ui' |
| | | import { NButton, NSpace, useMessage } from 'naive-ui' |
| | | |
| | | // 添加更好的错误处理和调试 |
| | | const { load } = useScriptTag('https://webapi.amap.com/maps?v=2.0&key=85e62187c6f8e51c797c87b1f36f787a') |
| | | const message = useMessage() |
| | | |
| | | const domRef = ref<HTMLDivElement>() |
| | | const map = ref(null) |
| | | const mapError = ref('') |
| | | const { VITE_MAP_KEY } = import.meta.env |
| | | |
| | | const formDefault: any = { |
| | | name: '', |
| | | remark: '', |
| | | effectiveTime: 1, |
| | | expireTime: 1, |
| | | effectiveTimeTimestamp: null, |
| | | expireTimeTimestamp: null |
| | | } |
| | | const formModel = ref({ ...formDefault }) |
| | | const groupList = ref<any[]>([ |
| | | { |
| | | name: `策略组1`, |
| | | type: 1, |
| | | timeList: [], |
| | | edit: false, |
| | | customTime: [], |
| | | id: 1 |
| | | } |
| | | ]) |
| | | const { load } = useScriptTag( |
| | | `https://webapi.amap.com/maps?v=2.0&key=${VITE_MAP_KEY}` |
| | | ) |
| | | |
| | | const showDrawer = ref(true) |
| | | const domRef = ref<HTMLDivElement | null>(null) |
| | | const map = ref<any>(null) |
| | | |
| | | const rules = { |
| | | name: { |
| | | required: true, |
| | | message: '请输入围栏名称', |
| | | trigger: 'blur', |
| | | }, |
| | | } |
| | | // ==================== 电子围栏相关 ==================== |
| | | const maxPoint = 16 |
| | | const fencePath = ref<any[]>([]) |
| | | const fencePolygon = ref<any>(null) |
| | | let drawing = false |
| | | |
| | | const dateOptions = [ |
| | | { |
| | | label: '工作日', |
| | | value: 1, |
| | | }, |
| | | { |
| | | label: '非工作日', |
| | | value: 2, |
| | | }, |
| | | { |
| | | label: '法定节假日', |
| | | value: 3, |
| | | }, |
| | | { |
| | | label: '自定义', |
| | | value: 4, |
| | | } |
| | | ] |
| | | |
| | | const weekOptions = [ |
| | | { |
| | | label: '周一', |
| | | value: 1, |
| | | }, |
| | | { |
| | | label: '周二', |
| | | value: 2, |
| | | }, |
| | | { |
| | | label: '周三', |
| | | value: 3, |
| | | }, |
| | | { |
| | | label: '周四', |
| | | value: 4, |
| | | }, |
| | | { |
| | | label: '周五', |
| | | value: 5, |
| | | }, |
| | | { |
| | | label: '周六', |
| | | value: 6, |
| | | }, |
| | | { |
| | | label: '周日', |
| | | value: 7, |
| | | }, |
| | | ] |
| | | |
| | | const addStrategyGroup = () => { |
| | | groupList.value.push({ |
| | | name: `策略组${groupList.value.length + 1}`, |
| | | type: 1, |
| | | timeList: [], |
| | | edit: false, |
| | | customTime: [], |
| | | id: groupList.value.length + 1 |
| | | }) |
| | | } |
| | | |
| | | const addTimeRange = (item: any) => { |
| | | console.log(item) |
| | | groupList.value.forEach((group) => { |
| | | if (group.id === item.id) { |
| | | group.timeList.push({ |
| | | startTime: 0, |
| | | endTime: 0, |
| | | id: group.timeList.length + 1 |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const removeTimePeriod = (item: any) => { |
| | | groupList.value.forEach((group) => { |
| | | if (group.id === item.id) { |
| | | group.timeList = group.timeList.filter((sub: any) => sub.id !== item.id) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // ==================== 初始化地图 ==================== |
| | | async function initMap() { |
| | | try { |
| | | if (!domRef.value) { |
| | | return |
| | | } |
| | | await load() |
| | | if (typeof AMap === 'undefined') { |
| | | console.error('AMap未定义') |
| | | return |
| | | } |
| | | map.value = new AMap.Map(domRef.value, { |
| | | zoom: 10, |
| | | center: [113.264499, 23.130061], |
| | | mapStyle: 'amap://styles/yuanshandai' |
| | | }) |
| | | if (!domRef.value) return |
| | | |
| | | console.log('地图初始化成功:', map.value) |
| | | } catch (err) { |
| | | mapError.value = '地图初始化失败: ' + (err as Error).message |
| | | console.error('地图初始化错误:', err) |
| | | await load() |
| | | |
| | | if (typeof (window as any).AMap === 'undefined') { |
| | | message.error('AMap 加载失败') |
| | | return |
| | | } |
| | | |
| | | map.value = new AMap.Map(domRef.value, { |
| | | zoom: 11, |
| | | center: [113.264499, 23.130061], |
| | | mapStyle: 'amap://styles/whitesmoke' |
| | | }) |
| | | |
| | | // 禁用双击缩放(否则会和闭合冲突) |
| | | map.value.setStatus({ doubleClickZoom: false }) |
| | | |
| | | console.log('地图初始化成功') |
| | | } |
| | | |
| | | // ==================== 开始绘制围栏 ==================== |
| | | const startDrawFence = () => { |
| | | if (!map.value) return |
| | | |
| | | clearFence() |
| | | |
| | | fencePath.value = [] |
| | | drawing = true |
| | | |
| | | message.info('单击添加顶点,双击完成绘制(最多16个点)') |
| | | |
| | | map.value.on('click', onMapClick) |
| | | map.value.on('dblclick', onMapDblClick) |
| | | } |
| | | |
| | | // ==================== 单击加点 ==================== |
| | | const onMapClick = (e: any) => { |
| | | if (!drawing) return |
| | | |
| | | if (fencePath.value.length >= maxPoint) { |
| | | message.warning('单个围栏最多支持 16 个顶点') |
| | | return |
| | | } |
| | | |
| | | fencePath.value.push(e.lnglat) |
| | | drawPreview() |
| | | } |
| | | |
| | | // ==================== 双击闭合 ==================== |
| | | const onMapDblClick = (e: any) => { |
| | | e.preventDefault() |
| | | |
| | | if (fencePath.value.length < 3) { |
| | | message.error('至少需要 3 个点才能形成围栏') |
| | | return |
| | | } |
| | | |
| | | finishDrawFence() |
| | | } |
| | | |
| | | // ==================== 实时预览 ==================== |
| | | const drawPreview = () => { |
| | | if (fencePolygon.value) { |
| | | map.value.remove(fencePolygon.value) |
| | | } |
| | | |
| | | fencePolygon.value = new AMap.Polygon({ |
| | | path: fencePath.value, |
| | | strokeColor: '#0068FF', |
| | | strokeWeight: 2, |
| | | fillColor: '#0068FF', |
| | | fillOpacity: 0.3 |
| | | }) |
| | | |
| | | map.value.add(fencePolygon.value) |
| | | } |
| | | |
| | | // ==================== 完成绘制 ==================== |
| | | const finishDrawFence = () => { |
| | | drawing = false |
| | | |
| | | map.value.off('click', onMapClick) |
| | | map.value.off('dblclick', onMapDblClick) |
| | | |
| | | message.success('电子围栏绘制完成') |
| | | |
| | | // 这里就是最终围栏坐标(可直接给后端) |
| | | console.log( |
| | | '围栏坐标:', |
| | | fencePath.value.map((p) => [p.lng, p.lat]) |
| | | ) |
| | | } |
| | | |
| | | // ==================== 清除围栏 ==================== |
| | | const clearFence = () => { |
| | | if (fencePolygon.value) { |
| | | map.value.remove(fencePolygon.value) |
| | | fencePolygon.value = null |
| | | } |
| | | } |
| | | |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="w-full h-full"> |
| | | <!-- 地图区域 --> |
| | | <div class="w-full h-[60%]" id="mapBox"> |
| | | <div id="map" ref="domRef" class="w-full h-full" style="position: relative;" /> |
| | | <div class="w-full h-full" style="position: relative"> |
| | | <!-- 地图容器 --> |
| | | <div ref="domRef" class="w-full h-full" /> |
| | | <!-- 提示 --> |
| | | <div class="tips"> |
| | | 请在地图上【非运营区域】绘制多边形区域,双击闭合 |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <div class="flex-1 info"> |
| | | <style lang="scss" scoped> |
| | | .tips { |
| | | position: absolute; |
| | | top: 24px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | z-index: 99; |
| | | padding: 7px 16px; |
| | | border-radius: 100px; |
| | | background: #171F26; |
| | | font-family: PingFang SC; |
| | | font-size: 13px; |
| | | font-weight: normal; |
| | | line-height: 18px; |
| | | letter-spacing: normal; |
| | | color: #FFFFFF; |
| | | } |
| | | </style> |
| | | <style lang="scss"> |
| | | .robotaxi-content { |
| | | padding: 0 !important; |
| | | } |
| | | </style> |
| | | |
| | | |
| | | <!-- <div class="flex-1 info"> |
| | | <div class="text-[16px] font-bold my-4">新建围栏</div> |
| | | <n-card :segmented="{ |
| | | content: true, |
| | |
| | | </n-tabs> |
| | | </n-form> |
| | | </n-card> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style lang="scss"> |
| | | .robotaxi-content { |
| | | padding: 0 !important; |
| | | } |
| | | </style> |
| | | </div> --> |