ソースを参照

巡检项目提交

luogang 8 ヶ月 前
コミット
a0eb9f14ff

+ 116 - 0
src/api/deviceCheck/index.ts

@@ -57,3 +57,119 @@ export const delInspectionTeam = (id: string | number | Array<string | number>)
         method: 'delete'
     });
 };
+
+/**
+ * 查询道路位置列表
+ * @param query
+ * @returns {*}
+ */
+export const listRoadSiteInfo = (query) => {
+  return request({
+      url: '/jdyw/roadSiteInfo/list',
+      method: 'get',
+      params: query
+  });
+};
+/**
+* 查询道路位置信息详细
+* @param id
+*/
+export const getRoadSiteInfo = (id) => {
+  return request({
+      url: '/jdyw/roadSiteInfo/' + id,
+      method: 'get'
+  });
+};
+
+/**
+* 新增道路位置信息
+* @param data
+*/
+export const addRoadSiteInfo = (data) => {
+  return request({
+      url: '/jdyw/roadSiteInfo',
+      method: 'post',
+      data: data
+  });
+};
+
+/**
+* 修改道路位置信息
+* @param data
+*/
+export const updateRoadSiteInfo = (data) => {
+  return request({
+      url: '/jdyw/roadSiteInfo',
+      method: 'put',
+      data: data
+  });
+};
+
+/**
+* 删除道路位置信息
+* @param id
+*/
+export const delRoadSiteInfo = (id: string | number | Array<string | number>) => {
+  return request({
+      url: '/jdyw/roadSiteInfo/' + id,
+      method: 'delete'
+  });
+};
+
+/**
+ * 查询巡检项列表
+ * @param query
+ * @returns {*}
+ */
+export const listInspectionItem = (query) => {
+  return request({
+      url: '/jdyw/inspectionItem/list',
+      method: 'get',
+      params: query
+  });
+};
+/**
+* 查询巡检项信息详细
+* @param id
+*/
+export const getInspectionItem = (id) => {
+  return request({
+      url: '/jdyw/inspectionItem/' + id,
+      method: 'get'
+  });
+};
+
+/**
+* 新增巡检项信息
+* @param data
+*/
+export const addInspectionItem = (data) => {
+  return request({
+      url: '/jdyw/inspectionItem',
+      method: 'post',
+      data: data
+  });
+};
+
+/**
+* 修改巡检项信息
+* @param data
+*/
+export const updateInspectionItem = (data) => {
+  return request({
+      url: '/jdyw/inspectionItem',
+      method: 'put',
+      data: data
+  });
+};
+
+/**
+* 删除巡检项信息
+* @param id
+*/
+export const delInspectionItem = (id: string | number | Array<string | number>) => {
+  return request({
+      url: '/jdyw/inspectionItem/' + id,
+      method: 'delete'
+  });
+};

+ 148 - 112
src/views/deviceCheck/checkTeam/index.vue

@@ -24,10 +24,12 @@
             <el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"> 修改 </el-button>
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"> 修改
+            </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"> 删除 </el-button>
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"> 删除
+            </el-button>
           </el-col>
           <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
         </el-row>
@@ -35,12 +37,25 @@
 
       <el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="巡检班组名称" align="center" prop="name" width="150" />
-        <el-table-column label="班组负责人" align="center" prop="phone" width="150" />
-        <el-table-column label="联系电话" align="center" prop="city" width="100" />
-        <el-table-column label="班次" align="center" prop="address" show-overflow-tooltip width="180" />
-        <el-table-column label="成员数" align="center" show-overflow-tooltip prop="remark" width="150" />
-        <el-table-column label="备注" align="center" prop="createTime" width="180" />
+        <el-table-column label="巡检班组名称" align="center" show-overflow-tooltip prop="name" width="150" />
+        <el-table-column label="班组负责人" align="center" prop="ext1.headerName" width="120" />
+        <el-table-column label="联系电话" align="center" prop="ext1.phone" width="180" />
+        <el-table-column label="班次" align="center" prop="workSquence"   >
+          <template #default="scope">
+            <dict-tag :options="work_squence" :value="scope.row.workSquence" />
+          </template>
+        </el-table-column>
+        <el-table-column label="负责路线" align="center" prop="ext1.route"   >
+          <template #default="scope">
+            <dict-tag :options="check_route" :value="scope.row.ext1.route" />
+          </template>
+        </el-table-column>
+        <el-table-column label="成员数" align="center">
+          <template #default="scope">
+            {{scope.row.members.length}}
+          </template>
+        </el-table-column>
+        <el-table-column label="备注" align="center" show-overflow-tooltip prop="remark" width="180" />
         <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
         <el-table-column label="操作" align="center" width="100" fixed="right" class-name="small-padding fixed-width">
           <template #default="scope">
@@ -65,32 +80,62 @@
             <el-form-item label="班组负责人" prop="headerId"
               :rules="[{ required: true, message: '班组负责人不能为空', trigger: 'change' }]">
               <el-select v-model="form.headerId" clearable @change="setUserInfo('leader')">
-                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId"></el-option>
+                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName"
+                  :value="item.userId"></el-option>
               </el-select>
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="联系电话" prop="ext1.phone">
-              <el-input v-model="form.ext1.phone" disabled  />
+              <el-input v-model="form.ext1.phone" disabled />
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="负责路线" prop="ext1.route">
-              <el-select v-model="form.ext1.route" clearable >
-                <el-option label="路线一" value="1"></el-option>
-                <el-option label="路线二" value="2"></el-option>
+              <el-select v-model="form.ext1.route" clearable placeholder="请选择负责路线">
+                <el-option v-for="dict in check_route" :key="dict.value" :label="dict.label"
+                :value="dict.value"></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="班次" prop="workSquence"
+              :rules="[{ required: true, message: '班次不能为空', trigger: 'blur' }]">
+              <el-select v-model="form.workSquence" clearable placeholder="请选择班次">
+                  <el-option v-for="dict in work_squence" :key="dict.value" :label="dict.label"
+                  :value="dict.value"></el-option>
               </el-select>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="班次" prop="workSquence" :rules="[{ required: true, message: '班次不能为空', trigger: 'blur' }]">
-              <el-input v-model="form.workSquence" placeholder="请输入联系电话" />
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="form.remark" placeholder="请输入备注" />
             </el-form-item>
           </el-col>
-          <!-- <el-col :span="12">
-            <el-form-item label="负责设备" prop="deviceTypeIds"
-              :rules="[{ required: true, message: '负责设备不能为空', trigger: 'change' }]">
-              <el-select v-model="form.deviceTypeIds" multiple clearable placeholder="请选择负责设备">
+        </el-row>
+        <SubTitle title="班组成员" />
+        <el-table :data="form.members" max-height="300">
+          <el-table-column label="班组成员" align="center" prop="name">
+            <template #default="scope">
+              <el-select v-model="scope.row.userId" clearable @change="setUserInfo('member', scope.$index)">
+                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName"
+                  :value="item.userId"></el-option>
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column label="联系方式" align="center">
+            <template #default="scope">
+              <el-input v-model="scope.row.phone" placeholder="请输入" />
+            </template>
+          </el-table-column>
+          <el-table-column label="技能描述" align="center" prop="skill">
+            <template #default="scope">
+              <el-input v-model="scope.row.skill" placeholder="请输入" />
+            </template>
+          </el-table-column>
+          <el-table-column label="负责设备类型" align="center">
+            <template #default="scope">
+              <el-select v-model="scope.row.deviceTypeIds" multiple collapse-tags clearable placeholder="请选择负责设备">
                 <el-option v-for="dict in dictGroup.deviceTypeDetailOptions" :key="dict.id"
                   :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
                   <span style="float: left">{{ dict.ext1.brand }}</span>
@@ -99,21 +144,23 @@
                   </span>
                 </el-option>
               </el-select>
-            </el-form-item>
-          </el-col> -->
-          <el-col :span="12">
-            <el-form-item label="备注" prop="remark">
-              <el-input v-model="form.remark" placeholder="请输入备注" />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <SubTitle title="班组成员" />
-        <el-table :data="form.members">
-          <el-table-column label="班组成员" align="center" prop="name" />
-          <el-table-column label="联系方式" align="center" prop="name" />
-          <el-table-column label="技能描述" align="center" prop="name" />
-          <el-table-column label="负责设备类型" align="center" prop="name" />
-          <el-table-column label="操作" align="center" prop="name" />
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="80">
+            <template #header>
+              <div class="operateBtn">
+                <span>操作</span>
+                <el-icon @click="addMembers">
+                  <CirclePlus />
+                </el-icon>
+              </div>
+            </template>
+            <template #default="scope">
+              <el-icon @click="delMembers(scope.$index)">
+                <Delete />
+              </el-icon>
+            </template>
+          </el-table-column>
         </el-table>
       </el-form>
       <template #footer>
@@ -131,7 +178,8 @@ import { listInspectionTeam, getInspectionTeam, delInspectionTeam, addInspection
 import {
   getDeviceTypeDetailList
 } from '@/api/deviceManage/deviceType';
-import {listUser} from '@/api/system/user/index'
+import { listUser } from '@/api/system/user/index'
+import { deepClone } from '@/utils';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const tableList = ref([]);
@@ -152,16 +200,25 @@ const dictGroup = reactive({
   productorOptions: [],
   deviceTypeDetailOptions: []
 })
+const { work_squence } = toRefs<any>(proxy?.useDict('work_squence'));
+const { check_route } = toRefs<any>(proxy?.useDict('check_route'));
 const initFormData = {
   id: undefined,
   name: undefined,
   headerId: undefined,
   workSquence: undefined,
   remark: undefined,
-  members: [],
+  members: [{
+    userId: '',
+    userName: '',
+    phone: '',
+    skill: '',
+    deviceTypeIds: [],
+  }],
   ext1: <any>{
+    headerName:'',
     phone: '',
-    route:''
+    route: ''
   },
   ext2: undefined
 };
@@ -183,7 +240,8 @@ const getList = async () => {
   const res = await listInspectionTeam(queryParams.value);
   tableList.value = res.rows.map((item) => ({
     ...item,
-    ext1: item.ext1 ? JSON.parse(item.ext1) : null
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null,
+    members: item.members ? JSON.parse(item.members) : null
   }));
   total.value = res.total;
   loading.value = false;
@@ -197,7 +255,7 @@ const cancel = () => {
 
 /** 表单重置 */
 const reset = () => {
-  form.value = { ...initFormData };
+  form.value = { ...deepClone(initFormData) };
   addFormRef.value?.resetFields();
 };
 
@@ -233,8 +291,7 @@ const handleUpdate = async (row) => {
   const _id = row?.id || ids.value[0];
   const { data } = await getInspectionTeam(_id);
   data.ext1 = data.ext1 && JSON.parse(data.ext1);
-  deviceTypeDetailList(data.productorId)
-  data.deviceTypeIds = data.deviceTypeIds.split(',');
+  data.members = data.members && JSON.parse(data.members);
   Object.assign(form.value, data);
   dialog.visible = true;
   dialog.title = '修改巡检班组';
@@ -244,9 +301,17 @@ const handleUpdate = async (row) => {
 const submitForm = () => {
   addFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
+      const { ext1, members } = form.value;
+      if (members.length === 0) return proxy?.$modal.msgError('请选择班组成员');
+      let validFlag =false
+      members.forEach(item => {
+        if (item.userId===''||item.deviceTypeIds.length==0) {
+          validFlag=true
+        }
+      })
+      if (validFlag) return proxy?.$modal.msgError('请选择补充班组成员信息');
       buttonLoading.value = true;
-      const { ext1, deviceTypeIds } = form.value;
-      const params = Object.assign(form.value, { ext1: JSON.stringify(ext1), deviceTypeIds: deviceTypeIds.join() });
+      const params = Object.assign(form.value, { ext1: JSON.stringify(ext1), members: JSON.stringify(members) });
       if (form.value.id) {
         await updateInspectionTeam(params).finally(() => (buttonLoading.value = false));
       } else {
@@ -274,42 +339,49 @@ const deviceTypeDetailList = async (productorId = '') => {
     ext1: item.ext1 ? JSON.parse(item.ext1) : null
   }));
 };
-const formatDevice = (val) => {
-  const arr = val.split(',')
-  let label = ''
-  dictGroup.deviceTypeDetailOptions.forEach((item) => {
-    arr.forEach(el => {
-      if (el === item.id) {
-        label += `${item.ext1.brand}${item.xh},`
-      }
-    })
-  })
-  return label.substring(0, label.length - 1)
-}
-const formatDict = (val, option: string) => {
-  let label = '';
-  dictGroup[option].forEach((item) => {
-    if (val === item.id) {
-      label = item.name;
-    }
-  });
-  return label;
-};
 const getUserList = () => {
-  listUser({ status: '0', pageSize: 10000, pageNum: 1 }).then(({ code,rows}) => {
-    if (code===200) {
+  listUser({ status: '0', pageSize: 10000, pageNum: 1 }).then(({ code, rows }) => {
+    if (code === 200) {
       userList.value = rows
     }
   })
 }
-const setUserInfo = (type) => {
-  const {headerId } = form.value
-  userList.value.forEach(item => {
-    if (headerId===item.userId) {
-      form.value.ext1.phone = item.phonenumber
-    }
+const setUserInfo = (type, index = 0) => {
+  if (type === 'leader') {
+    form.value.ext1.phone = ''
+    form.value.ext1.headerName = ''
+    const { headerId } = form.value
+    userList.value.forEach(item => {
+      if (headerId === item.userId) {
+        form.value.ext1.phone = item.phonenumber
+        form.value.ext1.headerName = item.nickName
+      }
+    })
+  } else {
+    const { userId } = form.value.members[index]
+    form.value.members[index].phone = ''
+        form.value.members[index].userName = ''
+    userList.value.forEach(item => {
+      if (userId === item.userId) {
+        form.value.members[index].phone = item.phonenumber
+        form.value.members[index].userName = item.nickName
+      }
+    })
+  }
+
+}
+const addMembers = () => {
+  form.value.members.push({
+    userId: '',
+    userName: '',
+    phone: '',
+    skill: '',
+    deviceTypeIds: [],
   })
 }
+const delMembers = (index) => {
+  form.value.members.splice(index, 1)
+}
 onMounted(() => {
   getList();
   deviceTypeDetailList()
@@ -317,50 +389,14 @@ onMounted(() => {
 });
 </script>
 <style lang="scss" scoped>
-.card-header {
-  display: flex;
-  justify-content: space-between;
-
-  .el-icon {
-    cursor: pointer;
-  }
-}
-
-.custom-tree-node {
-  flex: 1;
+.operateBtn {
   display: flex;
-  justify-content: space-between;
   align-items: center;
-  padding-right: 5px;
-}
 
-.node-operateBtns {
-  div {
-    margin-top: 3px;
-    text-align: center;
+  .el-icon {
+    color: #409eff;
+    margin-left: 5px;
     cursor: pointer;
-
-    &:hover {
-      color: #409eff;
-    }
-  }
-}
-</style>
-<style lang="scss">
-.customDrawer {
-  .el-drawer__header {
-    align-items: flex-start !important;
-    margin-bottom: 10px;
-
-    .drawer-title {
-      .title-name {
-        color: #000;
-      }
-    }
-  }
-
-  .el-drawer__body {
-    padding-top: 0;
   }
 }
 </style>

+ 301 - 0
src/views/deviceCheck/patrolItems/index.vue

@@ -0,0 +1,301 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
+      :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
+            <el-form-item label="巡检项名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入巡检项名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="巡检设备类型" prop="deviceDetailId">
+              <el-select v-model="queryParams.deviceDetailId"  clearable placeholder="请选择巡检设备类型">
+                <el-option v-for="dict in dictGroup.deviceTypeDetailOptions" :key="dict.id"
+                  :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
+                  <span style="float: left">{{ dict.ext1.brand }}</span>
+                  <span style="float: right;color: var(--el-text-color-secondary);font-size: 13px;">
+                    {{ dict.xh }}
+                  </span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
+              <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"> 修改
+            </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"> 删除
+            </el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="巡检项编号" align="center" show-overflow-tooltip prop="id" width="150" />
+        <el-table-column label="巡检项名称" align="center" show-overflow-tooltip prop="name" width="180" />
+        <el-table-column label="巡检设备类型" align="center" prop="deviceDetailId" width="180" >
+          <template #default="scope">
+            {{formatDevice(scope.row.deviceDetailId)}}
+          </template>
+        </el-table-column>
+        <el-table-column label="检验方法" align="center" prop="detectionMethod" width="120" />
+        <el-table-column label="检验工具" align="center" show-overflow-tooltip prop="detectionTools" width="120" />
+        <el-table-column label="检验标准" align="center" show-overflow-tooltip prop="detectionStandard" width="120" />
+        <el-table-column label="判断标准" align="center" show-overflow-tooltip prop="judgeStandard" width="120" />
+        <el-table-column label="标准上限" align="center" show-overflow-tooltip prop="judgeMaxValue" width="120" />
+        <el-table-column label="标准下限" align="center" show-overflow-tooltip prop="judgeMinValue" width="120" />
+        <el-table-column label="操作" align="center" width="150" fixed="right" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
+            <el-button size="small" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+        :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="900px" append-to-body>
+      <el-form ref="addFormRef" :model="form" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="巡检项名称" prop="name" :rules="[{ required: true, message: '巡检项名称不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.name" placeholder="请输入巡检项名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="设备类型" prop="deviceDetailId"
+              :rules="[{ required: true, message: '设备类型不能为空', trigger: 'change' }]">
+              <el-select v-model="form.deviceDetailId"  clearable placeholder="请选择设备类型">
+                <el-option v-for="dict in dictGroup.deviceTypeDetailOptions" :key="dict.id"
+                  :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
+                  <span style="float: left">{{ dict.ext1.brand }}</span>
+                  <span style="float: right;color: var(--el-text-color-secondary);font-size: 13px;">
+                    {{ dict.xh }}
+                  </span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="检验方法" prop="detectionMethod" :rules="[{ required: true, message: '检验方法不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.detectionMethod" placeholder="请输入检验方法" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="检验工具" prop="detectionTools" >
+              <el-input v-model="form.detectionTools" placeholder="请输入检验工具" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="检验标准" prop="detectionStandard" :rules="[{ required: true, message: '检验标准不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.detectionStandard" placeholder="请输入检验标准" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="判断标准" prop="judgeStandard" :rules="[{ required: true, message: '判断标准不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.judgeStandard" placeholder="请输入判断标准" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="标准上限" prop="judgeMaxValue" >
+              <el-input v-model="form.judgeMaxValue" placeholder="请输入标准上限" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="标准下限" prop="judgeMinValue" >
+              <el-input v-model="form.judgeMinValue" placeholder="请输入标准下限" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="备注" prop="remark" >
+              <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+          <el-button @click="cancel"> 取 消 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="PatrolItems" lang="ts">
+import { listInspectionItem, getInspectionItem, delInspectionItem, addInspectionItem, updateInspectionItem } from '@/api/deviceCheck/index';
+import {
+  getDeviceTypeDetailList
+} from '@/api/deviceManage/deviceType';
+import { deepClone } from '@/utils';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const tableList = ref([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const queryFormRef = ref<ElFormInstance>();
+const addFormRef = ref<ElFormInstance>();
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+const initFormData = {
+  id: undefined,
+  name: undefined,
+  deviceDetailId: undefined,
+  detectionMethod: undefined,
+  detectionStandard: undefined,
+  detectionTools: undefined,
+  judgeStandard: undefined,
+  judgeMaxValue: undefined,
+  judgeMinValue: undefined,
+  remark: undefined,
+  ext1: undefined,
+  ext2: undefined
+};
+const formData = reactive({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    deviceDetailId:undefined,
+    params: {}
+  }
+});
+
+const { queryParams, form } = toRefs(formData);
+const dictGroup = reactive({
+  deviceTypeDetailOptions: []
+})
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listInspectionItem(queryParams.value);
+  tableList.value = res.rows.map((item) => ({
+    ...item,
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null,
+  }));
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...deepClone(initFormData) };
+  addFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '新增巡检项';
+};
+/** 修改按钮操作 */
+const handleUpdate = async (row) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const { data } = await getInspectionItem(_id);
+  data.ext1 = data.ext1 && JSON.parse(data.ext1);
+  Object.assign(form.value, data);
+  dialog.visible = true;
+  dialog.title = '修改巡检项';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  addFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      const { ext1 } = form.value;
+      buttonLoading.value = true;
+      const params = Object.assign({}, form.value, { ext1: JSON.stringify(ext1) });
+      if (form.value.id) {
+        await updateInspectionItem(params).finally(() => (buttonLoading.value = false));
+      } else {
+        await addInspectionItem(params).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+/** 删除按钮操作 */
+const handleDelete = async (row) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
+  await delInspectionItem(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+const deviceTypeDetailList = async () => {
+  const res = await getDeviceTypeDetailList({  });
+  dictGroup.deviceTypeDetailOptions = res.rows.map((item) => ({
+    ...item,
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null
+  }));
+};
+const formatDevice = (val) => {
+  let label = ''
+  dictGroup.deviceTypeDetailOptions.forEach((item) => {
+      if (val===item.id) {
+        label=`${item.ext1.brand}${item.xh}`
+      }
+  })
+  return label
+}
+onMounted(() => {
+  getList();
+  deviceTypeDetailList()
+});
+</script>
+<style lang="scss" scoped>
+</style>

+ 301 - 0
src/views/deviceCheck/patrolPlan/index.vue

@@ -0,0 +1,301 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
+      :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
+            <el-form-item label="巡检项名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入巡检项名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="巡检设备类型" prop="deviceDetailId">
+              <el-select v-model="queryParams.deviceDetailId"  clearable placeholder="请选择巡检设备类型">
+                <el-option v-for="dict in dictGroup.deviceTypeDetailOptions" :key="dict.id"
+                  :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
+                  <span style="float: left">{{ dict.ext1.brand }}</span>
+                  <span style="float: right;color: var(--el-text-color-secondary);font-size: 13px;">
+                    {{ dict.xh }}
+                  </span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
+              <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"> 修改
+            </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"> 删除
+            </el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="巡检项编号" align="center" show-overflow-tooltip prop="id" width="150" />
+        <el-table-column label="巡检项名称" align="center" show-overflow-tooltip prop="name" width="180" />
+        <el-table-column label="巡检设备类型" align="center" prop="deviceDetailId" width="180" >
+          <template #default="scope">
+            {{formatDevice(scope.row.deviceDetailId)}}
+          </template>
+        </el-table-column>
+        <el-table-column label="检验方法" align="center" prop="detectionMethod" width="120" />
+        <el-table-column label="检验工具" align="center" show-overflow-tooltip prop="detectionTools" width="120" />
+        <el-table-column label="检验标准" align="center" show-overflow-tooltip prop="detectionStandard" width="120" />
+        <el-table-column label="判断标准" align="center" show-overflow-tooltip prop="judgeStandard" width="120" />
+        <el-table-column label="标准上限" align="center" show-overflow-tooltip prop="judgeMaxValue" width="120" />
+        <el-table-column label="标准下限" align="center" show-overflow-tooltip prop="judgeMinValue" width="120" />
+        <el-table-column label="操作" align="center" width="150" fixed="right" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
+            <el-button size="small" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+        :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="900px" append-to-body>
+      <el-form ref="addFormRef" :model="form" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="巡检项名称" prop="name" :rules="[{ required: true, message: '巡检项名称不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.name" placeholder="请输入巡检项名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="设备类型" prop="deviceDetailId"
+              :rules="[{ required: true, message: '设备类型不能为空', trigger: 'change' }]">
+              <el-select v-model="form.deviceDetailId"  clearable placeholder="请选择设备类型">
+                <el-option v-for="dict in dictGroup.deviceTypeDetailOptions" :key="dict.id"
+                  :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
+                  <span style="float: left">{{ dict.ext1.brand }}</span>
+                  <span style="float: right;color: var(--el-text-color-secondary);font-size: 13px;">
+                    {{ dict.xh }}
+                  </span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="检验方法" prop="detectionMethod" :rules="[{ required: true, message: '检验方法不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.detectionMethod" placeholder="请输入检验方法" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="检验工具" prop="detectionTools" >
+              <el-input v-model="form.detectionTools" placeholder="请输入检验工具" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="检验标准" prop="detectionStandard" :rules="[{ required: true, message: '检验标准不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.detectionStandard" placeholder="请输入检验标准" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="判断标准" prop="judgeStandard" :rules="[{ required: true, message: '判断标准不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.judgeStandard" placeholder="请输入判断标准" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="标准上限" prop="judgeMaxValue" >
+              <el-input v-model="form.judgeMaxValue" placeholder="请输入标准上限" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="标准下限" prop="judgeMinValue" >
+              <el-input v-model="form.judgeMinValue" placeholder="请输入标准下限" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="备注" prop="remark" >
+              <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+          <el-button @click="cancel"> 取 消 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="PatrolItems" lang="ts">
+import { listInspectionItem, getInspectionItem, delInspectionItem, addInspectionItem, updateInspectionItem } from '@/api/deviceCheck/index';
+import {
+  getDeviceTypeDetailList
+} from '@/api/deviceManage/deviceType';
+import { deepClone } from '@/utils';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const tableList = ref([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const queryFormRef = ref<ElFormInstance>();
+const addFormRef = ref<ElFormInstance>();
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+const initFormData = {
+  id: undefined,
+  name: undefined,
+  deviceDetailId: undefined,
+  detectionMethod: undefined,
+  detectionStandard: undefined,
+  detectionTools: undefined,
+  judgeStandard: undefined,
+  judgeMaxValue: undefined,
+  judgeMinValue: undefined,
+  remark: undefined,
+  ext1: undefined,
+  ext2: undefined
+};
+const formData = reactive({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    deviceDetailId:undefined,
+    params: {}
+  }
+});
+
+const { queryParams, form } = toRefs(formData);
+const dictGroup = reactive({
+  deviceTypeDetailOptions: []
+})
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listInspectionItem(queryParams.value);
+  tableList.value = res.rows.map((item) => ({
+    ...item,
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null,
+  }));
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...deepClone(initFormData) };
+  addFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '新增巡检项';
+};
+/** 修改按钮操作 */
+const handleUpdate = async (row) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const { data } = await getInspectionItem(_id);
+  data.ext1 = data.ext1 && JSON.parse(data.ext1);
+  Object.assign(form.value, data);
+  dialog.visible = true;
+  dialog.title = '修改巡检项';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  addFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      const { ext1 } = form.value;
+      buttonLoading.value = true;
+      const params = Object.assign({}, form.value, { ext1: JSON.stringify(ext1) });
+      if (form.value.id) {
+        await updateInspectionItem(params).finally(() => (buttonLoading.value = false));
+      } else {
+        await addInspectionItem(params).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+/** 删除按钮操作 */
+const handleDelete = async (row) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
+  await delInspectionItem(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+const deviceTypeDetailList = async () => {
+  const res = await getDeviceTypeDetailList({  });
+  dictGroup.deviceTypeDetailOptions = res.rows.map((item) => ({
+    ...item,
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null
+  }));
+};
+const formatDevice = (val) => {
+  let label = ''
+  dictGroup.deviceTypeDetailOptions.forEach((item) => {
+      if (val===item.id) {
+        label=`${item.ext1.brand}${item.xh}`
+      }
+  })
+  return label
+}
+onMounted(() => {
+  getList();
+  deviceTypeDetailList()
+});
+</script>
+<style lang="scss" scoped>
+</style>

+ 368 - 0
src/views/deviceCheck/roadLocation/index.vue

@@ -0,0 +1,368 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
+      :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
+            <el-form-item label="道路点位名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入道路点位名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery"> 搜索 </el-button>
+              <el-button icon="Refresh" @click="resetQuery"> 重置 </el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增 </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"> 修改
+            </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"> 删除
+            </el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList" />
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="位置别名" align="center" show-overflow-tooltip prop="name" width="150" />
+        <el-table-column label="详细地址" align="center" show-overflow-tooltip prop="address" width="180" />
+        <el-table-column label="经度" align="center" prop="longitude" width="180" />
+        <el-table-column label="纬度" align="center" prop="latitude" width="180" />
+        <el-table-column label="负责人" align="center" show-overflow-tooltip prop="ext1.leaderName" width="120" />
+        <el-table-column label="联系电话" align="center" show-overflow-tooltip prop="ext1.phone" width="180" />
+        <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
+        <el-table-column label="操作" align="center" width="150" fixed="right" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button size="small" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
+            <el-button size="small" link type="primary" @click="bindDevice(scope.row)">绑定设备</el-button>
+            <el-button size="small" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
+        :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改对话框 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="900px" append-to-body>
+      <el-form ref="addFormRef" :model="form" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="位置别名" prop="name" :rules="[{ required: true, message: '位置别名不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.name" placeholder="请输入位置别名" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="详细地址" prop="address"
+              :rules="[{ required: true, message: '详细地址不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.address" placeholder="请输入详细地址" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="负责人" prop="leaderId"
+              :rules="[{ required: true, message: '负责人不能为空', trigger: 'change' }]">
+              <el-select v-model="form.leaderId" clearable @change="setUserInfo">
+                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName"
+                  :value="item.userId"></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系电话" prop="ext1.phone">
+              <el-input v-model="form.ext1.phone" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="地理位置" prop="longitude"
+              :rules="[{ required: true, message: '地理位置不能为空', trigger: 'blur' }]">
+              <div id="map" class="bdmap"></div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+          <el-button @click="cancel"> 取 消 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="deviceDialog" title="选择设备" width="750px" append-to-body>
+      <el-table ref="deviceTableRef" :data="deviceList" @selection-change="deviceSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="设备编号" align="center" prop="sn" />
+        <el-table-column label="设备名称" align="center" prop="name" />
+        <el-table-column label="设备位置" align="center" show-overflow-tooltip prop="ext1.address" />
+      </el-table>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="deviceConfirm"> 确 定 </el-button>
+          <el-button @click="deviceDialog = false"> 取 消 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="RoadLocation" lang="ts">
+import { listRoadSiteInfo, getRoadSiteInfo, delRoadSiteInfo, addRoadSiteInfo, updateRoadSiteInfo } from '@/api/deviceCheck/index';
+import { listUser } from '@/api/system/user/index'
+import { listDevice } from '@/api/deviceManage/device';
+import { deepClone } from '@/utils';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const tableList = ref([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const queryFormRef = ref<ElFormInstance>();
+const addFormRef = ref<ElFormInstance>();
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+const deviceDialog = ref(false)
+const deviceList = ref([]);
+const deviceSelectedIds = ref([])
+const deviceTableRef = ref<ElTableInstance>()
+const bdmap = ref(null)
+const initFormData = {
+  id: undefined,
+  name: undefined,
+  address: undefined,
+  latitude: undefined,
+  longitude: undefined,
+  leaderId: undefined,
+  devices: undefined,
+  remark: undefined,
+  ext1: <any>{
+    leaderName: '',
+    phone: '',
+  },
+  ext2: undefined
+};
+const formData = reactive({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    params: {}
+  }
+});
+
+const { queryParams, form } = toRefs(formData);
+const userList = ref([])
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listRoadSiteInfo(queryParams.value);
+  tableList.value = res.rows.map((item) => ({
+    ...item,
+    ext1: item.ext1 ? JSON.parse(item.ext1) : null,
+  }));
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...deepClone(initFormData) };
+  addFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+const deviceSelectionChange = (selection) => {
+  deviceSelectedIds.value = selection.map((item) => item.id);
+}
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '新增道路点位';
+  setTimeout(() => {
+    initMap();
+  }, 100);
+};
+const bindDevice = (row) => {
+  deviceDialog.value = true
+  form.value = row
+  const selectedDevices =row.devices||''
+  nextTick(() => {
+    if (deviceTableRef.value) {
+      deviceTableRef.value.clearSelection();
+      deviceList.value.forEach(item => {
+        if (selectedDevices.includes(item.id)) {
+          deviceTableRef.value.toggleRowSelection(item, true);
+        }
+      });
+    }
+  });
+
+}
+/** 修改按钮操作 */
+const handleUpdate = async (row) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const { data } = await getRoadSiteInfo(_id);
+  data.ext1 = data.ext1 && JSON.parse(data.ext1);
+  Object.assign(form.value, data);
+  dialog.visible = true;
+  dialog.title = '修改道路点位';
+  setTimeout(() => {
+    initMap();
+    const { longitude, latitude } = form.value
+    if (longitude) {
+      const pt = new BMapGL.Point(longitude, latitude);
+      const marker = new BMapGL.Marker(pt);
+      bdmap.value.addOverlay(marker);
+      bdmap.value.centerAndZoom(pt, 20);
+    }
+  }, 100);
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  addFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      const { ext1 } = form.value;
+      buttonLoading.value = true;
+      const params = Object.assign({}, form.value, { ext1: JSON.stringify(ext1) });
+      if (form.value.id) {
+        await updateRoadSiteInfo(params).finally(() => (buttonLoading.value = false));
+      } else {
+        await addRoadSiteInfo(params).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+const deviceConfirm = async () => {
+  if (deviceSelectedIds.value.length === 0) return proxy?.$modal.msgError('请勾选设备');
+  const { ext1 } = form.value;
+  buttonLoading.value = true;
+  const params = Object.assign({}, form.value, { ext1: JSON.stringify(ext1), devices: deviceSelectedIds.value.join() });
+  await updateRoadSiteInfo(params).finally(() => (buttonLoading.value = false));
+  proxy?.$modal.msgSuccess('操作成功');
+  deviceDialog.value = false;
+  await getList();
+}
+/** 删除按钮操作 */
+const handleDelete = async (row) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
+  await delRoadSiteInfo(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+const getUserList = () => {
+  listUser({ status: '0', pageSize: 10000, pageNum: 1 }).then(({ code, rows }) => {
+    if (code === 200) {
+      userList.value = rows
+    }
+  })
+}
+const initMap = () => {
+  var map = new BMapGL.Map('map'); // 创建地图实例
+  var point = new BMapGL.Point(118.879999, 32.016216); // 创建点坐标
+  map.centerAndZoom(point, 20);
+  map.enableScrollWheelZoom(true);
+  map.addEventListener('click', (evt) => {
+    bdmap.value.clearOverlays();
+    const { lng, lat } = evt.latlng;
+    form.value.longitude = lng
+    form.value.latitude = lat
+    const pt = new BMapGL.Point(lng, lat);
+    const marker = new BMapGL.Marker(pt);
+    bdmap.value.addOverlay(marker);
+  });
+  bdmap.value = map;
+};
+const setUserInfo = () => {
+  form.value.ext1.phone = ''
+  form.value.ext1.leaderName = ''
+  const { leaderId } = form.value
+  userList.value.forEach(item => {
+    if (leaderId === item.userId) {
+      form.value.ext1.phone = item.phonenumber
+      form.value.ext1.leaderName = item.nickName
+    }
+  })
+}
+
+const getDeviceList = async () => {
+  const { code, rows } = await listDevice({});
+  if (code === 200) {
+    deviceList.value = rows.map((item) => ({
+      ...item,
+      controlFuncs: item.controlFuncs !== '{}' ? JSON.parse(item.controlFuncs) : [],
+      dataPoints: item.dataPoints !== '{}' ? JSON.parse(item.dataPoints) : [],
+      events: item.events !== '{}' ? JSON.parse(item.events) : [],
+      ext1: item.ext1 ? JSON.parse(item.ext1) : null
+    }));;
+  }
+}
+onMounted(() => {
+  getList();
+  getUserList();
+  getDeviceList();
+});
+</script>
+<style lang="scss" scoped>
+.operateBtn {
+  display: flex;
+  align-items: center;
+
+  .el-icon {
+    color: #409eff;
+    margin-left: 5px;
+    cursor: pointer;
+  }
+}
+
+.bdmap {
+  width: 100%;
+  height: 250px;
+}
+</style>

+ 2 - 2
src/views/deviceManage/archives/index.vue

@@ -401,7 +401,7 @@ const initFormData = {
   useTime: '',
   qualityEndTime: '',
   status: '',
-  remark: '',
+  remark: undefined,
   dataPoints: '{}',
   controlFuncs: '{}',
   events: '{}',
@@ -575,7 +575,7 @@ const submitForm = () => {
     if (valid) {
       buttonLoading.value = true;
       const { ext1 } = form.value;
-      const params = Object.assign(form.value, { ext1: JSON.stringify(ext1) });
+      const params = Object.assign({},form.value, { ext1: JSON.stringify(ext1) });
       if (form.value.id) {
         await updateDevice(params).finally(() => (buttonLoading.value = false));
       } else {