yanghao 1 өдөр өмнө
parent
commit
dd8961d4bc

+ 1 - 1
.env.local

@@ -4,7 +4,7 @@ NODE_ENV=development
 VITE_DEV=true
 
 # 请求路径  http://192.168.188.149:48080  https://iot.deepoil.cc  http://172.26.0.3:48080
-VITE_BASE_URL='https://iot.deepoil.cc'
+VITE_BASE_URL='http://172.26.0.56:48080'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server

+ 20 - 0
src/api/pms/qhse/index.ts

@@ -162,3 +162,23 @@ export const IotMeasureRecordApi = {
     return await request.download({ url: `/rq/iot-measure-record/export-excel`, params })
   }
 }
+
+// 危险源管理
+export const IotDangerApi = {
+  // 获得危险源分页
+  getDangerList: async (params) => {
+    return await request.get({ url: `/rq/iot-danger-source/page`, params })
+  },
+  // 删除危险源
+  deleteDanger: async (id) => {
+    return await request.delete({ url: `/rq/iot-danger-source/delete?id=` + id })
+  },
+  // 添加危险源
+  createDanger: async (data) => {
+    return await request.post({ url: `/rq/iot-danger-source/create`, data })
+  },
+  // 修改危险源
+  updateDanger: async (data) => {
+    return await request.put({ url: `/rq/iot-danger-source/update`, data })
+  }
+}

+ 7 - 1
src/utils/dict.ts

@@ -314,5 +314,11 @@ export enum DICT_TYPE {
 
   DEVICE_GROUP_TYPE = 'device_group_type',
   EVENT_TYPE = 'event_type',
-  EVENT_STATE = 'event_state'
+  EVENT_STATE = 'event_state',
+
+  // QHSE
+  MEASURE_TYPE = 'measure_type',
+  PERSON_CERT = 'person_cert',
+  ORG_CERT = 'org_cert',
+  DANGER_GRADE = 'danger_grade'
 }

+ 371 - 240
src/views/pms/iotopeationfill/index.vue

@@ -6,62 +6,70 @@
       </ContentWrap>
     </el-col>
     <el-col :span="20" :xs="24">
-    <ContentWrap>
-      <!-- 搜索工作栏 -->
-      <el-form
-        class="-mb-15px"
-        :model="queryParams"
-        ref="queryFormRef"
-        :inline="true"
-        label-width="68px"
-      >
-        <el-form-item :label="t('operationFill.name')" prop="orderName" style="margin-left: 15px">
-          <el-input
-            v-model="queryParams.orderName"
-            :placeholder="t('operationFill.nameHolder')"
-            clearable
-            @keyup.enter="handleQuery"
-            class="!w-240px"
-          />
-        </el-form-item>
-        <el-form-item :label="t('operationFill.status')" prop="orderStatus">
-          <el-select
-            v-model="queryParams.orderStatus"
-            :placeholder="t('operationFill.status')"
-            clearable
-            class="!w-240px"
+      <ContentWrap>
+        <!-- 搜索工作栏 -->
+        <el-form
+          class="-mb-15px"
+          :model="queryParams"
+          ref="queryFormRef"
+          :inline="true"
+          label-width="68px"
+        >
+          <el-form-item :label="t('operationFill.name')" prop="orderName" style="margin-left: 15px">
+            <el-input
+              v-model="queryParams.orderName"
+              :placeholder="t('operationFill.nameHolder')"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item :label="t('operationFill.status')" prop="orderStatus">
+            <el-select
+              v-model="queryParams.orderStatus"
+              :placeholder="t('operationFill.status')"
+              clearable
+              class="!w-240px"
+            >
+              <el-option
+                v-for="dict in getStrDictOptions(DICT_TYPE.OPERATION_FILL_ORDER_STATUS)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            :label="t('operationFill.createTime')"
+            prop="createTime"
+            label-width="100px"
           >
-            <el-option
-              v-for="dict in getStrDictOptions(DICT_TYPE.OPERATION_FILL_ORDER_STATUS)"
-              :key="dict.value"
-              :label="dict.label"
-              :value="dict.value"
+            <el-date-picker
+              v-model="queryParams.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              :start-placeholder="t('operationFill.start')"
+              :end-placeholder="t('operationFill.end')"
+              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+              class="!w-220px"
             />
-          </el-select>
-        </el-form-item>
-        <el-form-item :label="t('operationFill.createTime')" prop="createTime" label-width="100px">
-          <el-date-picker
-            v-model="queryParams.createTime"
-            value-format="YYYY-MM-DD HH:mm:ss"
-            type="daterange"
-            :start-placeholder="t('operationFill.start')"
-            :end-placeholder="t('operationFill.end')"
-            :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-            class="!w-220px"
-          />
-        </el-form-item>
-        <el-form-item>
-          <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />{{t('operationFill.search')}}</el-button>
-          <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />{{t('operationFill.reset')}}</el-button>
-<!--          <el-button-->
-<!--            type="primary"-->
-<!--            plain-->
-<!--            @click="openForm('create')"-->
-<!--            v-hasPermi="['rq:iot-inspect-order:create']"-->
-<!--          >-->
-<!--            <Icon icon="ep:plus" class="mr-5px" /> 新增-->
-<!--          </el-button>-->
-<!--          <el-button
+          </el-form-item>
+          <el-form-item>
+            <el-button @click="handleQuery"
+              ><Icon icon="ep:search" class="mr-5px" />{{ t('operationFill.search') }}</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" />{{ t('operationFill.reset') }}</el-button
+            >
+            <!--          <el-button-->
+            <!--            type="primary"-->
+            <!--            plain-->
+            <!--            @click="openForm('create')"-->
+            <!--            v-hasPermi="['rq:iot-inspect-order:create']"-->
+            <!--          >-->
+            <!--            <Icon icon="ep:plus" class="mr-5px" /> 新增-->
+            <!--          </el-button>-->
+            <!--          <el-button
             type="success"
             plain
             @click="handleExport"
@@ -70,79 +78,151 @@
           >
             <Icon icon="ep:download" class="mr-5px" /> 导出
           </el-button>-->
-        </el-form-item>
-      </el-form>
-    </ContentWrap>
-
-    <!-- 列表 -->
-    <ContentWrap>
-      <el-table v-loading="loading" :data="list" :stripe="true"  >
-        <el-table-column :label="t('common.index')" min-width="60" align="center">
-          <template #default="scope">
-            {{ scope.$index + 1 }}
-          </template>
-        </el-table-column>
-        <el-table-column :label="t('bomList.name')" align="center" prop="orderName" min-width="320"/>
-        <el-table-column :label="t('operationFill.duty')" align="center" prop="userName" min-width="100"/>
-        <el-table-column :label="t('operationFill.orderDevice')" align="center" prop="fillList" min-width="150" :show-overflow-tooltip="true"/>
-        <el-table-column :label="t('operationFill.status')" align="center" prop="orderStatus" min-width="120">
-          <template #default="scope">
-            <el-tooltip
-              v-if="scope.row.orderStatus === 3"
-              effect="dark"
-              :content="scope.row.reason"
-              placement="top"
-            >
-              <dict-tag :type="DICT_TYPE.OPERATION_FILL_ORDER_STATUS" :value="scope.row.orderStatus" />
-            </el-tooltip>
-            <dict-tag
-              v-else
-              :type="DICT_TYPE.OPERATION_FILL_ORDER_STATUS"
-              :value="scope.row.orderStatus"
-            />
-          </template>
-        </el-table-column>
-        <el-table-column :label="t('operationFill.deviceCount')" align="center" prop="allDev" min-width="120">
-          <template #default="scope">
-            <el-tag  type="info"> {{scope.row.allDev}}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column :label="t('operationFill.fillCount')" align="center" prop="fillDev" min-width="120">
-          <template #default="scope">
-            <el-tag  type="success"> {{scope.row.fillDev}}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column :label="t('operationFill.unFillCount')" align="center" prop="unFillDev" min-width="120">
-          <template #default="scope">
-            <el-tag  type="danger"> {{scope.row.unFillDev}}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column
-          :label="t('dict.createTime')"
-          align="center"
-          prop="createTime"
-          :formatter="dateFormatter"
-          min-width="170"
-        />
-        <el-table-column
-          :label="t('dict.fillTime')"
-          align="center"
-          prop="updateTime"
-          :formatter="dateFormatter"
-          min-width="170"
-        />
-        <el-table-column :label="t('operationFill.operation')" align="center" min-width="120px" fixed="right">
-          <template #default="scope">
+          </el-form-item>
+        </el-form>
+      </ContentWrap>
 
-              <div v-if="scope.row.orderStatus == 0||scope.row.orderStatus == 2">
+      <!-- 列表 -->
+      <ContentWrap>
+        <el-table v-loading="loading" :data="list" :stripe="true">
+          <el-table-column :label="t('common.index')" min-width="60" align="center">
+            <template #default="scope">
+              {{ scope.$index + 1 }}
+            </template>
+          </el-table-column>
+          <el-table-column
+            :label="t('bomList.name')"
+            align="center"
+            prop="orderName"
+            min-width="320"
+          />
+          <el-table-column
+            :label="t('operationFill.duty')"
+            align="center"
+            prop="userName"
+            min-width="100"
+          />
+          <el-table-column
+            :label="t('operationFill.orderDevice')"
+            align="center"
+            prop="fillList"
+            min-width="150"
+          >
+            <template #default="scope">
+              <el-popover
+                style="padding: 0"
+                placement="left"
+                :width="300"
+                :hide-after="0"
+                trigger="hover"
+                popper-class="project-popover"
+              >
+                <template #reference>
+                  <span class="cursor-pointer">{{ truncateText(scope.row.fillList, 20) }}</span>
+                </template>
+                <div class="scrollable-tooltip">
+                  <p>{{ scope.row.fillList }}</p>
+                </div>
+              </el-popover>
+            </template>
+          </el-table-column>
+          <el-table-column
+            :label="t('operationFill.status')"
+            align="center"
+            prop="orderStatus"
+            min-width="120"
+          >
+            <template #default="scope">
+              <el-tooltip
+                v-if="scope.row.orderStatus === 3"
+                effect="dark"
+                :content="scope.row.reason"
+                placement="top"
+              >
+                <dict-tag
+                  :type="DICT_TYPE.OPERATION_FILL_ORDER_STATUS"
+                  :value="scope.row.orderStatus"
+                />
+              </el-tooltip>
+              <dict-tag
+                v-else
+                :type="DICT_TYPE.OPERATION_FILL_ORDER_STATUS"
+                :value="scope.row.orderStatus"
+              />
+            </template>
+          </el-table-column>
+          <el-table-column
+            :label="t('operationFill.deviceCount')"
+            align="center"
+            prop="allDev"
+            min-width="120"
+          >
+            <template #default="scope">
+              <el-tag type="info"> {{ scope.row.allDev }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column
+            :label="t('operationFill.fillCount')"
+            align="center"
+            prop="fillDev"
+            min-width="120"
+          >
+            <template #default="scope">
+              <el-tag type="success"> {{ scope.row.fillDev }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column
+            :label="t('operationFill.unFillCount')"
+            align="center"
+            prop="unFillDev"
+            min-width="120"
+          >
+            <template #default="scope">
+              <el-tag type="danger"> {{ scope.row.unFillDev }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column
+            :label="t('dict.createTime')"
+            align="center"
+            prop="createTime"
+            :formatter="dateFormatter"
+            min-width="170"
+          />
+          <el-table-column
+            :label="t('dict.fillTime')"
+            align="center"
+            prop="updateTime"
+            :formatter="dateFormatter"
+            min-width="170"
+          />
+          <el-table-column
+            :label="t('operationFill.operation')"
+            align="center"
+            min-width="120px"
+            fixed="right"
+          >
+            <template #default="scope">
+              <div v-if="scope.row.orderStatus == 0 || scope.row.orderStatus == 2">
                 <el-button
                   link
                   type="primary"
-                  @click="openWrite(scope.row.deptId+','+scope.row.userId+','+scope.row.createTime+','+scope.row.id+','+scope.row.orderStatus)"
+                  @click="
+                    openWrite(
+                      scope.row.deptId +
+                        ',' +
+                        scope.row.userId +
+                        ',' +
+                        scope.row.createTime +
+                        ',' +
+                        scope.row.id +
+                        ',' +
+                        scope.row.orderStatus
+                    )
+                  "
                   v-hasPermi="['rq:iot-opeation-fill:update']"
                   v-if="scope.row.orderStatus !== 1"
                 >
-                  {{t('operationFill.fill')}}
+                  {{ t('operationFill.fill') }}
                 </el-button>
                 <el-button
                   link
@@ -151,82 +231,110 @@
                   v-if="scope.row.orderStatus !== 1"
                   @click="openDialog(scope.row.id)"
                 >
-                  {{t('operationFill.ignore')}}
+                  {{ t('operationFill.ignore') }}
                 </el-button>
               </div>
               <div v-else-if="scope.row.orderStatus === 3">
                 <el-button
                   link
                   type="success"
-                  @click="openWrite(scope.row.deptId+','+scope.row.userId+','+scope.row.createTime+','+scope.row.id+','+scope.row.orderStatus)"
+                  @click="
+                    openWrite(
+                      scope.row.deptId +
+                        ',' +
+                        scope.row.userId +
+                        ',' +
+                        scope.row.createTime +
+                        ',' +
+                        scope.row.id +
+                        ',' +
+                        scope.row.orderStatus
+                    )
+                  "
                 >
-                  {{t('operationFill.view')}}
+                  {{ t('operationFill.view') }}
                 </el-button>
               </div>
               <div v-else>
                 <el-button
                   link
                   type="primary"
-                  @click="openWrite(scope.row.deptId+','+scope.row.userId+','+scope.row.createTime+','+scope.row.id+','+0)"
+                  @click="
+                    openWrite(
+                      scope.row.deptId +
+                        ',' +
+                        scope.row.userId +
+                        ',' +
+                        scope.row.createTime +
+                        ',' +
+                        scope.row.id +
+                        ',' +
+                        0
+                    )
+                  "
                   v-hasPermi="['rq:iot-opeation-fill:update']"
                   v-if="isSameDay(scope.row.createTime)"
                 >
-                  {{t('fault.edit')}}
+                  {{ t('fault.edit') }}
                 </el-button>
                 <el-button
                   link
                   type="success"
-                  @click="openWrite(scope.row.deptId+','+scope.row.userId+','+scope.row.createTime+','+scope.row.id+','+scope.row.orderStatus)"
+                  @click="
+                    openWrite(
+                      scope.row.deptId +
+                        ',' +
+                        scope.row.userId +
+                        ',' +
+                        scope.row.createTime +
+                        ',' +
+                        scope.row.id +
+                        ',' +
+                        scope.row.orderStatus
+                    )
+                  "
                 >
-                  {{t('operationFill.view')}}
+                  {{ t('operationFill.view') }}
                 </el-button>
               </div>
 
-
-            <!-- 编辑按钮 -->
-
-
-          </template>
-        </el-table-column>
-      </el-table>
-      <el-dialog
-        v-model="dialogVisible"
-        title="忽略理由"
-        :width="600"
-        :before-close="handleClose"
-        append-to-body
-        :close-on-click-modal="false"
-      >
-        <el-form
-          ref="reasonFormRef"
-          :model="form"
-          :rules="rules"
-          label-width="60px"
+              <!-- 编辑按钮 -->
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-dialog
+          v-model="dialogVisible"
+          title="忽略理由"
+          :width="600"
+          :before-close="handleClose"
+          append-to-body
+          :close-on-click-modal="false"
         >
-          <el-form-item label="理由" prop="reason">
-            <el-input
-              type="textarea"
-              v-model="form.reason"
-              placeholder="请输入忽略理由"
-              :rows="4"
-              resize="none"
-            />
-          </el-form-item>
-        </el-form>
-
-        <template #footer>
-          <el-button @click="handleCancel">取消</el-button>
-          <el-button type="primary" @click="handleConfirm">确定</el-button>
-        </template>
-      </el-dialog>
-      <!-- 分页 -->
-      <Pagination
-        :total="total"
-        v-model:page="queryParams.pageNo"
-        v-model:limit="queryParams.pageSize"
-        @pagination="getList"
-      />
-    </ContentWrap>
+          <el-form ref="reasonFormRef" :model="form" :rules="rules" label-width="60px">
+            <el-form-item label="理由" prop="reason">
+              <el-input
+                type="textarea"
+                v-model="form.reason"
+                placeholder="请输入忽略理由"
+                :rows="4"
+                resize="none"
+              />
+            </el-form-item>
+          </el-form>
+
+          <template #footer>
+            <el-button @click="handleCancel">取消</el-button>
+            <el-button type="primary" @click="handleConfirm">确定</el-button>
+          </template>
+        </el-dialog>
+        <!-- 分页 -->
+        <Pagination
+          :total="total"
+          v-model:page="queryParams.pageNo"
+          v-model:limit="queryParams.pageSize"
+          @pagination="getList"
+        />
+      </ContentWrap>
     </el-col>
   </el-row>
   <!-- 表单弹窗:添加/修改 -->
@@ -234,25 +342,23 @@
 </template>
 
 <script setup lang="ts">
-
-
 import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotInspectOrderApi, IotInspectOrderVO } from '@/api/pms/inspect/order'
 //import IotInspectOrderForm from './IotInspectOrderForm.vue'
-import {DICT_TYPE, getStrDictOptions} from "@/utils/dict";
-import DeptTree from "@/views/system/user/DeptTree.vue";
-import {onMounted, ref} from "vue";
-import {IotOpeationFillApi, IotOpeationFillVO} from "@/api/pms/iotopeationfill";
-import {useUserStore} from "@/store/modules/user";
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import DeptTree from '@/views/system/user/DeptTree.vue'
+import { onMounted, ref } from 'vue'
+import { IotOpeationFillApi, IotOpeationFillVO } from '@/api/pms/iotopeationfill'
+import { useUserStore } from '@/store/modules/user'
 const { push } = useRouter()
-const { query} = useRoute() // 查询参数
-const deptId= query.deptId;
-const orderStatus = query.orderStatus;
-const createTime = query.createTime;
+const { query } = useRoute() // 查询参数
+const deptId = query.deptId
+const orderStatus = query.orderStatus
+const createTime = query.createTime
 /** 巡检工单 列表 */
 defineOptions({ name: 'IotOpeationFill1' })
-const dialogVisible = ref(false);
+const dialogVisible = ref(false)
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
 
@@ -269,15 +375,14 @@ const queryParams = reactive({
   createTime: [],
   deptId: useUserStore().getUser.deptId,
   deviceIds: undefined,
-  orderStatus:undefined
+  orderStatus: undefined
   //userId:useUserStore().getUser.id
 })
 
-
 const form = reactive({
   id: undefined,
-  reason: '',
-});
+  reason: ''
+})
 
 // 表单验证规则
 const rules = {
@@ -285,45 +390,47 @@ const rules = {
     { required: true, message: '请输入忽略理由', trigger: 'blur' },
     { min: 2, message: '理由长度不能少于2个字符', trigger: 'blur' }
   ]
-};
+}
 // 打开对话框
-const openDialog = (id:number) => {
-  dialogVisible.value = true;
-  form.id = id;
-  form.reason = '';
-};
+const openDialog = (id: number) => {
+  dialogVisible.value = true
+  form.id = id
+  form.reason = ''
+}
+
+const truncateText = (text: string, maxLength: number) => {
+  if (!text) return ''
+  return text.length > maxLength ? text.substring(0, maxLength) + '...' : text
+}
 // 取消按钮处理
 const handleCancel = () => {
-  dialogVisible.value = false;
-  resetForm();
-};
+  dialogVisible.value = false
+  resetForm()
+}
 
 // 确定按钮处理
 const handleConfirm = async () => {
   // 表单验证
   try {
-    await reasonFormRef.value.validate();
+    await reasonFormRef.value.validate()
     // 验证通过,调用接口
     await IotOpeationFillApi.updateIotOpeationFill1(form)
-    ElMessage.success('操作成功');
-    dialogVisible.value = false;
-    resetForm();
+    ElMessage.success('操作成功')
+    dialogVisible.value = false
+    resetForm()
   } catch (error) {
-    return;
+    return
   }
-};
+}
 // 重置表单
 const resetForm = () => {
-  reasonFormRef.value?.resetFields();
-};
-const reasonFormRef = ref(null);
-
-
+  reasonFormRef.value?.resetFields()
+}
+const reasonFormRef = ref(null)
 
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
 
-
 // 新增变量用于控制悬浮提示
 const hoverRowId = ref<number | null>(null)
 
@@ -347,8 +454,7 @@ const getTooltipContent = (row: IotOpeationFillVO) => {
   `
 }
 
-
- // 判断两个日期是否为同一天
+// 判断两个日期是否为同一天
 const isSameDay = (dateString) => {
   if (!dateString) return false
 
@@ -357,12 +463,13 @@ const isSameDay = (dateString) => {
   const today = new Date()
 
   // 比较年、月、日
-  return targetDate.getFullYear() === today.getFullYear() &&
+  return (
+    targetDate.getFullYear() === today.getFullYear() &&
     targetDate.getMonth() === today.getMonth() &&
     targetDate.getDate() === today.getDate()
+  )
 }
 
-
 const handleDeptNodeClick = async (row) => {
   queryParams.deptId = row.id
   await getList()
@@ -394,11 +501,11 @@ const resetQuery = () => {
 /** 添加/修改操作 */
 const formRef = ref()
 const openForm = (id?: number) => {
-  push({ name: 'InspectOrderDetail', params:{id} })
+  push({ name: 'InspectOrderDetail', params: { id } })
 }
 
 const openWrite = (id?: string) => {
-  push({ name: 'FillOrderInfo',params:{id}})
+  push({ name: 'FillOrderInfo', params: { id } })
 }
 /** 删除按钮操作 */
 const handleDelete = async (id: number) => {
@@ -430,49 +537,73 @@ const handleExport = async () => {
 
 /** 初始化 **/
 onMounted(async () => {
-
   // 计算近一周时间
-  const end = new Date();
-  const start = new Date();
-  start.setTime(start.getTime() - 7 * 24 * 60 * 60 * 1000);
+  const end = new Date()
+  const start = new Date()
+  start.setTime(start.getTime() - 7 * 24 * 60 * 60 * 1000)
 
   // 格式化日期为后端需要的格式
   const formatDate = (date) => {
-    const year = date.getFullYear();
-    const month = String(date.getMonth() + 1).padStart(2, '0');
-    const day = String(date.getDate()).padStart(2, '0');
-    const hours = String(date.getHours()).padStart(2, '0');
-    const minutes = String(date.getMinutes()).padStart(2, '0');
-    const seconds = String(date.getSeconds()).padStart(2, '0');
-    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
-  };
+    const year = date.getFullYear()
+    const month = String(date.getMonth() + 1).padStart(2, '0')
+    const day = String(date.getDate()).padStart(2, '0')
+    const hours = String(date.getHours()).padStart(2, '0')
+    const minutes = String(date.getMinutes()).padStart(2, '0')
+    const seconds = String(date.getSeconds()).padStart(2, '0')
+    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+  }
 
-  queryParams.createTime = [formatDate(start), formatDate(end)];
+  queryParams.createTime = [formatDate(start), formatDate(end)]
 
-  if(deptId != null){
-    queryParams.deptId = deptId;
+  if (deptId != null) {
+    queryParams.deptId = deptId
   }
-  if(orderStatus === 4){
+  if (orderStatus === 4) {
     queryParams.orderStatus = null
   }
-  if(orderStatus != null && orderStatus != 4){
-    queryParams.orderStatus = orderStatus;
+  if (orderStatus != null && orderStatus != 4) {
+    queryParams.orderStatus = orderStatus
   }
-  if(createTime){
-    const timeArr = createTime.split(',');
+  if (createTime) {
+    const timeArr = createTime.split(',')
     if (timeArr.length === 2) {
-      queryParams.createTime = timeArr;
+      queryParams.createTime = timeArr
     } else {
       // 处理格式不正确的情况,可以给个默认值或提示
-      console.warn('createTime参数格式不正确');
+      console.warn('createTime参数格式不正确')
     }
     //queryParams.createTime = createTime;
   }
 
-
-
-
-
   getList()
 })
 </script>
+
+<style lang="scss">
+.scrollable-tooltip {
+  min-height: 100px;
+  overflow-y: auto;
+  overflow-x: hidden;
+  /* white-space: pre-wrap;
+  word-break: break-word; */
+  background-color: rgba(0, 0, 0, 0.8);
+  color: #fff;
+  font-size: 14px;
+}
+.project-popover.el-popover.el-popper {
+  opacity: 0.8;
+  background: #000000;
+  border: none !important;
+  font-size: 14px;
+  color: #ffffff;
+  font-weight: 400;
+  min-width: 54px;
+
+  .el-popper__arrow::before {
+    background: #000000;
+    border: none;
+  }
+}
+</style>
+
+<style scoped lang="scss"></style>

+ 112 - 38
src/views/pms/qhse/certificate.vue

@@ -15,21 +15,24 @@
             <el-select v-model="queryParams.type" placeholder="请选择证书类型" style="width: 150px">
               <el-option label="个人证书" value="personal" />
               <el-option label="组织证书" value="organization" />
-              <el-option label="其他" value="other" />
             </el-select>
           </el-form-item>
 
           <el-form-item label="证书类别" prop="classify">
             <el-select
               v-model="queryParams.classify"
-              placeholder="请选择证书类别"
-              style="width: 150px"
+              placeholder="证书类别"
+              clearable
+              class="!w-240px"
             >
-              <el-option label="职业资格证" value="professional" />
-              <el-option label="技能证书" value="skill" />
-              <el-option label="学历证书" value="education" />
-              <el-option label="荣誉证书" value="honor" />
-              <el-option label="其他" value="other" />
+              <el-option
+                v-for="dict in getStrDictOptions(DICT_TYPE.PERSON_CERT).concat(
+                  getStrDictOptions(DICT_TYPE.ORG_CERT)
+                )"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
             </el-select>
           </el-form-item>
 
@@ -43,7 +46,7 @@
             <el-button @click="resetQuery"
               ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('devicePerson.reset') }}</el-button
             >
-            <el-button @click="handleExport" type="success" plain
+            <el-button @click="handleExport" type="success" plain :loading="exportLoading"
               ><Icon icon="ep:download" class="mr-5px" /> 导出Excel</el-button
             >
           </el-form-item>
@@ -65,9 +68,14 @@
             </template>
           </el-table-column>
 
-          <el-table-column label="证书类别" align="center" prop="classify">
+          <el-table-column label="证书类别" align="center" width="150" prop="classify">
             <template #default="scope">
-              {{ getCertificateCategoryText(scope.row.classify) }}
+              <dict-tag
+                v-if="scope.row.type === 'organization'"
+                :type="DICT_TYPE.ORG_CERT"
+                :value="scope.row.classify"
+              />
+              <dict-tag v-else :type="DICT_TYPE.PERSON_CERT" :value="scope.row.classify" />
             </template>
           </el-table-column>
 
@@ -137,25 +145,64 @@
       v-loading="formLoading"
     >
       <el-form-item label="证书类型" prop="type">
-        <el-select v-model="formData.type" placeholder="请选择证书类型">
+        <el-select
+          v-model="formData.type"
+          placeholder="请选择证书类型"
+          @change="formData.classify = ''"
+        >
           <el-option label="个人证书" value="personal" />
           <el-option label="组织证书" value="organization" />
-          <el-option label="其他" value="other" />
         </el-select>
       </el-form-item>
 
       <el-form-item label="证书类别" prop="classify">
-        <el-select v-model="formData.classify" placeholder="请选择证书类别">
-          <el-option label="职业资格证" value="professional" />
-          <el-option label="技能证书" value="skill" />
-          <el-option label="学历证书" value="education" />
-          <el-option label="荣誉证书" value="honor" />
-          <el-option label="其他" value="other" />
+        <el-select
+          v-if="formData.type === 'personal'"
+          v-model="formData.classify"
+          placeholder="证书类别"
+          clearable
+        >
+          <el-option
+            v-for="dict in getStrDictOptions(DICT_TYPE.PERSON_CERT)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+        <el-select v-else v-model="formData.classify" placeholder="证书类别" clearable>
+          <el-option
+            v-for="dict in getStrDictOptions(DICT_TYPE.ORG_CERT)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
         </el-select>
       </el-form-item>
 
-      <el-form-item label="所属公司/人" prop="certBelong">
-        <el-input v-model="formData.certBelong" placeholder="请输入所属公司或人员" />
+      <el-form-item label="所在部门" prop="deptId">
+        <el-tree-select
+          clearable
+          v-model="formData.deptId"
+          :data="deptList2"
+          :props="defaultProps"
+          check-strictly
+          node-key="id"
+          filterable
+          placeholder="请选择所在部门"
+          @change="handleDeptChange"
+        />
+      </el-form-item>
+
+      <span class="absolute left-19 text-red" v-if="formData.type === 'personal'">*</span>
+      <el-form-item label="所属人" prop="userId">
+        <el-select v-model="formData.userId" placeholder="请选择所属人" clearable>
+          <el-option
+            v-for="dict in userList"
+            :key="dict.id"
+            :label="dict.nickname"
+            :value="dict.id"
+          />
+        </el-select>
       </el-form-item>
 
       <el-form-item label="颁发机构" prop="certOrg">
@@ -233,6 +280,9 @@ const deptList = ref<Tree[]>([]) // 树形结构
 const deptList2 = ref<Tree[]>([]) // 树形结构
 import { formatDate } from '@/utils/formatTime'
 import UploadImage from '@/components/UploadFile/src/UploadImg.vue'
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { defaultProps } from '@/utils/tree'
+import { selectedDeptsEmployee } from '@/api/system/user'
 
 defineOptions({ name: 'IotQHSECertificate' })
 
@@ -272,7 +322,8 @@ const formRef = ref()
 const formData = ref({
   type: '', // 证书类型
   classify: '', // 证书类别
-  certBelong: '', // 证书所属公司/个人
+  userId: '',
+
   certOrg: '', // 证书颁发机构
   certStandard: '', // 证书标准
   certIssue: '', // 证书颁发时间
@@ -293,18 +344,6 @@ const getCertificateTypeText = (type: string) => {
   return map[type] || type
 }
 
-// 获取证书类别文本
-const getCertificateCategoryText = (category: string) => {
-  const map: Record<string, string> = {
-    professional: '职业资格证',
-    skill: '技能证书',
-    education: '学历证书',
-    honor: '荣誉证书',
-    other: '其他'
-  }
-  return map[category] || category
-}
-
 // 正确格式化日期的函数
 const formatDateCorrectly = (timestamp) => {
   if (!timestamp) return ''
@@ -323,7 +362,30 @@ const formatDateCorrectly = (timestamp) => {
 const formRules = {
   type: [{ required: true, message: '证书类型不能为空', trigger: 'blur' }],
   classify: [{ required: true, message: '证书类别不能为空', trigger: 'blur' }],
-  certBelong: [{ required: true, message: '所属公司/人不能为空', trigger: 'blur' }],
+  deptId: [{ required: true, message: '所在部门不能为空', trigger: 'blur' }],
+  userId: [
+    {
+      required: false, // 默认不强制验证
+      validator: (rule, value, callback) => {
+        // 只有当证书类型为个人且证书类别为个人证书时才验证
+        if (
+          formData.value.type === 'personal' &&
+          getStrDictOptions(DICT_TYPE.PERSON_CERT).some(
+            (personCert) => personCert.value === formData.value.classify
+          )
+        ) {
+          if (!value) {
+            callback(new Error('个人证书必须选择所属人'))
+          } else {
+            callback()
+          }
+        } else {
+          callback() // 不需要验证时直接通过
+        }
+      },
+      trigger: ['blur', 'change']
+    }
+  ],
   certOrg: [{ required: true, message: '颁发机构不能为空', trigger: 'blur' }],
   certIssue: [{ required: true, message: '颁发时间不能为空', trigger: 'blur' }],
   certExpire: [{ required: true, message: '有效期不能为空', trigger: 'blur' }]
@@ -343,8 +405,10 @@ const getList = async () => {
 
 const handleExport = async () => {
   try {
+    exportLoading.value = true
     const response = await IotMeasureCertApi.exportIotMeasureCert(queryParams)
     downloadFile(response)
+    exportLoading.value = false
   } catch (error) {
     ElMessage.error('导出失败,请重试')
     console.error('导出错误:', error)
@@ -437,8 +501,7 @@ const handleDelete = async (id: number) => {
 const resetForm = () => {
   formData.value = {
     type: '', // 证书类型
-    classify: '', // 证书类别
-    certBelong: '', // 证书所属公司/个人
+    classify: '',
     certOrg: '', // 证书颁发机构
     certStandard: '', // 证书标准
     certIssue: '', // 证书颁发时间
@@ -446,7 +509,8 @@ const resetForm = () => {
     noticeBefore: '', // 到期前提醒
     certPic: '', // 证书图片上传
     remark: '', // 备注
-    deptId: '' // 部门id
+    deptId: '', // 部门id
+    userId: ''
   }
   formRef.value?.clearValidate()
 }
@@ -520,6 +584,16 @@ const downloadFile = (response: any) => {
   window.URL.revokeObjectURL(url)
 }
 
+let userList = ref([])
+const handleDeptChange = async (value) => {
+  const res = await selectedDeptsEmployee({
+    deptIds: value
+  })
+
+  userList.value = res
+  console.log('value>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>', userList.value)
+}
+
 onMounted(async () => {
   getList()
 

+ 385 - 0
src/views/pms/qhse/hazard/index.vue

@@ -0,0 +1,385 @@
+<template>
+  <div class="hazard-table-container">
+    <ContentWrap style="border: 0">
+      <!-- 搜索工作栏 -->
+      <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
+        <el-form-item label="计量器具名称" prop="measureName">
+          <el-input
+            v-model="queryParams.measureName"
+            placeholder="请输入计量器具名称"
+            clearable
+            @keyup.enter="handleQuery"
+            class="!w-150px"
+          />
+        </el-form-item>
+
+        <el-form-item label="计量项目" prop="measureProject">
+          <el-input
+            v-model="queryParams.measureProject"
+            placeholder="请输入计量项目"
+            clearable
+            @keyup.enter="handleQuery"
+            class="!w-150px"
+          />
+        </el-form-item>
+
+        <el-form-item>
+          <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+          <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+          <el-button type="primary" plain @click="openForm('create')">
+            <Icon icon="ep:plus" class="mr-5px" /> 新增
+          </el-button>
+          <el-button type="success" plain @click="handleExport" :loading="exportLoading">
+            <Icon icon="ep:download" class="mr-5px" /> 导出
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </ContentWrap>
+
+    <ContentWrap style="border: 0">
+      <el-table
+        :data="tableData"
+        border
+        style="width: 100%"
+        :header-cell-style="{ background: '#f5f7fa', color: '#333' }"
+        :cell-style="{ padding: '12px 8px' }"
+      >
+        <!-- 区域/位置 列 -->
+        <el-table-column prop="area" label="区域/位置" width="150" align="center">
+          <template #default="{ row }">
+            <div v-if="row.area && !row.subRow" class="area-header">{{ row.area }}</div>
+            <div v-else class="sub-row">{{ row.subRow }}</div>
+          </template>
+        </el-table-column>
+
+        <!-- 序号 -->
+        <el-table-column prop="index" label="序号" width="60" align="center" />
+
+        <!-- 危害因素描述 -->
+        <el-table-column
+          prop="elementDescription"
+          label="危害因素描述"
+          width="200"
+          align="center"
+        />
+
+        <!-- 可导致的后果 -->
+        <el-table-column prop="maybeResult" label="可导致的后果" align="center" />
+
+        <!-- 风险评价(多列) -->
+        <el-table-column label="风险评价" width="240" align="center">
+          <template #default="{ row }">
+            <div class="risk-evaluation">
+              <div class="risk-item">可能性 (L)</div>
+              <div class="risk-item">严重性 (S)</div>
+              <div class="risk-item">风险值 (R)</div>
+              <div
+                class="risk-item risk-level"
+                :style="{ backgroundColor: getRiskColor(row.riskLevel) }"
+              >
+                {{ row.riskLevel }}
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+
+        <!-- 控制措施 -->
+        <el-table-column prop="controlMethod" label="控制措施" align="center" />
+
+        <!-- 操作列 -->
+        <el-table-column label="操作" width="150" align="center">
+          <template #default="{ row }">
+            <div class="flex gap-2">
+              <el-link :underline="false" size="small" type="primary" @click="openForm('edit', row)"
+                >编辑</el-link
+              >
+              <el-link :underline="false" size="small" type="danger" @click="openForm('edit', row)"
+                >删除</el-link
+              >
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <div class="mt-2 flex justify-right">
+        <el-pagination
+          v-model:current-page="pagination.pageNo"
+          v-model:page-size="pagination.pageSize"
+          :total="total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          background
+        />
+      </div>
+    </ContentWrap>
+
+    <!-- 新增/编辑弹窗 -->
+    <!-- 新增/编辑弹窗 -->
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" @close="resetForm">
+      <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
+        <!-- 区域/位置 -->
+        <el-form-item label="区域/位置" prop="region">
+          <el-input v-model="formData.region" placeholder="请输入区域/位置" />
+        </el-form-item>
+
+        <!-- 危害因素描述 -->
+        <el-form-item label="危害因素描述" prop="elementDescription">
+          <el-input v-model="formData.elementDescription" placeholder="请输入危害因素描述" />
+        </el-form-item>
+
+        <!-- 可能导致的后果 -->
+        <el-form-item label="可能导致的后果" prop="maybeResult">
+          <el-input v-model="formData.maybeResult" placeholder="请输入可能导致的后果" />
+        </el-form-item>
+
+        <!-- 风险评价可能性 (L) -->
+        <el-form-item label="风险评价可能性" prop="evalKn">
+          <el-input-number v-model="formData.evalKn" controls-position="right" />
+        </el-form-item>
+
+        <!-- 风险评价严重性 (S) -->
+        <el-form-item label="风险评价严重性" prop="evalYz">
+          <el-input-number v-model="formData.evalYz" controls-position="right" />
+        </el-form-item>
+
+        <!-- 风险评价风险值 (R) -->
+        <el-form-item label="风险评价风险值" prop="evalFxz">
+          <el-input-number v-model="formData.evalFxz" disabled />
+        </el-form-item>
+
+        <!-- 风险等级 -->
+        <el-form-item label="风险等级" prop="riskGrade">
+          <!-- <el-select v-model="formData.riskGrade" placeholder="请选择风险等级">
+            <el-option label="一般风险" value="一般风险" />
+            <el-option label="低风险" value="低风险" />
+          </el-select> -->
+
+          <el-select v-model="formData.riskGrade" placeholder="请选择风险等级" clearable>
+            <el-option
+              v-for="dict in getStrDictOptions(DICT_TYPE.DANGER_GRADE)"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value"
+            />
+          </el-select>
+        </el-form-item>
+
+        <!-- 控制措施 -->
+        <el-form-item label="控制措施" prop="controlMethod">
+          <el-input
+            v-model="formData.controlMethod"
+            type="textarea"
+            :rows="3"
+            placeholder="请输入控制措施"
+          />
+        </el-form-item>
+
+        <!-- 备注 -->
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="formData.remark" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="submitForm">确定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, watch, onMounted } from 'vue'
+import { IotDangerApi } from '@/api/pms/qhse/index'
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+
+// 查询参数
+const queryParams = reactive({
+  measureName: '',
+  measureProject: ''
+})
+
+// 表格数据
+const tableData = ref([])
+
+// 弹窗控制
+const dialogVisible = ref(false)
+const dialogTitle = ref('新增')
+const formData = reactive({
+  region: '', // 区域/位置
+
+  elementDescription: '', // 危害因素描述
+  maybeResult: '', // 可能导致的后果
+  evalKn: 1, // 可能性
+  evalYz: 1, // 严重性
+  evalFxz: 1, // 风险值(自动计算)
+  riskGrade: '一般风险', // 风险等级
+  controlMethod: '', // 控制措施
+  remark: '' // 备注
+})
+
+// 表单校验规则
+const rules = {
+  region: [{ required: true, message: '请输入区域/位置', trigger: 'blur' }],
+
+  elementDescription: [{ required: true, message: '请输入危害因素描述', trigger: 'blur' }],
+  maybeResult: [{ required: true, message: '请输入可能导致的后果', trigger: 'blur' }],
+  evalKn: [{ required: true, message: '请输入风险评价可能性', trigger: 'change' }],
+  evalYz: [{ required: true, message: '请输入风险评价严重性', trigger: 'change' }],
+  riskGrade: [{ required: true, message: '请选择风险等级', trigger: 'change' }],
+  controlMethod: [{ required: true, message: '请输入控制措施', trigger: 'blur' }]
+}
+
+// 获取风险等级颜色
+const getRiskColor = (level) => {
+  if (level === '一般风险') return '#fff9c4'
+  if (level === '低风险') return '#e3f2fd'
+  return '#ffffff'
+}
+
+watch(
+  () => [formData.evalKn, formData.evalYz],
+  ([kn, yz]) => {
+    if (kn && yz) {
+      formData.evalFxz = kn * yz
+    }
+  },
+  { immediate: true }
+)
+// 搜索
+const handleQuery = () => {
+  console.log('搜索:', queryParams)
+}
+
+// 每页数量变化
+const handleSizeChange = (val) => {
+  pagination.pageSize = val
+  pagination.pageNo = 1 // 重置为第一页
+  loadTableData()
+}
+
+// 当前页变化
+const handleCurrentChange = (val) => {
+  pagination.pageNo = val
+  loadTableData()
+}
+
+// 重置查询
+const resetQuery = () => {
+  queryParams.measureName = ''
+  queryParams.measureProject = ''
+}
+
+// 打开表单
+const openForm = (type, row = null) => {
+  dialogTitle.value = type === 'create' ? '新增' : '编辑'
+  if (type === 'edit') {
+    Object.assign(formData, row)
+    // 计算风险值 R = L × S
+    formData.riskValue = formData.possibility * formData.severity
+  } else {
+    resetForm()
+  }
+  dialogVisible.value = true
+}
+
+// 重置表单
+const resetForm = () => {
+  Object.keys(formData).forEach((key) => {
+    formData[key] = ''
+  })
+}
+
+// 提交表单
+const formRef = ref(null)
+const submitForm = async () => {
+  await formRef.value.validate()
+  try {
+    const params = {
+      ...formData,
+      evalFxz: formData.evalFxz // 使用已计算的值
+    }
+
+    if (dialogTitle.value === '新增') {
+      await IotDangerApi.createDanger(params)
+      ElMessage.success('新增成功')
+    } else {
+      await IotDangerApi.updateDanger(formData.id, params)
+      ElMessage.success('修改成功')
+    }
+
+    loadTableData()
+    dialogVisible.value = false
+  } catch (error) {
+    ElMessage.error('提交失败')
+  }
+}
+
+// 加载数据
+const exportLoading = ref(false)
+let total = ref(0)
+const pagination = reactive({
+  pageNo: 1,
+  pageSize: 10
+})
+const loadTableData = async () => {
+  try {
+    const res = await IotDangerApi.getDangerList({
+      pageNo: pagination.pageNo,
+      pageSize: pagination.pageSize
+    })
+    tableData.value = res.list || []
+    total.value = res.total || 0
+  } catch (error) {
+    console.error('加载失败:', error)
+  }
+}
+
+// 页面挂载后加载数据
+onMounted(() => {
+  loadTableData()
+})
+</script>
+
+<style scoped>
+.hazard-table-container {
+  margin: 20px;
+}
+
+.area-header {
+  font-weight: bold;
+  text-align: center;
+  padding: 12px 0;
+  background-color: #f5f7fa;
+  border-bottom: 1px solid #ddd;
+}
+
+.sub-row {
+  padding: 12px 0;
+  text-align: left;
+}
+
+.risk-evaluation {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+  align-items: center;
+}
+
+.risk-item {
+  font-size: 12px;
+  padding: 4px 8px;
+  border-radius: 4px;
+  background-color: #f5f7fa;
+  color: #333;
+}
+
+.risk-level {
+  font-weight: bold;
+  color: #333;
+  padding: 6px 12px;
+  border-radius: 4px;
+}
+</style>

+ 35 - 6
src/views/pms/qhse/index.vue

@@ -57,7 +57,11 @@
           <el-table-column label="责任人" align="center" prop="dutyPerson" />
           <el-table-column label="品牌" align="center" prop="brand" />
           <el-table-column label="规格型号" align="center" prop="modelName" />
-          <el-table-column label="分类" align="center" prop="classify" />
+          <el-table-column label="分类" align="center" prop="classify" width="150">
+            <template #default="scope">
+              <dict-tag :type="DICT_TYPE.MEASURE_TYPE" :value="scope.row.classify" />
+            </template>
+          </el-table-column>
           <el-table-column label="采购日期" align="center" prop="buyDate">
             <template #default="scope">
               {{ formatDateCorrectly(scope.row.buyDate) }}
@@ -169,7 +173,19 @@
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="分类" prop="classify">
-            <el-input v-model="formData.classify" placeholder="请输入分类" />
+            <el-select
+              v-model="formData.classify"
+              placeholder="请选择平台"
+              clearable
+              class="!w-240px"
+            >
+              <el-option
+                v-for="dict in getStrDictOptions(DICT_TYPE.MEASURE_TYPE)"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="12">
@@ -222,6 +238,14 @@
             />
           </el-form-item>
         </el-col>
+        <el-col :span="12">
+          <el-form-item label="证书编码" prop="measureCertNo">
+            <el-input v-model="formData.measureCertNo" placeholder="请输入证书编码" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="备注" prop="remark">
             <el-input v-model="formData.remark" type="textarea" placeholder="请输入描述" />
@@ -247,6 +271,7 @@ import { ElMessageBox } from 'element-plus'
 const deptList = ref<Tree[]>([]) // 树形结构
 const deptList2 = ref<Tree[]>([]) // 树形结构
 import { formatDate } from '@/utils/formatTime'
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 
 defineOptions({ name: 'IotQHSEMeasure' })
 
@@ -290,7 +315,8 @@ const formData = ref({
   measureCode: '',
   validity: null, // 有效期
   lastTime: null, // 上次检验/校准日期
-  measurePrice: 0 // 价格
+  measurePrice: 0, // 价格
+  measureCertNo: ''
 })
 
 // 正确格式化日期的函数
@@ -311,8 +337,10 @@ const formatDateCorrectly = (timestamp) => {
 const formRules = {
   measureName: [{ required: true, message: '计量器具名称不能为空', trigger: 'blur' }],
   dutyPerson: [{ required: true, message: '责任人不能为空', trigger: 'blur' }],
-  classify: [{ required: true, message: '分类不能为空', trigger: 'blur' }]
-  // measureCode: [{ required: true, message: '编码不能为空', trigger: 'blur' }]
+  classify: [{ required: true, message: '分类不能为空', trigger: 'blur' }],
+  deptId: [{ required: true, message: '部门不能为空', trigger: 'blur' }],
+  measureCertNo: [{ required: true, message: '证书编码不能为空', trigger: 'blur' }],
+  validity: [{ required: true, message: '有效期不能为空', trigger: 'blur' }]
 }
 
 /** 查询列表 */
@@ -463,7 +491,8 @@ const resetForm = () => {
     measureCode: '',
     validity: null, // 有效期
     lastTime: null, // 上次检验/校准日期
-    measurePrice: 0 // 价格
+    measurePrice: 0, // 价格
+    measureCertNo: ''
   }
   formRef.value?.clearValidate()
 }