@@ -5,42 +5,45 @@
< div class = "title" > 基本信息 < / div >
< div class = "addBuildingFormBody" >
< el-form class = "formBody" label -position = " right " ref = "formRef" :model = "form" :rules = "rules" label -width = " 130px " >
< el-form-item label = "巡检点 名称" prop = "point Name" >
< el-input v -model .trim = " form.point Name " placeholder = "请输入巡检点 名称" / >
< el-form-item label = "巡检路线 名称" prop = "route Name" >
< el-input v -model .trim = " form.route Name " placeholder = "请输入巡检路线 名称" / >
< / el-form-item >
< el-form-item label = "巡检项目 " prop = "itemId " >
< el-form-item label = "巡检路线描述 " prop = "description " >
< el-input type = "textarea" :rows = "3" v -model .trim = " form.description " placeholder = "请输入巡检路线描述" / >
< / el-form-item >
< el-form-item label = "预计巡检时长" prop = "estimatedTime" >
< el-input v -model .trim.number = " form.estimatedTime " placeholder = "预计巡检时长" >
< template # append > 分钟 < / template >
< / el-input >
< / el-form-item >
< el-form-item label = "巡检路线状态" prop = "routeStatus" >
< el-radio-group v-model = "form.routeStatus" >
< el -radio v-for = "(item, index) in com_inspection_route_status" :key="index" :value="item.value" :label="item.label" > < / el -radio >
< / el-radio-group >
< / el-form-item >
< el-form-item label = "巡检点" >
< div class = "itembox" >
< div
class = "item"
: class = "{
active: checkoutPointItem list.includes(item.id)
active: checkoutPointlist.includes(item.id)
}"
@click ="checkoutItem(item.id)"
v-for = "(item, index) in pointItem list"
v-for = "(item, index) in points list"
:key = "index"
>
< el-icon > < Check / > < / el-icon >
< div > { { item . item Name } } < / div >
< el-icon v-show = "checkoutPointlist.includes(item.id)" > < Check / > < / el-icon >
< div > { { item . point Name } } < / div >
< / div >
< / div >
< / el-form-item >
< el-form-item label = "打卡方式" prop = "checkMode " >
< el-select v-model = "form.checkMode" placeholder="请选择打卡方式" clearable >
< el-option v-for = "dict in com_inspection_point_checkmode" :key="dict.value" :label="dict.label" :value="dict.value" / >
< / el-select >
< / el-form-item >
< el-form-item label = "巡检点状态" prop = "status" >
< el-radio-group v-model = "form.status" >
< el -radio v-for = "item in com_inspection_point_status" :value="item.value" :key="item.value" :label="item.value" > {{
item.label
}} < / el -radio >
< / el-radio-group >
< / el-form-item >
< el-form-item label = "巡检点位置" prop = "location" >
< el-form-item label = "绘制巡检路线 " >
< div class = "map" id = "containerMap" > < / div >
< div class = "tips" >
< span > 点击地图选择位置 < / span >
< span class = "deleteMarker" v-show = "Boolean(marker)" @click="clearLocation" > 清空选择地点 < / span >
< div class = "tips flex flex-items-center justify-between " >
< div > 点击地图, 绘制巡检路线 ; 再次点击可继续绘制下一条 ( 但只能保留一条 ) < / div >
< div >
< span class = "deleteMarker" @click ="clearAll" > 清空所有路线 < / span >
< / div >
< / div >
< / el -form -item >
< el-form-item style = "margin-top: 30px" >
@@ -54,20 +57,17 @@
< / template >
< script setup lang = "ts" >
import { ref , toRefs } from 'vue' ;
import { ref , toRefs , nextTick } from 'vue' ;
import { getCurrentInstance , ComponentInternalInstance } from 'vue' ;
import { useRouter , useRoute } from 'vue-router' ;
import { ElMessage } from 'element-plus' ;
import PageHeader from '@/components/Pageheader/index.vue' ;
import { addPoint , getPoint , updatePoint } from '@/api/system/InspectionPoint' ;
import { listItem } from '@/api/system/InspectionItem' ;
import { addPoint , getPoint , listPoint , updatePoint } from '@/api/system/InspectionPoint' ;
import { onMounted , onUnmounted } from 'vue' ;
import AMapLoader from '@amap/amap-jsapi-loader' ;
import { getVillageByIdAPI } from '@/api/system/community ' ;
import { addRoute , getRoute , updateRoute } from '@/api/system/InspectionRoute ' ;
const { proxy } = getCurrentInstance ( ) as ComponentInternalInstance ;
const { com _inspection _point _checkmode } = toRefs < any > ( proxy ? . useDict ( 'com_inspection_point_checkmode ' ) ) ;
const { com _inspection _point _status } = toRefs < any > ( proxy ? . useDict ( 'com_inspection_point_status' ) ) ;
const { com _inspection _route _status } = toRefs < any > ( proxy ? . useDict ( 'com_inspection_route_status ' ) ) ;
const router = useRouter ( ) ;
const route = useRoute ( ) ;
@@ -75,73 +75,282 @@ const route = useRoute();
const formRef = ref ( ) ;
const form = ref ( {
id : null ,
pointName : '' ,
itemId : '' ,
location : '' ,
status : '0 ' ,
checkMode : '9999'
startLat : '' ,
startLng : '' ,
endLat : '' ,
endLng : '' ,
routeName : '' , // 巡检路线名称
description : '' , // 巡检路线描述
routeStatus : '0' , // 巡检路线状态
estimatedTime : null , // 预计巡检时长
pathPoints : '' , // 巡检路线经纬度
totalPoints : '' // 巡检点
} ) ;
const rules = ref ( {
point Name: [ { required : true , message : '请输入巡检点 名称' , trigger : 'blur' } ] ,
itemId : [ { required : true , message : '请选择巡检项目 ' , trigger : 'change ' } ] ,
checkMode : [ { required : true , message : '请选择打卡方式' , trigger : 'change' } ] ,
location : [ { required : true , message : '请选择巡检点位置' , trigger : 'change' } ]
route Name: [ { required : true , message : '请输入巡检路线 名称' , trigger : 'blur' } ] ,
estimatedTime : [ { required : true , message : '请输入预计巡检时长 ' , trigger : 'blur ' } ]
} ) ;
// !== 初始化 =============================================================================
const routeid = ref ( null ) ;
// 巡检点 列表
const pointslist = ref ( [ ] ) ;
const pointId = ref ( null ) ;
const pointItem list = ref ( [ ] ) ;
const checkoutPointItemlist = ref ( [ ] ) ;
// 选择巡检点列表
const checkoutPoint list = ref ( [ ] ) ;
function checkoutItem ( id : string ) {
form . value . itemId = '' ; // 每次点击都先清空 itemId
if ( checkoutPointItem list . value . includes ( id ) ) {
checkoutPointItemlist . value = checkoutPointItemlist . value . filter ( ( item ) => item !== id ) ;
if ( checkoutPointlist . value . includes ( id ) ) {
checkoutPointlist . value = checkoutPointlist . value . filter ( ( item ) => item !== id ) ;
removePointMarker ( id ) ;
} else {
checkoutPointItem list . value . push ( id ) ;
}
form . value . itemI d = checkoutPointItemlist . value . join ( ',' ) ; // 将选中的 id 数组转换为逗号分隔的字符串
}
function init ( ) {
// 获取巡检项目列表
listItem ( ) . then ( ( res ) => {
pointItemlist . value = res . rows ;
} ) ;
if ( route . query . id ) {
pointId . value = route . query . id ;
// 获取巡检点详情
getPoint ( pointId . value ) . then ( ( res ) => {
form . value = {
... form . value ,
... res . data
} ;
// 如果有 itemId, 拆分到 checkoutPointItemlist
if ( res . data . itemId ) {
checkoutPointItemlist . value = res . data . itemId . split ( ',' ) ;
checkoutPointlist . value . push ( id ) ;
const find = pointslist . value . find ( ( item ) => item . id === id ) ;
if ( fin d && find . location ) {
console . log ( find . location , find . pointName ) ;
if ( find && f ind . location ) {
addPointMarker ( id , find . location , find . pointName ) ;
}
}
}
// 调整地图视野以适应所有 marker
map . value . setFitView ( ) ;
}
// 添加 marker
function addPointMarker ( id : string , location : string , title ? : string ) {
if ( ! map . value || ! AMapLib . value ) return ;
const [ lng , lat ] = location . split ( ',' ) . map ( ( v ) => Number ( v . trim ( ) ) ) ;
if ( ! Number . isFinite ( lng ) || ! Number . isFinite ( lat ) ) return ;
// 获取到经纬度后,添加 marker 数组
const lnglat = new AMapLib . value . LngLat ( lng , lat ) ;
const index = Object . keys ( markers . value ) . length + 1 ; // 获取当前 marker 数量,作为新的 marker 的编号
markers . value [ id ] = new AMapLib . value . Marker ( {
position : lnglat ,
map : map . value ,
title : title || '巡检点' ,
label : title || '巡检点' ,
content : `
<div style="display:flex;align-items:center;justify-content:center;width:30px;height:30px;line-height:18px;border-radius:50%;background-color:rgba(26,117,255,0.3);">
<div
style=" width: 20px;
border-radius: 50%;
height: 20px;
line-height: 20px;
background-color: rgba(26,117,255,1);
color: #ffffff;
font-size: 20px;
text-align: center;"
> ${ index } </div>
</div> `
} ) ;
}
// 移除 marker
function removePointMarker ( id : string ) {
const markerItem = markers . value [ id ] ;
if ( markerItem ) {
map . value . remove ( markerItem ) ;
delete markers . value [ id ] ;
}
}
// ! 初始化 =========================================================================
function init ( ) {
if ( route . query . id ) {
routeid . value = route . query . id ;
// 获取巡检点详情
getRoute ( routeid . value ) . then ( ( res ) => {
form . value = {
id : res . data . id ,
startLat : res . data . startLat ,
startLng : res . data . startLng ,
endLat : res . data . endLat ,
endLng : res . data . endLng ,
routeName : res . data . routeName , // 巡检路线名称
description : res . data . description , // 巡检路线描述
routeStatus : res . data . routeStatus , // 巡检路线状态
estimatedTime : res . data . estimatedTime , // 预计巡检时长
pathPoints : res . data . pathPoints , // 巡检路线经纬度
totalPoints : res . data . totalPoints // 巡检点
} ;
console . log ( '巡检路线详情:' , form . value ) ;
nextTick ( ( ) => {
initLocation ( ) ;
if ( routeid . value ) {
checkoutPointlist . value = res . data . totalPoints ? res . data . totalPoints . split ( ',' ) : [ ] ;
if ( checkoutPointlist . value . length > 0 ) {
// 回显 marker
checkoutPointlist . value . forEach ( ( id ) => {
const find = res . data . points . find ( ( item ) => item . id === id ) ;
if ( find && find . location ) {
addPointMarker ( id , find . location , find . pointName ) ;
}
} ) ;
}
// 回显路线
if ( res . data . pathPoints ) {
console . log ( '回显巡检点:' , res . data . pathPoints ) ;
renderSavedPath ( res . data . pathPoints ) ;
}
}
} ) ;
} ) ;
}
}
init ( ) ;
// ! map
const mapReady = ref ( false ) ;
const AMapLib = ref ( null ) ;
const map = ref < any > ( null ) ;
const markers = ref < Record < string , any > > ( { } ) ;
const mouseTool = ref ( null ) ;
const drawnPolylines = ref ( [ ] ) ; // 存所有线条
onMounted ( async ( ) => {
// 获取巡检点列表
if ( ! routeid . value ) {
listPoint ( ) . then ( ( res ) => {
pointslist . value = res . rows ;
} ) ;
}
await nextTick ( ) ;
window . _AMapSecurityConfig = { securityJsCode : '09534b3534d8da53968529a9ce164118' } ;
AMapLoader . load ( {
key : '8e302fe1be5f4db62bc734db23dca149' ,
version : '2.0' ,
plugins : [ 'AMap.Scale' , 'AMap.Marker' ]
} ) . then ( async ( AMap ) => {
AMapLib . value = AMap ;
map . value = new AMap . Map ( 'containerMap' , {
viewMode : '3D' ,
zoom : 11 ,
center : [ 116.397428 , 39.90923 ]
} ) ;
mapReady . value = true ;
// 初始化绘图插件 MouseTool
AMap . plugin ( [ 'AMap.MouseTool' ] , ( ) => {
mouseTool . value = new AMap . MouseTool ( map . value ) ;
// ✅ 只在这里调用一次,不要重复调用
// 监听绘制(全局只绑定一次!)
mouseTool . value . on ( 'draw' , ( e ) => {
if ( ! e . obj ) return ;
// ==========================================
// 核心:永远只保留一条线,画新的自动删旧的
// ==========================================
if ( drawnPolylines . value . length > 0 ) {
map . value . remove ( drawnPolylines . value [ 0 ] ) ;
}
drawnPolylines . value = [ e . obj ] ;
// 画完继续允许画下一条(但永远只保留一条)
initDrawTool ( ) ;
} ) ;
initDrawTool ( ) ;
} ) ;
init ( ) ;
} ) ;
} ) ;
// 回显
function renderSavedPath ( pathStr : string ) {
if ( ! pathStr || ! map . value || ! AMapLib . value ) return ;
try {
const path = JSON . parse ( pathStr ) ;
if ( ! Array . isArray ( path ) || path . length === 0 ) return ;
// 先清空旧路线
drawnPolylines . value . forEach ( ( line ) => {
map . value . remove ( line ) ;
} ) ;
drawnPolylines . value = [ ] ; // 🔴 清空数组
// 只回显一条
const polyline = new AMapLib . value . Polyline ( {
path : path ,
strokeColor : '#3366FF' ,
strokeOpacity : 1 ,
strokeWeight : 3 ,
strokeStyle : 'solid' ,
map : map . value
} ) ;
drawnPolylines . value . push ( polyline ) ; // ✅ 只一条
map . value . setFitView ( polyline ) ;
} catch ( e ) {
console . error ( '路线解析失败' , e ) ;
}
}
// 初始化绘图(只执行一次,防止事件重复绑定)
function initDrawTool ( ) {
if ( ! mouseTool . value ) return ;
// 先关闭可能存在的绘图
mouseTool . value . close ( false ) ;
// 开启折线绘制
mouseTool . value . polyline ( {
strokeColor : '#3366FF' ,
strokeOpacity : 1 ,
strokeWeight : 3 ,
strokeStyle : 'solid'
} ) ;
}
// 清空所有画线
function clearAll ( ) {
if ( mouseTool . value ) {
mouseTool . value . close ( true ) ; // 关闭绘图并清空
drawnPolylines . value . forEach ( ( line ) => {
map . value . removeOverlay ( line ) ;
} ) ;
drawnPolylines . value = [ ] ;
initDrawTool ( ) ;
}
}
// !!! 核心:获取绘制的路线经纬度数组
function getPolylinePath ( ) {
if ( drawnPolylines . value . length === 0 ) return [ ] ;
const lastLine = drawnPolylines . value . at ( - 1 ) ;
console . log ( '最后一条线的路径:' , lastLine . getPath ( ) ) ;
const path = lastLine . getPath ( ) . map ( ( p ) => [ p . lng , p . lat ] ) ;
return path ;
}
// !== 提交 =============================================================================
const submitForm = ( ) => {
formRef . value ? . validate ( async ( valid : boolean ) => {
if ( valid ) {
// 设置 itemId
form . value . itemId = checkoutPointItemlist . value . join ( ',' ) ;
if ( pointId . value ) {
updatePoint ( form . value ) . then ( ( res ) => {
// 获取路线经纬度
const path = getPolylinePath ( ) as [ number , number ] [ ] ;
if ( path . length === 0 ) {
ElMessage . warning ( '请绘制巡检路线' ) ;
return ;
}
// 转成字符串传给后端
const [ start _lng , start _lat ] = path . shift ( ) as [ number , number ] ;
const [ end _lng , end _lat ] = path . pop ( ) as [ number , number ] ;
form . value . startLng = ` ${ start _lng } ` ;
form . value . startLat = ` ${ start _lat } ` ;
form . value . endLng = ` ${ end _lng } ` ;
form . value . endLat = ` ${ end _lat } ` ;
form . value . pathPoints = JSON . stringify ( path ) ;
form . value . totalPoints = checkoutPointlist . value . join ( ',' ) ;
console . log ( '提交数据:' , form . value ) ;
if ( routeid . value ) {
updateRoute ( form . value ) . then ( ( ) => {
ElMessage . success ( '编辑成功' ) ;
cancelForm ( ) ;
} ) ;
} else {
addPoint ( form . value ) . then ( ( res ) => {
addRoute ( form . value ) . then ( ( ) => {
ElMessage . success ( '添加成功' ) ;
cancelForm ( ) ;
} ) ;
@@ -152,85 +361,7 @@ const submitForm = () => {
// 返回
const cancelForm = ( ) => {
router . push ( {
path : '/i nspection/point '
} ) ;
} ;
// ! map
const mapReady = ref ( false ) ;
let map = ref ( null ) ;
const marker = ref ( null ) ;
const villageid = localStorage . getItem ( 'villageid' ) ;
onMounted ( async ( ) => {
await nextTick ( ) ;
window . _AMapSecurityConfig = { securityJsCode : '09534b3534d8da53968529a9ce164118' } ;
AMapLoader . load ( {
key : '8e302fe1be5f4db62bc734db23dca149' ,
version : '2.0' ,
plugins : [ 'AMap.Scale' , 'AMap.Marker' ]
} ) . then ( async ( AMap ) => {
const villageinfo = await getVillageByIdAPI ( Number ( villageid ) ) ;
console . log ( villageinfo ) ;
map . value = new AMap . Map ( 'containerMap' , {
viewMode : '3D' ,
zoom : 11 ,
center : [ 116.397428 , 39.90923 ]
} ) ;
mapReady . value = true ;
handleMap ( AMap ) ;
initLocation ( AMap ) ;
} ) ;
} ) ;
/** 清空选择地点 */
function clearLocation ( ) {
form . value . location = '' ;
if ( marker . value ) {
map . value . remove ( marker . value ) ;
marker . value = null ;
}
}
function handleMap ( AMap ) {
// 添加点击事件
map . value . on ( 'click' , ( e ) => {
const lnglat = e . lnglat ;
form . value . location = ` ${ lnglat . lng } , ${ lnglat . lat } ` ;
// 如果有 marker.value, 先移除
if ( marker . value ) {
map . value . remove ( marker . value ) ;
}
// 添加新 marker
marker . value = new AMap . Marker ( {
position : lnglat ,
map : map . value ,
title : form . value . pointName || '巡检点'
} ) ;
} ) ;
}
const initLocation = ( AMap ) => {
if ( ! map . value || ! form . value . location ) return ;
const [ lng , lat ] = form . value . location . split ( ',' ) . map ( ( v ) => Number ( v . trim ( ) ) ) ;
if ( ! Number . isFinite ( lng ) || ! Number . isFinite ( lat ) ) return ;
if ( ! lng || ! lat ) return ;
const lnglat = new AMap . LngLat ( lng , lat ) ;
map . value . setCenter ( lnglat ) ;
map . value . setZoom ( 16 ) ;
if ( marker . value ) {
map . value . remove ( marker . value ) ;
}
marker . value = new AMap . Marker ( {
position : lnglat ,
map : map . value ,
title : form . value . pointName || '巡检点'
path : '/I nspection/route '
} ) ;
} ;
@@ -304,6 +435,7 @@ onUnmounted(() => {
margin - top : 10 px ;
}
. tips {
width : 100 % ;
margin - top : 10 px ;
font - size : 12 px ;
color : # ccc ;