Browse Source

巡检计划提交

luogang 8 tháng trước cách đây
mục cha
commit
6e0c729a53

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

@@ -173,3 +173,61 @@ export const delInspectionItem = (id: string | number | Array<string | number>)
       method: 'delete'
   });
 };
+
+/**
+ * 查询巡检任务列表
+ * @param query
+ * @returns {*}
+ */
+export const listInspectionTask = (query) => {
+  return request({
+      url: '/jdyw/inspectionTask/list',
+      method: 'get',
+      params: query
+  });
+};
+/**
+* 查询巡检任务信息详细
+* @param id
+*/
+export const getInspectionTask = (id) => {
+  return request({
+      url: '/jdyw/inspectionTask/' + id,
+      method: 'get'
+  });
+};
+
+/**
+* 新增巡检任务信息
+* @param data
+*/
+export const addInspectionTask = (data) => {
+  return request({
+      url: '/jdyw/inspectionTask',
+      method: 'post',
+      data: data
+  });
+};
+
+/**
+* 修改巡检任务信息
+* @param data
+*/
+export const updateInspectionTask = (data) => {
+  return request({
+      url: '/jdyw/inspectionTask',
+      method: 'put',
+      data: data
+  });
+};
+
+/**
+* 删除巡检任务信息
+* @param id
+*/
+export const delInspectionTask = (id: string | number | Array<string | number>) => {
+  return request({
+      url: '/jdyw/inspectionTask/' + id,
+      method: 'delete'
+  });
+};

+ 58 - 0
src/api/projectManage/index.ts

@@ -0,0 +1,58 @@
+import request from '@/utils/request';
+/**
+ * 查询项目列表
+ * @param query
+ * @returns {*}
+ */
+export const listProjectInfo = (query) => {
+    return request({
+        url: '/jdyw/projectInfo/list',
+        method: 'get',
+        params: query
+    });
+};
+/**
+ * 查询项目信息详细
+ * @param id
+ */
+export const getProjectInfo = (id) => {
+    return request({
+        url: '/jdyw/projectInfo/' + id,
+        method: 'get'
+    });
+};
+
+/**
+ * 新增项目信息
+ * @param data
+ */
+export const addProjectInfo = (data) => {
+    return request({
+        url: '/jdyw/projectInfo',
+        method: 'post',
+        data: data
+    });
+};
+
+/**
+ * 修改项目信息
+ * @param data
+ */
+export const updateProjectInfo = (data) => {
+    return request({
+        url: '/jdyw/projectInfo',
+        method: 'put',
+        data: data
+    });
+};
+
+/**
+ * 删除项目信息
+ * @param id
+ */
+export const delProjectInfo = (id: string | number | Array<string | number>) => {
+    return request({
+        url: '/jdyw/projectInfo/' + id,
+        method: 'delete'
+    });
+};

+ 248 - 81
src/views/deviceCheck/patrolPlan/index.vue

@@ -9,7 +9,7 @@
               <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-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>
@@ -48,19 +48,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" 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" >
+        <el-table-column label="计划名称" align="center" show-overflow-tooltip prop="name" width="150" />
+        <el-table-column label="计划编号" align="center" show-overflow-tooltip prop="id" width="150" />
+        <el-table-column label="计划开始时间" align="center" show-overflow-tooltip prop="startTime" width="150" />
+        <el-table-column label="计划结束时间" align="center" show-overflow-tooltip prop="endTime" width="150" />
+        <el-table-column label="巡检类型" align="center"   width="100" >
           <template #default="scope">
-            {{formatDevice(scope.row.deviceDetailId)}}
+            <dict-tag :options="inspection_type" :value="scope.row.inspectionType" />
           </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" show-overflow-tooltip prop="inspectionCycle" width="100" />
+        <el-table-column label="巡检员" align="center" show-overflow-tooltip prop="ext1.inspectionPersonName" width="120" />
+        <el-table-column label="备注" align="center" show-overflow-tooltip prop="remark" width="150" />
+        <el-table-column label="计划状态" align="center"  prop="status" width="120" >
+          <template #default="scope">
+            <dict-tag :options="inspection_status" :value="scope.row.status" />
+          </template>
+        </el-table-column>
+        <el-table-column label="已执行任务次数" align="center" show-overflow-tooltip prop="judgeMinValue" width="120" />
+        <el-table-column label="创建时间" align="center" show-overflow-tooltip prop="createTime" 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>
@@ -72,64 +78,116 @@
         :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-dialog v-model="dialog.visible" :title="dialog.title" width="950px" append-to-body>
+      <el-form ref="addFormRef" :model="form" label-width="110px">
         <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-col :span="8">
+            <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-col :span="8">
+            <el-form-item label="计划开始时间" prop="startTime"
+              :rules="[{ required: true, message: '计划开始时间不能为空', trigger: 'change' }]">
+              <el-date-picker v-model="form.startTime" style="width: 100%;" value-format="YYYY-MM-DD hh:mm:ss" type="datetime"
+                placeholder="请选择开始时间" />
             </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-col :span="8">
+            <el-form-item label="计划结束时间" prop="endTime"
+              :rules="[{ required: true, message: '计划结束时间不能为空', trigger: 'change' }]">
+              <el-date-picker v-model="form.endTime" style="width: 100%;" value-format="YYYY-MM-DD hh:mm:ss" type="datetime"
+                placeholder="请选择结束时间" />
             </el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item label="检验工具" prop="detectionTools" >
-              <el-input v-model="form.detectionTools" placeholder="请输入检验工具" />
+          <el-col :span="8">
+            <el-form-item label="巡检类型" prop="inspectionType"
+              :rules="[{ required: true, message: '巡检类型不能为空', trigger: 'change' }]">
+              <el-select v-model="form.inspectionType" clearable placeholder="请选择巡检类型" @change="inspectionTypeChange">
+                <el-option v-for="dict in inspection_type" :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="detectionStandard" :rules="[{ required: true, message: '检验标准不能为空', trigger: 'blur' }]">
-              <el-input v-model="form.detectionStandard" placeholder="请输入检验标准" />
+          <el-col :span="8">
+            <el-form-item label="巡检周期(天)" prop="inspectionCycle"
+              :rules="[{ required: true, message: '巡检周期不能为空', trigger: 'blur' }]">
+              <el-input-number v-model="form.inspectionCycle" :disabled="cycleDisabled" style="width:100%" :min="1" />
             </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-col :span="8">
+            <el-form-item label="巡检范围" prop="inspectionRange"
+              :rules="[{ required: true, message: '巡检类型不能为空', trigger: 'change' }]">
+              <el-select v-model="form.inspectionRange" clearable placeholder="请选择巡检范围">
+                <el-option label="按设备" value="1"></el-option>
+                <el-option label="按道路点位" value="2"></el-option>
+                <el-option label="按项目" value="3"></el-option>
+              </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item label="标准上限" prop="judgeMaxValue" >
-              <el-input v-model="form.judgeMaxValue" placeholder="请输入标准上限" />
+          <el-col :span="8">
+            <el-form-item label="巡检员" prop="inspectionPersonId"
+              :rules="[{ required: true, message: '巡检员不能为空', trigger: 'change' }]">
+              <el-cascader v-model="form.inspectionPersonId" :props="{ emitPath: false }" :options="userOptions"
+                placeholder="请选择巡检员" clearable :show-all-levels="false" @change="setUserInfo" />
             </el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item label="标准下限" prop="judgeMinValue" >
-              <el-input v-model="form.judgeMinValue" placeholder="请输入标准下限" />
+          <el-col :span="8">
+            <el-form-item label="联系电话" prop="ext1.phone">
+              <el-input v-model="form.ext1.phone" disabled 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-col :span="8">
+            <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.inspectionItem" max-height="300">
+          <el-table-column label="设备类型名称" align="center">
+            <template #default="scope">
+              <el-select v-model="scope.row.deviceTypeId" clearable placeholder="请选择设备类型名称"
+                @change="deviceTypeChange(scope.$index, $event)">
+                <el-option v-for="dict in dictGroup.deviceTypeOptions" :key="dict.id" :label="dict.name"
+                  :value="dict.id">
+                </el-option>
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column label="设备编号" align="center">
+            <template #default="scope">
+              <el-select v-model="scope.row.deviceTypeDetailIds" multiple collapse-tags clearable placeholder="请选择设备编号">
+                <el-option v-for="dict in dictGroup.deviceOptions" :key="dict.id" :label="dict.sn" :value="dict.id">
+                </el-option>
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column label="巡检项目" align="center">
+            <template #default="scope">
+              <el-select v-model="scope.row.items" multiple collapse-tags clearable placeholder="请选择巡检项目">
+                <el-option v-for="dict in dictGroup.inspectionItemOptions" :key="dict.id" :label="dict.name"
+                  :value="dict.id">
+                </el-option>
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="80">
+            <template #header>
+              <div class="operateBtn">
+                <span>操作</span>
+                <el-icon @click="addInspectionItem">
+                  <CirclePlus />
+                </el-icon>
+              </div>
+            </template>
+            <template #default="scope">
+              <el-icon @click="delInspectionItem(scope.$index)">
+                <Delete />
+              </el-icon>
+            </template>
+          </el-table-column>
+        </el-table>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
@@ -141,11 +199,15 @@
   </div>
 </template>
 
-<script setup name="PatrolItems" lang="ts">
-import { listInspectionItem, getInspectionItem, delInspectionItem, addInspectionItem, updateInspectionItem } from '@/api/deviceCheck/index';
+<script setup name="PatrolPlan" lang="ts">
+import { listInspectionTeam, listInspectionItem, listInspectionTask, getInspectionTask, delInspectionTask, addInspectionTask, updateInspectionTask } from '@/api/deviceCheck/index';
 import {
+  listDeviceType,
   getDeviceTypeDetailList
 } from '@/api/deviceManage/deviceType';
+import {
+  listDevice,
+} from '@/api/deviceManage/device';
 import { deepClone } from '@/utils';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const tableList = ref([]);
@@ -165,15 +227,23 @@ const dialog = reactive<DialogOption>({
 const initFormData = {
   id: undefined,
   name: undefined,
-  deviceDetailId: undefined,
-  detectionMethod: undefined,
-  detectionStandard: undefined,
-  detectionTools: undefined,
-  judgeStandard: undefined,
-  judgeMaxValue: undefined,
-  judgeMinValue: undefined,
+  startTime: undefined,
+  endTime: undefined,
+  inspectionType: undefined,
+  inspectionCycle: 1,
+  status: 0,
+  inspectionRange: undefined,
+  inspectionPersonId: undefined,
+  inspectionItem: [{
+    deviceTypeId: undefined,
+    deviceTypeDetailIds: [],
+    items: []
+  }],
   remark: undefined,
-  ext1: undefined,
+  ext1: <any>{
+    phone: undefined,
+    inspectionPersonName: undefined,
+  },
   ext2: undefined
 };
 const formData = reactive({
@@ -182,19 +252,24 @@ const formData = reactive({
     pageNum: 1,
     pageSize: 10,
     name: undefined,
-    deviceDetailId:undefined,
+    deviceDetailId: undefined,
     params: {}
   }
 });
-
+const cycleDisabled = ref(false)
 const { queryParams, form } = toRefs(formData);
+const { inspection_type } = toRefs<any>(proxy?.useDict('inspection_type'));
+const { inspection_status } = toRefs<any>(proxy?.useDict('inspection_status'));
 const dictGroup = reactive({
-  deviceTypeDetailOptions: []
+  deviceOptions: [],
+  deviceTypeOptions: [],
+  inspectionItemOptions: []
 })
+const userOptions = ref([])
 /** 查询列表 */
 const getList = async () => {
   loading.value = true;
-  const res = await listInspectionItem(queryParams.value);
+  const res = await listInspectionTask(queryParams.value);
   tableList.value = res.rows.map((item) => ({
     ...item,
     ext1: item.ext1 ? JSON.parse(item.ext1) : null,
@@ -237,30 +312,38 @@ const handleSelectionChange = (selection) => {
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = '新增巡检';
+  dialog.title = '新增巡检计划';
 };
 /** 修改按钮操作 */
 const handleUpdate = async (row) => {
   reset();
   const _id = row?.id || ids.value[0];
-  const { data } = await getInspectionItem(_id);
+  const { data } = await getInspectionTask(_id);
   data.ext1 = data.ext1 && JSON.parse(data.ext1);
   Object.assign(form.value, data);
   dialog.visible = true;
-  dialog.title = '修改巡检';
+  dialog.title = '修改巡检计划';
 };
 
 /** 提交按钮 */
 const submitForm = () => {
   addFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
-      const { ext1 } = form.value;
+      const { ext1, inspectionItem } = form.value;
+      if (inspectionItem.length === 0) return proxy?.$modal.msgError('请添加巡检内容');
+      let validFlag = false
+      inspectionItem.forEach(item => {
+        if (item.deviceTypeId === '' || item.deviceTypeDetailIds.length == 0 || item.items.length == 0) {
+          validFlag = true
+        }
+      })
+      if (validFlag) return proxy?.$modal.msgError('请选择补充巡检内容信息');
       buttonLoading.value = true;
-      const params = Object.assign({}, form.value, { ext1: JSON.stringify(ext1) });
+      const params = Object.assign({}, form.value, { ext1: JSON.stringify(ext1), inspectionItem: JSON.stringify(inspectionItem) });
       if (form.value.id) {
-        await updateInspectionItem(params).finally(() => (buttonLoading.value = false));
+        await updateInspectionTask(params).finally(() => (buttonLoading.value = false));
       } else {
-        await addInspectionItem(params).finally(() => (buttonLoading.value = false));
+        await addInspectionTask(params).finally(() => (buttonLoading.value = false));
       }
       proxy?.$modal.msgSuccess('操作成功');
       dialog.visible = false;
@@ -272,30 +355,114 @@ const submitForm = () => {
 const handleDelete = async (row) => {
   const _ids = row?.id || ids.value;
   await proxy?.$modal.confirm('是否确认删除?').finally(() => (loading.value = false));
-  await delInspectionItem(_ids);
+  await delInspectionTask(_ids);
   proxy?.$modal.msgSuccess('删除成功');
   await getList();
 };
-const deviceTypeDetailList = async () => {
-  const res = await getDeviceTypeDetailList({  });
-  dictGroup.deviceTypeDetailOptions = res.rows.map((item) => ({
+const deviceTypeChange = (index, val) => {
+  form.value.inspectionItem[index].deviceTypeDetailIds = []
+  getDeviceList(val)
+}
+const getDeviceList = async (deviceTypeId) => {
+  let deviceTypeDetailList = []
+  dictGroup.deviceOptions = []
+  await getDeviceTypeDetailList({ deviceTypeId }).then(({ code, rows }) => {
+    if (code === 200) {
+      deviceTypeDetailList = rows
+    }
+  })
+  deviceTypeDetailList.forEach(async (item) => {
+    const res = await listDevice({ devTypeDetailId: item.id });
+    if (res.code === 200) {
+      dictGroup.deviceOptions.push(...res.rows.map((item) => ({
+        ...item,
+        ext1: item.ext1 ? JSON.parse(item.ext1) : null
+      })))
+    }
+  })
+};
+const deviceTypeList = async () => {
+  const res = await listDeviceType({});
+  dictGroup.deviceTypeOptions = 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}`
+const getInspectionItem = () => {
+  listInspectionItem({}).then(({ code, rows }) => {
+    if (code === 200) {
+      dictGroup.inspectionItemOptions = rows
+    }
+  })
+}
+const inspectionTypeChange = () => {
+  if (form.value.inspectionType === '1') {
+    form.value.inspectionCycle = 1
+    cycleDisabled.value = true
+  } else {
+    cycleDisabled.value = false
+  }
+}
+const setUserInfo = () => {
+  form.value.ext1.phone = ''
+  form.value.ext1.inspectionPersonName = ''
+  const { inspectionPersonId } = form.value
+  userOptions.value.forEach(item => {
+    item.children.forEach(el => {
+      if (inspectionPersonId === el.value) {
+        form.value.ext1.phone = el.phone
+        form.value.ext1.inspectionPersonName = el.label
       }
+    })
+
   })
-  return label
+}
+const getInspectionTeamList = async () => {
+  const res = await listInspectionTeam({});
+  res.rows.forEach((item) => {
+    if (item.ext1) {
+      item.ext1 = JSON.parse(item.ext1)
+    }
+    if (item.members) {
+      item.members = JSON.parse(item.members)
+    }
+  });
+  userOptions.value = res.rows.map(item => ({
+    label: item.name,
+    value: item.id,
+    children: item.members.map(el => ({
+      label: el.userName,
+      value: el.userId,
+      phone:el.phone
+    }))
+  }))
+};
+const addInspectionItem = () => {
+  form.value.inspectionItem.push({
+    deviceTypeId: undefined,
+    deviceTypeDetailIds: [],
+    items: []
+  })
+}
+const delInspectionItem = (index) => {
+  form.value.inspectionItem.splice(index, 1)
 }
 onMounted(() => {
   getList();
-  deviceTypeDetailList()
+  deviceTypeList()
+  getInspectionItem()
+  getInspectionTeamList()
 });
 </script>
 <style lang="scss" scoped>
+.operateBtn {
+  display: flex;
+  align-items: center;
+
+  .el-icon {
+    color: #409eff;
+    margin-left: 5px;
+    cursor: pointer;
+  }
+}
 </style>

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

@@ -237,7 +237,6 @@ const bindDevice = (row) => {
       });
     }
   });
-
 }
 /** 修改按钮操作 */
 const handleUpdate = async (row) => {

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

@@ -106,7 +106,7 @@
           <el-col :span="12">
             <el-form-item label="设备类型" prop="devTypeDetailId"
               :rules="[{ required: true, message: '设备类型不能为空', trigger: 'change' }]">
-              <el-select v-model="form.devTypeDetailId" :disabled="ifEdit" clearable placeholder="请选择设备类型"
+              <el-select v-model="form.devTypeDetailId" :disabled="ifEdit"  clearable placeholder="请选择设备类型"
                 @change="devTypeDetailChange">
                 <el-option v-for="dict in dictGroup.deviceTypeDetailOptions" :key="dict.id"
                   :label="dict.deviceType.name" :value="dict.id">
@@ -408,6 +408,7 @@ const initFormData = {
   ext1: <any>{
     brand: '',
     xh: '',
+    deviceTypeId:'',
     longitude: '',
     latitude: '',
     address: ''
@@ -624,8 +625,9 @@ const getDeviceTypeList = async () => {
 };
 const devTypeDetailChange = (val) => {
   if (val) {
-    const [{ ext1: { brand }, xh, productorId, deviceType: { modeId } }] = dictGroup.deviceTypeDetailOptions.filter(item => item.id === val)
+    const [{ ext1: { brand }, xh, productorId,deviceTypeId, deviceType: { modeId } }] = dictGroup.deviceTypeDetailOptions.filter(item => item.id === val)
     form.value.ext1.brand = brand
+    form.value.ext1.deviceTypeId = deviceTypeId
     form.value.ext1.xh = xh
     form.value.productorId = productorId
     form.value.modelId = modeId

+ 392 - 0
src/views/projectManage/project/index.vue

@@ -0,0 +1,392 @@
+<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="projectSn">
+              <el-input v-model="queryParams.projectSn" 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="projectSn" width="150" />
+        <el-table-column label="项目名称" align="center" show-overflow-tooltip prop="name" width="150" />
+        <el-table-column label="项目负责人" align="center" show-overflow-tooltip prop="ext1.leaderName" width="150" />
+        <el-table-column label="联系电话" align="center" show-overflow-tooltip prop="ext1.phone" width="150" />
+        <el-table-column label="开始日期" align="center" show-overflow-tooltip prop="ext1.startDate" width="150" />
+        <el-table-column label="结束日期" align="center" show-overflow-tooltip prop="ext1.endDate" width="150" />
+        <el-table-column label="监理" align="center" show-overflow-tooltip prop="ext1.supervisor" width="150" />
+        <el-table-column label="设计单位" align="center" show-overflow-tooltip prop="ext1.designerCompany" width="150" />
+        <el-table-column label="承建单位" align="center" show-overflow-tooltip prop="ext1.buildCompany" width="150" />
+        <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="120px">
+        <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="projectSn" :rules="[{ required: true, message: '项目编号不能为空', trigger: 'blur' }]">
+              <el-input v-model="form.projectSn" 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 placeholder="根据项目负责人带出" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="开始时间" prop="ext1.startDate">
+              <el-date-picker v-model="form.ext1.startDate" style="width: 100%;" value-format="YYYY-MM-DD" type="date" placeholder="请选择开始时间" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="结束时间" prop="ext1.endDate">
+              <el-date-picker v-model="form.ext1.endDate" style="width: 100%;" value-format="YYYY-MM-DD" type="date" placeholder="请选择结束时间" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="监理单位" prop="ext1.supervisorCompany">
+              <el-input v-model="form.ext1.supervisorCompany" placeholder="请输入监理单位" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="监理姓名" prop="ext1.supervisor">
+              <el-input v-model="form.ext1.supervisor" placeholder="请输入监理姓名" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系电话" prop="ext1.supervisorPhone">
+              <el-input v-model="form.ext1.supervisorPhone" placeholder="请输入联系电话" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="设计单位" prop="ext1.designerCompany">
+              <el-input v-model="form.ext1.designerCompany" placeholder="请输入设计单位" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="设计单位负责人" prop="ext1.designer">
+              <el-input v-model="form.ext1.designer" placeholder="请输入设计单位负责人" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系电话" prop="ext1.designerPhone">
+              <el-input v-model="form.ext1.designerPhone" placeholder="请输入联系电话" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="承建单位" prop="ext1.buildCompany">
+              <el-input v-model="form.ext1.buildCompany" placeholder="请输入承建单位" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="施工承建负责人" prop="ext1.buildLeader">
+              <el-input v-model="form.ext1.buildLeader" placeholder="请输入施工承建负责人" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系电话" prop="ext1.buildLeaderPhone">
+              <el-input v-model="form.ext1.buildLeaderPhone" 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>
+    <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="Project" lang="ts">
+import { listProjectInfo, getProjectInfo, delProjectInfo, addProjectInfo, updateProjectInfo } from '@/api/projectManage/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 deviceTableRef = ref<ElTableInstance>();
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+const userList = ref([]);
+const deviceDialog = ref(false)
+const deviceList = ref([])
+const deviceSelectedIds = ref([])
+const initFormData = {
+  id: undefined,
+  name: undefined,
+  projectSn: undefined,
+  num: undefined,
+  leaderId: undefined,
+  files: undefined,
+  devices: undefined,
+  remark: undefined,
+  ext1: <any>{
+    leaderName:'',
+    phone: '',
+    startDate: '',
+    endDate: '',
+    supervisorCompany: '',
+    supervisor: '',
+    supervisorPhone: '',
+    designerCompany: '',
+    designer: '',
+    designerPhone: '',
+    buildCompany: '',
+    buildLeader: '',
+    buildLeaderPhone: '',
+  },
+  ext2: undefined
+};
+const formData = reactive({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    projectSn: undefined,
+    params: {}
+  }
+});
+
+const { queryParams, form } = toRefs(formData);
+const dictGroup = reactive({
+  deviceTypeDetailOptions: []
+})
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listProjectInfo(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 getProjectInfo(_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 updateProjectInfo(params).finally(() => (buttonLoading.value = false));
+      } else {
+        await addProjectInfo(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 delProjectInfo(_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 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 deviceSelectionChange = (selection) => {
+  deviceSelectedIds.value = selection.map((item) => item.id);
+}
+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 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 updateProjectInfo(params).finally(() => (buttonLoading.value = false));
+  proxy?.$modal.msgSuccess('操作成功');
+  deviceDialog.value = false;
+  await getList();
+}
+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></style>