yanghao 1 주 전
부모
커밋
430eb6456c
3개의 변경된 파일149개의 추가작업 그리고 28개의 파일을 삭제
  1. 1 1
      .env.local
  2. 5 0
      src/api/pms/qhse/index.ts
  3. 143 27
      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.79:48080  https://iot.deepoil.cc  http://172.26.0.56:48080
-VITE_BASE_URL='https://iot.deepoil.cc'
+VITE_BASE_URL='http://172.26.0.56:48080'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server

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

@@ -192,6 +192,11 @@ export const IotDangerApi = {
   // 导出危险源 Excel
   exportDanger: async (params) => {
     return await request.download({ url: `/rq/iot-danger-source/export-excel`, params })
+  },
+
+  // 统计
+  getDangerStatistics: async (id) => {
+    return await request.get({ url: `/rq/iot-danger-source/stat?deptId=${id}` })
   }
 }
 

+ 143 - 27
src/views/pms/qhse/hazard/index.vue

@@ -10,14 +10,12 @@
               v-model="queryParams.riskGrade"
               placeholder="请选择风险等级"
               clearable
-              style="width: 200px"
-            >
+              style="width: 200px">
               <el-option
                 v-for="dict in getStrDictOptions(DICT_TYPE.DANGER_GRADE)"
                 :key="dict.value"
                 :label="dict.label"
-                :value="dict.value"
-              />
+                :value="dict.value" />
             </el-select>
           </el-form-item>
 
@@ -39,14 +37,38 @@
       </ContentWrap>
 
       <ContentWrap style="border: 0">
+        <div v-if="staticData.length" class="stats-cards">
+          <!-- <div class="stats-card stats-card--total">
+            <div class="stats-card__label">风险总数</div>
+            <div class="stats-card__value">{{ totalRiskCount }}</div>
+          </div> -->
+          <div
+            v-for="item in staticData"
+            :key="item.classify"
+            class="stats-card"
+            :class="getStatsCardClass(item.classify)">
+            <div class="stats-card__label">
+              <!-- <span
+                :class="`w-50 h-50 rounded-full inline-block bg-[${getStatsCardClass(item.classify)}]`"></span> -->
+              <span>{{ item.classify }}</span>
+            </div>
+            <div class="stats-card__value">
+              <CountTo
+                :duration="2600"
+                :end-val="item.count"
+                :start-val="0"
+                :class="'stats-card__count'"
+                :style="{ color: getStatsCardClass(item.classify) }" />
+            </div>
+          </div>
+        </div>
         <zm-table
           :data="tableData"
           border
           style="width: 100%"
           :header-cell-style="{ background: '#f5f7fa', color: '#333' }"
           :cell-style="{ padding: '12px 8px' }"
-          height="70.5vh"
-        >
+          height="55vh">
           <!-- 区域/位置 列(已合并) -->
           <zm-table-column prop="region" label="区域/位置" align="center" fixed="left" />
 
@@ -87,8 +109,7 @@
             prop="controlMethod"
             label="控制措施"
             show-overflow-tooltip
-            align="center"
-          />
+            align="center" />
           <zm-table-column prop="charge" label="责任人" align="center" />
           <zm-table-column label="操作" width="150" align="center" fixed="right" action>
             <template #default="{ row }">
@@ -97,8 +118,7 @@
                   :underline="false"
                   size="small"
                   type="primary"
-                  @click="openForm('edit', row)"
-                >
+                  @click="openForm('edit', row)">
                   编辑
                 </el-link>
                 <el-link :underline="false" size="small" type="danger" @click="deleteRow(row)">
@@ -118,8 +138,7 @@
             layout="total, sizes, prev, pager, next, jumper"
             @size-change="handleSizeChange"
             @current-change="handleCurrentChange"
-            background
-          />
+            background />
         </div>
       </ContentWrap>
     </el-col>
@@ -150,8 +169,7 @@
               v-model="formData.maybeResult"
               placeholder="请输入可能导致的后果"
               type="textarea"
-              :rows="1"
-            />
+              :rows="1" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
@@ -159,8 +177,7 @@
             <el-input-number
               v-model="formData.evalKn"
               controls-position="right"
-              style="width: 100%"
-            />
+              style="width: 100%" />
           </el-form-item>
         </el-col>
       </el-row>
@@ -172,8 +189,7 @@
             <el-input-number
               v-model="formData.evalYz"
               controls-position="right"
-              style="width: 100%"
-            />
+              style="width: 100%" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
@@ -191,14 +207,12 @@
               v-model="formData.riskGrade"
               placeholder="请选择风险等级"
               clearable
-              style="width: 100%"
-            >
+              style="width: 100%">
               <el-option
                 v-for="dict in getStrDictOptions(DICT_TYPE.DANGER_GRADE)"
                 :key="dict.value"
                 :label="dict.label"
-                :value="dict.value"
-              />
+                :value="dict.value" />
             </el-select>
           </el-form-item>
         </el-col>
@@ -216,8 +230,7 @@
               v-model="formData.remark"
               type="textarea"
               placeholder="请输入备注"
-              :rows="1"
-            />
+              :rows="1" />
           </el-form-item>
         </el-col>
       </el-row>
@@ -230,8 +243,7 @@
               v-model="formData.controlMethod"
               type="textarea"
               :rows="4"
-              placeholder="请输入控制措施"
-            />
+              placeholder="请输入控制措施" />
           </el-form-item>
         </el-col>
       </el-row>
@@ -245,11 +257,13 @@
 </template>
 
 <script setup>
-import { ref, reactive, watch, onMounted } from 'vue'
+import { ref, reactive, watch, onMounted, computed } from 'vue'
 import { IotDangerApi } from '@/api/pms/qhse/index'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 import DeptTree from '@/views/system/user/HazardTree.vue'
+import { useUserStore } from '@/store/modules/user'
 
+const userStore = useUserStore()
 // 查询参数
 const queryParams = reactive({
   riskGrade: '',
@@ -300,12 +314,14 @@ watch(
 const handleQuery = () => {
   pagination.pageNo = 1 // 搜索后回到第一页
   loadTableData()
+  getStatic()
 }
 
 const handleDeptNodeClick = async (row) => {
   queryParams.deptId = row.id
   pagination.pageNo = 1
   loadTableData()
+  getStatic()
 }
 
 const downloadFile = (response) => {
@@ -361,6 +377,7 @@ const resetQuery = () => {
   queryParams.riskGrade = '' // 清空风险等级筛选
   pagination.pageNo = 1 // 重置为第一页
   loadTableData()
+  getStatic()
 }
 
 // 删除确认
@@ -514,9 +531,34 @@ const loadTableData = async () => {
   }
 }
 
+let staticData = ref([])
+const totalRiskCount = computed(() =>
+  staticData.value.reduce((sum, item) => sum + (Number(item.count) || 0), 0)
+)
+
+const getStatsCardClass = (classify) => {
+  const value = String(classify || '')
+  if (value.includes('重大')) return 'stats-card--major'
+  if (value.includes('较大')) return 'stats-card--high'
+  if (value.includes('一般')) return 'stats-card--medium'
+  if (value.includes('低')) return 'stats-card--low'
+  return 'stats-card--default'
+}
+
+async function getStatic() {
+  if (queryParams.deptId) {
+    const res = await IotDangerApi.getDangerStatistics(queryParams.deptId)
+    staticData.value = res.classify
+  } else {
+    const res = await IotDangerApi.getDangerStatistics(userStore.user.deptId)
+    staticData.value = res.classify
+  }
+}
+
 // 页面挂载后加载数据
 onMounted(() => {
   loadTableData()
+  getStatic()
 })
 </script>
 
@@ -568,4 +610,78 @@ onMounted(() => {
   padding: 6px 12px;
   border-radius: 4px;
 }
+
+.stats-cards {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+  gap: 12px;
+  margin-bottom: 16px;
+}
+
+.stats-card {
+  padding: 16px;
+  border-radius: 10px;
+  border: 1px solid #e4ecf7;
+
+  background: linear-gradient(180deg, rgb(212 228 252 / 42%) 0%, rgb(220 232 250 / 28%) 100%);
+  border: 1px solid rgb(255 255 255 / 58%);
+  border-radius: 18px;
+  box-shadow:
+    inset 0 1px 0 rgb(255 255 255 / 74%),
+    0 8px 18px rgb(63 103 171 / 7%);
+}
+
+.stats-card--total {
+  background: linear-gradient(180deg, #eff7ff 0%, #dfefff 100%);
+  border-color: #bfd8fb;
+}
+
+.stats-card__label {
+  font-size: 14px;
+  font-weight: 600;
+  color: #6b7280;
+}
+
+.stats-card__value {
+  margin-top: 10px;
+}
+
+.stats-card__count {
+  display: block;
+  padding-top: 10px;
+  text-align: center;
+  font-size: 40px !important;
+  font-weight: 700;
+  line-height: 1;
+}
+
+.stats-card--major .stats-card__label,
+.stats-card--major .stats-card__count {
+  color: #d03050;
+}
+
+.stats-card--high .stats-card__label,
+.stats-card--high .stats-card__count {
+  color: #dd6b20;
+}
+
+.stats-card--medium .stats-card__label,
+.stats-card--medium .stats-card__count {
+  color: #ebaa3c;
+}
+
+.stats-card--low .stats-card__label,
+.stats-card--low .stats-card__count {
+  color: #f2c11a;
+}
+
+.stats-card--default .stats-card__label,
+.stats-card--default .stats-card__count {
+  color: #475569;
+}
+
+.stats-card--total .stats-card__label,
+.stats-card--total .stats-card__value {
+  color: #1f5bb8;
+}
 </style>