yanghao před 1 dnem
rodič
revize
ef24fd45cb
3 změnil soubory, kde provedl 296 přidání a 151 odebrání
  1. 1 1
      .env.local
  2. 33 14
      src/views/pms/monitor/index.vue
  3. 262 136
      src/views/pms/qhse/hazard/index.vue

+ 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='http://172.26.0.56:48080'
+VITE_BASE_URL='https://iot.deepoil.cc'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server

+ 33 - 14
src/views/pms/monitor/index.vue

@@ -49,7 +49,7 @@
               :lg="6"
               class="mt-2"
             >
-              <el-card shadow="hover" class="device-card" style="border: 0">
+              <el-card shadow="hover" class="device-card hhh rounded-sm">
                 <div class="card-header">
                   <div class="flex justify-between w-full">
                     <div>
@@ -86,7 +86,9 @@
                   <!-- <div class="card-row"><span class="muted">描述:</span> {{ item.remark }}</div> -->
                 </div>
                 <template #footer>
-                  <el-button type="primary" link @click="handleEdit(item)"> 查看详情 </el-button>
+                  <el-button type="primary" link @click="handleEdit(item)"
+                    ><Icon icon="ep:view" class="mr-5px" /> 查看详情
+                  </el-button>
                 </template>
               </el-card>
             </el-col>
@@ -391,7 +393,17 @@ onMounted(async () => {
 })
 </script>
 
-<style scoped>
+<style scoped lang="scss">
+::v-deep .el-card {
+  border-radius: 5px !important;
+}
+
+::v-deep .hhh.el-card {
+  background-color: transparent !important;
+  border-radius: 5px !important;
+  overflow: hidden;
+  box-shadow: 0 6px 18px rgba(28, 39, 63, 0.06);
+}
 .transfer-container {
   display: flex;
   flex-direction: column;
@@ -429,11 +441,15 @@ onMounted(async () => {
   border-radius: 12px;
   overflow: hidden;
   box-shadow: 0 6px 18px rgba(28, 39, 63, 0.06);
+  /* 渐变蓝背景 */
+  background: linear-gradient(to top, #fff 0%, #effaff 100%);
+  color: #717e9d !important;
 }
 
 /* 移除 el-card 默认内边距,统一由子元素控制 */
 .device-card ::v-deep .el-card__body {
   padding: 0;
+  color: #717e9d !important;
 }
 
 .device-card .card-header {
@@ -441,20 +457,20 @@ onMounted(async () => {
   justify-content: space-between;
   align-items: center;
   padding: 18px 16px;
-  background: linear-gradient(90deg, #2dd4bf 0%, #0ea5e9 100%);
-  color: #fff;
 }
 
 .device-card .card-title {
   font-weight: 700;
   font-size: 16px;
   line-height: 1.2;
+  color: #34475c;
 }
 
 .device-card .card-dept {
   font-size: 12px;
-  opacity: 0.9;
+  opacity: 0.95;
   margin-top: 6px;
+  color: #34475c;
 }
 
 .device-card .card-status {
@@ -466,15 +482,16 @@ onMounted(async () => {
 }
 
 .device-card .card-body {
-  background: #fff;
+  background: transparent;
   padding: 14px 16px;
+  font-size: 14px;
+  color: #34475c;
 }
 
 .device-card .card-row {
   display: flex;
   align-items: center;
-  padding: 12px 0;
-  border-bottom: 1px solid #f5f7fa;
+  padding: 10px 0;
 }
 
 .device-card .card-row:last-child {
@@ -482,13 +499,13 @@ onMounted(async () => {
 }
 
 .device-card .card-row .muted {
-  color: #97a0b5;
+  color: #34475c;
   width: 86px;
   flex-shrink: 0;
 }
 
 .device-card .card-row .value {
-  color: #2b3a4a;
+  color: #fff;
   flex: 1;
 }
 
@@ -497,16 +514,18 @@ onMounted(async () => {
   justify-content: space-between;
   align-items: center;
   padding: 12px 16px 16px;
-  background: #fff;
+  background: transparent;
+  color: #fff;
 }
 
 .device-card .detail-link {
-  color: var(--el-color-primary, #409eff);
+  color: #fff;
   font-weight: 500;
 }
 
 .device-card .id-label {
-  color: #97a0b5;
+  color: rgba(255, 255, 255, 0.85);
+  font-size: 12px;
   font-size: 12px;
 }
 </style>

+ 262 - 136
src/views/pms/qhse/hazard/index.vue

@@ -3,30 +3,26 @@
     <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="请输入计量项目"
+        <el-form-item label="风险等级" prop="riskGrade">
+          <el-select
+            v-model="queryParams.riskGrade"
+            placeholder="请选择风险等级"
             clearable
-            @keyup.enter="handleQuery"
-            class="!w-150px"
-          />
+            style="width: 200px"
+          >
+            <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>
           <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')">
+          <el-button type="primary" @click="openForm('create')" color="#626aef">
             <Icon icon="ep:plus" class="mr-5px" /> 新增
           </el-button>
           <el-button type="success" plain @click="handleExport" :loading="exportLoading">
@@ -43,59 +39,76 @@
         style="width: 100%"
         :header-cell-style="{ background: '#f5f7fa', color: '#333' }"
         :cell-style="{ padding: '12px 8px' }"
+        height="70vh"
       >
-        <!-- 区域/位置 列 -->
-        <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>
+        <!-- 区域/位置 列(已合并) -->
+        <el-table-column prop="region" label="区域/位置" width="150" align="center" />
+
+        <!-- 其他列保持不变 -->
+        <el-table-column label="序号" width="70" align="center">
+          <template #default="scope">
+            {{ scope.$index + 1 }}
           </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 }}
+        <!-- 风险评价列保持不变 -->
+        <el-table-column label="风险评价" width="320" align="center">
+          <el-table-column prop="evalKn" label="可能性 (L)" width="80" align="center">
+            <template #default="{ row }">
+              {{ row.evalKn }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="evalYz" label="严重性 (S)" width="80" align="center">
+            <template #default="{ row }">
+              {{ row.evalYz }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="evalFxz" label="风险值 (R)" width="80" align="center">
+            <template #default="{ row }">
+              {{ row.evalFxz }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="riskGrade" label="风险等级" width="100" align="center">
+            <template #default="scope">
+              <div class="bg-[#ffff00] w-full rounded-md" v-if="scope.row.riskGrade === 'normal'">
+                一般风险
               </div>
-            </div>
-          </template>
+              <div class="bg-[#ffc000] w-full rounded-md" v-else-if="scope.row.riskGrade === 'big'">
+                较大风险
+              </div>
+              <div class="bg-[#0070c0] w-full text-white rounded-md" v-else>低风险</div>
+            </template>
+          </el-table-column>
         </el-table-column>
 
-        <!-- 控制措施 -->
-        <el-table-column prop="controlMethod" label="控制措施" align="center" />
-
-        <!-- 操作列 -->
+        <el-table-column
+          prop="controlMethod"
+          label="控制措施"
+          min-width="200"
+          show-overflow-tooltip
+          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 class="flex gap-3 justify-center">
+              <el-link
+                :underline="false"
+                size="small"
+                type="primary"
+                @click="openForm('edit', row)"
               >
+                编辑
+              </el-link>
+              <el-link :underline="false" size="small" type="danger" @click="deleteRow(row)">
+                删除
+              </el-link>
             </div>
           </template>
         </el-table-column>
@@ -119,67 +132,105 @@
     <!-- 新增/编辑弹窗 -->
     <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-row :gutter="20">
+          <!-- 第一行 -->
+          <el-col :span="12">
+            <el-form-item label="区域/位置" prop="region">
+              <el-input v-model="formData.region" placeholder="请输入区域/位置" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="危害因素描述" prop="elementDescription">
+              <el-input v-model="formData.elementDescription" placeholder="请输入危害因素描述" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <!-- 第二行 -->
+          <el-col :span="12">
+            <el-form-item label="可能导致的后果" prop="maybeResult">
+              <el-input
+                v-model="formData.maybeResult"
+                placeholder="请输入可能导致的后果"
+                type="textarea"
+                :rows="1"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="风险评价可能性" prop="evalKn">
+              <el-input-number
+                v-model="formData.evalKn"
+                controls-position="right"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <!-- 第三行 -->
+          <el-col :span="12">
+            <el-form-item label="风险评价严重性" prop="evalYz">
+              <el-input-number
+                v-model="formData.evalYz"
+                controls-position="right"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="风险评价风险值" prop="evalFxz">
+              <el-input-number v-model="formData.evalFxz" disabled style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <!-- 第四行 -->
+          <el-col :span="12">
+            <el-form-item label="风险等级" prop="riskGrade">
+              <el-select
+                v-model="formData.riskGrade"
+                placeholder="请选择风险等级"
+                clearable
+                style="width: 100%"
+              >
+                <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-col>
+          <el-col :span="12">
+            <el-form-item label="备注" prop="remark">
+              <el-input
+                v-model="formData.remark"
+                type="textarea"
+                placeholder="请输入备注"
+                :rows="1"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 控制措施单独一行(占满) -->
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="控制措施" prop="controlMethod">
+              <el-input
+                v-model="formData.controlMethod"
+                type="textarea"
+                :rows="4"
+                placeholder="请输入控制措施"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
 
       <template #footer>
@@ -197,8 +248,7 @@ import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 
 // 查询参数
 const queryParams = reactive({
-  measureName: '',
-  measureProject: ''
+  riskGrade: ''
 })
 
 // 表格数据
@@ -232,13 +282,6 @@ const rules = {
   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]) => {
@@ -250,7 +293,35 @@ watch(
 )
 // 搜索
 const handleQuery = () => {
-  console.log('搜索:', queryParams)
+  pagination.pageNo = 1 // 搜索后回到第一页
+  loadTableData()
+}
+
+// 重置查询
+const resetQuery = () => {
+  queryParams.riskGrade = '' // 清空风险等级筛选
+  pagination.pageNo = 1 // 重置为第一页
+  loadTableData()
+}
+
+// 删除确认
+const deleteRow = async (row) => {
+  try {
+    await ElMessageBox.confirm(`确认删除 ${row.elementDescription} 吗?`, '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning'
+    })
+
+    await IotDangerApi.deleteDanger(row.id)
+    ElMessage.success('删除成功')
+    loadTableData() // 重新加载数据
+  } catch (error) {
+    // 用户取消或删除失败
+    if (error !== 'cancel') {
+      ElMessage.error('删除失败')
+    }
+  }
 }
 
 // 每页数量变化
@@ -260,18 +331,53 @@ const handleSizeChange = (val) => {
   loadTableData()
 }
 
+// 预先计算合并信息
+const spanArr = ref([])
+const pos = ref(0)
+
+// 计算合并信息
+const getSpanArr = (data) => {
+  spanArr.value = []
+  pos.value = 0
+
+  data.forEach((item, index) => {
+    if (index === 0) {
+      spanArr.value.push(1)
+      pos.value = 0
+    } else {
+      if (data[index].region === data[index - 1].region) {
+        spanArr.value[pos.value] += 1
+        spanArr.value.push(0)
+      } else {
+        spanArr.value.push(1)
+        pos.value = index
+      }
+    }
+  })
+}
+
+// 行合并方法
+const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
+  if (columnIndex === 0) {
+    const _row = spanArr.value[rowIndex]
+    const _col = _row > 0 ? 1 : 0
+    return {
+      rowspan: _row,
+      colspan: _col
+    }
+  }
+  return {
+    rowspan: 1,
+    colspan: 1
+  }
+}
+
 // 当前页变化
 const handleCurrentChange = (val) => {
   pagination.pageNo = val
   loadTableData()
 }
 
-// 重置查询
-const resetQuery = () => {
-  queryParams.measureName = ''
-  queryParams.measureProject = ''
-}
-
 // 打开表单
 const openForm = (type, row = null) => {
   dialogTitle.value = type === 'create' ? '新增' : '编辑'
@@ -306,7 +412,8 @@ const submitForm = async () => {
       await IotDangerApi.createDanger(params)
       ElMessage.success('新增成功')
     } else {
-      await IotDangerApi.updateDanger(formData.id, params)
+      params.id = formData.id
+      await IotDangerApi.updateDanger(params)
       ElMessage.success('修改成功')
     }
 
@@ -326,12 +433,22 @@ const pagination = reactive({
 })
 const loadTableData = async () => {
   try {
-    const res = await IotDangerApi.getDangerList({
+    const params = {
       pageNo: pagination.pageNo,
-      pageSize: pagination.pageSize
-    })
+      pageSize: pagination.pageSize,
+      riskGrade: queryParams.riskGrade // 添加搜索参数
+    }
+    const res = await IotDangerApi.getDangerList(params)
     tableData.value = res.list || []
     total.value = res.total || 0
+
+    // // 按 region 排序(支持中文)
+    // tableData.value.sort((a, b) => {
+    //   return a.region.localeCompare(b.region, 'zh-CN')
+    // })
+
+    // 计算合并信息
+    getSpanArr(tableData.value)
   } catch (error) {
     console.error('加载失败:', error)
   }
@@ -343,9 +460,18 @@ onMounted(() => {
 })
 </script>
 
-<style scoped>
+<style scoped lang="scss">
+::v-deep .el-button {
+  border-radius: 0;
+}
+
+::v-deep .el-select__wrapper {
+  border-radius: 0 !important;
+  height: 26px;
+}
 .hazard-table-container {
   margin: 20px;
+  margin-top: 10px;
 }
 
 .area-header {