gao 1 hari lalu
induk
melakukan
c7c5335ad6
3 mengubah file dengan 214 tambahan dan 148 penghapusan
  1. 6 1
      package-lock.json
  2. 87 75
      src/views/safety/danger_view/index.vue
  3. 121 72
      src/views/safety/safetyManage/index.vue

+ 6 - 1
package-lock.json

@@ -2790,7 +2790,7 @@
     },
     "axios": {
       "version": "0.21.0",
-      "resolved": "https://registry.npmmirror.com/axios/-/axios-0.21.0.tgz",
+      "resolved": "https://mirrors.huaweicloud.com/repository/npm/axios/-/axios-0.21.0.tgz",
       "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==",
       "requires": {
         "follow-redirects": "^1.10.0"
@@ -4912,6 +4912,11 @@
         "is-data-view": "^1.0.1"
       }
     },
+    "dayjs": {
+      "version": "1.11.18",
+      "resolved": "https://mirrors.huaweicloud.com/repository/npm/dayjs/-/dayjs-1.11.18.tgz",
+      "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA=="
+    },
     "de-indent": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",

+ 87 - 75
src/views/safety/danger_view/index.vue

@@ -31,98 +31,110 @@
       </div>
       <el-table :data="topList" border style="width: 100%;" fit>
         <el-table-column prop="id" label="编号" width="80" />
-        <el-table-column prop="problem" label="问题描述" />
-        <el-table-column prop="date" label="发现时间" width="180" />
-        <el-table-column prop="status" label="状态" width="120" />
+        <el-table-column prop="description" label="问题描述" />
+        <el-table-column prop="foundAt" label="发现时间" width="180">
+          <template slot-scope="scope">
+            {{ formatDateTime(scope.row.foundAt) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="120">
+          <template slot-scope="scope">
+            <el-tag :type="scope.row.status === 'PENDING' ? 'danger' : 'success'">
+              {{ scope.row.status === 'PENDING' ? '未整改' : '已整改' }}
+            </el-tag>
+          </template>
+        </el-table-column>
       </el-table>
     </el-card>
   </div>
 </template>
 
 <script>
-import * as echarts from "echarts";
+import * as echarts from 'echarts'
+import { fetchDashboard } from '@/api/safety/safety'
 
 export default {
-  name: "RiskDashboard",
+  name: 'RiskDashboard',
   data() {
     return {
-      stats: {
-        high: 3,
-        medium: 5,
-        low: 7,
-      },
-      topList: [
-        { id: 1, problem: "电线老化存在安全隐患", status: "未整改", date: "2025-09-01 09:00" },
-        { id: 2, problem: "高压气罐未定期检查", status: "未整改", date: "2025-09-02 11:20" },
-        { id: 3, problem: "化学品存放不规范", status: "未整改", date: "2025-09-03 14:10" },
-        { id: 4, problem: "消防通道堵塞", status: "未整改", date: "2025-09-03 17:40" },
-        { id: 5, problem: "仓库堆放易燃物品", status: "已整改", date: "2025-09-04 09:30" },
-      ],
-      trendData: [2, 3, 1, 5, 4, 2, 3], // 模拟最近 7 天新增问题
-    };
+      stats: { high: 0, medium: 0, low: 0 },
+      topList: [],
+      trendXAxis: [],
+      trendData: [],
+      _timer: null,
+      _chart: null
+    }
   },
   mounted() {
-    this.initChart();
+    this.loadDashboard()
+    // 简单轮询:8 秒刷新一次
+    this._timer = setInterval(this.loadDashboard, 8000)
+    this.$once('hook:beforeDestroy', () => {
+      if (this._timer) clearInterval(this._timer)
+      if (this._chart) this._chart.dispose()
+    })
   },
   methods: {
-    initChart() {
-      const chart = echarts.init(document.getElementById("riskChart"));
-      chart.setOption({
-        tooltip: { trigger: "axis" },
-        xAxis: {
-          type: "category",
-          data: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
-        },
-        yAxis: { type: "value" },
-        series: [
-          {
-            name: "新增隐患数",
-            type: "bar",
-            data: this.trendData,
-            itemStyle: { color: "#ff4d4f" },
-          },
-        ],
-      });
-    },
-  },
-};
-</script>
-
-<style scoped>
-.risk-dashboard {
-  padding: 20px;
-}
-
-.stats {
-  display: flex;
-  gap: 20px;
-}
-
-.stat-card {
-  flex: 1;
-  text-align: center;
-  padding: 20px;
-}
+    async loadDashboard() {
+      try {
+        const res = await fetchDashboard(7)
+        const c = res.counters || {}
+        this.stats.high = c.HIGH || 0
+        this.stats.medium = c.MEDIUM || 0
+        this.stats.low = c.LOW || 0
 
-.stat-card .title {
-  font-size: 16px;
-  margin-bottom: 10px;
-}
-
-.stat-card .count {
-  font-size: 32px;
-  font-weight: bold;
-}
+        // trend: [{date:'2025-09-01', count:2}, ...]
+        const trend = res.weeklyTrend || []
+        this.trendXAxis = trend.map(x => this.toWeekLabel(x.date))
+        this.trendData = trend.map(x => x.count || 0)
 
-.high {
-  border-left: 6px solid #ff4d4f;
-}
+        // top5
+        this.topList = res.top5High || []
 
-.medium {
-  border-left: 6px solid #faad14;
+        this.drawChart()
+      } catch (e) {
+        // 可以静默或提示
+        // this.$message.error('仪表盘加载失败')
+        console.error(e)
+      }
+    },
+    drawChart() {
+      if (!this._chart) {
+        this._chart = echarts.init(document.getElementById('riskChart'))
+      }
+      this._chart.setOption({
+        tooltip: { trigger: 'axis' },
+        xAxis: { type: 'category', data: this.trendXAxis },
+        yAxis: { type: 'value' },
+        series: [
+          { name: '新增隐患数', type: 'bar', data: this.trendData, itemStyle: { color: '#ff4d4f' } }
+        ]
+      })
+    },
+    toWeekLabel(ymd) {
+      // ymd -> '周一/周二' 标签(也可直接返回 ymd)
+      const d = new Date(ymd)
+      const w = d.getDay() // 0..6
+      const names = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
+      return names[w]
+    },
+    formatDateTime(dt) {
+      if (!dt) return ''
+      const d = new Date(dt)
+      const pad = n => (n < 10 ? '0' + n : n)
+      return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
+    }
+  }
 }
+</script>
 
-.low {
-  border-left: 6px solid #1890ff;
-}
+<style scoped>
+.risk-dashboard { padding: 20px; }
+.stats { display: flex; gap: 20px; }
+.stat-card { flex: 1; text-align: center; padding: 20px; }
+.stat-card .title { font-size: 16px; margin-bottom: 10px; }
+.stat-card .count { font-size: 32px; font-weight: bold; }
+.high   { border-left: 6px solid #ff4d4f; }
+.medium { border-left: 6px solid #faad14; }
+.low    { border-left: 6px solid #1890ff; }
 </style>

+ 121 - 72
src/views/safety/safetyManage/index.vue

@@ -3,11 +3,11 @@
     <!-- 新增风险问题 -->
     <div style="margin-bottom: 20px; display: flex; align-items: center;">
       <el-input
-        v-model="form.problem"
+        v-model="form.description"
         placeholder="请输入风险问题描述"
         style="width: 400px; margin-right: 10px;"
       />
-      <el-select v-model="form.level" placeholder="风险等级" style="width: 150px; margin-right: 10px;">
+      <el-select v-model="form.riskLevelCn" placeholder="风险等级" style="width: 150px; margin-right: 10px;">
         <el-option label="低" value="低"></el-option>
         <el-option label="中" value="中"></el-option>
         <el-option label="高" value="高"></el-option>
@@ -22,105 +22,154 @@
       </div>
       <el-table :data="problemList" border style="width: 100%;" fit>
         <el-table-column prop="id" label="编号" width="80" />
-        <el-table-column prop="problem" label="问题描述" />
-        <el-table-column prop="level" label="风险等级" width="120">
+        <el-table-column prop="description" label="问题描述" />
+        <el-table-column label="风险等级" width="120">
           <template slot-scope="scope">
-            <el-tag :type="scope.row.level === '高' ? 'danger' : scope.row.level === '中' ? 'warning' : 'info'">
-              {{ scope.row.level }}
+            <el-tag :type="scope.row.riskLevel === 'HIGH' ? 'danger' : scope.row.riskLevel === 'MEDIUM' ? 'warning' : 'info'">
+              {{ riskToCn(scope.row.riskLevel) }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="status" label="状态" width="120" />
-        <el-table-column prop="date" label="发现时间" width="180" />
-        <el-table-column label="操作" width="180">
+        <el-table-column label="状态" width="120">
           <template slot-scope="scope">
-            <el-button size="mini" type="primary" @click="markResolved(scope.$index)">
+            <el-tag :type="scope.row.status === 'PENDING' ? 'danger' : 'success'">
+              {{ scope.row.status === 'PENDING' ? '未整改' : '已整改' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="foundAt" label="发现时间" width="180">
+          <template slot-scope="scope">
+            {{ formatDateTime(scope.row.foundAt) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="220">
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              type="primary"
+              :disabled="scope.row.status === 'RECTIFIED'"
+              @click="markResolved(scope.row)"
+            >
               标记已整改
             </el-button>
-            <el-button size="mini" type="danger" @click="deleteProblem(scope.$index)">
+            <el-button size="mini" type="danger" @click="deleteProblem(scope.row)">
               删除
             </el-button>
           </template>
         </el-table-column>
       </el-table>
-      <div style="margin-top: 10px;">隐患总数:{{ problemList.length }}</div>
+      <div style="margin-top: 10px;">隐患总数:{{ total }}</div>
     </el-card>
   </div>
 </template>
 
 <script>
+import { fetchIssues, createIssue, updateIssueStatus, deleteIssue } from '@/api/safety/safety'
 export default {
-  name: "RiskWarning",
+  name: 'RiskWarning',
   data() {
     return {
       form: {
-        problem: "",
-        level: "",
+        description: '',
+        riskLevelCn: '' // '高' | '中' | '低'
       },
-      // 死数据示例
-      problemList: [
-        {
-          id: 1,
-          problem: "电线老化存在安全隐患",
-          level: "高",
-          status: "未整改",
-          date: "2025-09-01 09:00",
-        },
-        {
-          id: 2,
-          problem: "消防器材过期",
-          level: "中",
-          status: "未整改",
-          date: "2025-09-02 10:30",
-        },
-        {
-          id: 3,
-          problem: "仓库通道堆放杂物",
-          level: "低",
-          status: "已整改",
-          date: "2025-09-02 15:20",
-        },
-      ],
-    };
+      problemList: [],
+      total: 0,
+      pageNum: 1,
+      pageSize: 100,
+      _timer: null
+    }
+  },
+  mounted() {
+    this.loadList()
+    // 8 秒轮询
+    this._timer = setInterval(this.loadList, 8000)
+    this.$once('hook:beforeDestroy', () => {
+      if (this._timer) clearInterval(this._timer)
+    })
   },
   methods: {
-    addProblem() {
-      if (!this.form.problem || !this.form.level) {
-        this.$message.warning("请填写完整问题信息!");
-        return;
+    // 录入
+    async addProblem() {
+      if (!this.form.description || !this.form.riskLevelCn) {
+        this.$message.warning('请填写完整问题信息!')
+        return
+      }
+      const payload = {
+        description: this.form.description,
+        riskLevel: this.cnToRisk(this.form.riskLevelCn), // HIGH/MEDIUM/LOW
+        foundAt: new Date().toISOString()
+      }
+      try {
+        await createIssue(payload)
+        this.$message.success('录入成功!')
+        this.form.description = ''
+        this.form.riskLevelCn = ''
+        this.loadList()
+      } catch (e) {
+        this.$message.error('录入失败')
+        console.error(e)
       }
-      const newId = this.problemList.length
-        ? Math.max(...this.problemList.map((p) => p.id)) + 1
-        : 1;
-      this.problemList.push({
-        id: newId,
-        problem: this.form.problem,
-        level: this.form.level,
-        status: "未整改",
-        date: new Date().toLocaleString(),
-      });
-      this.$message.success("录入成功!");
-      this.form.problem = "";
-      this.form.level = "";
     },
-    markResolved(index) {
-      this.problemList[index].status = "已整改";
-      this.$message.success("该问题已标记为整改完成!");
+
+    // 列表
+    async loadList() {
+      try {
+        const params = { pageNum: this.pageNum, pageSize: this.pageSize }
+        const res = await fetchIssues(params)
+        this.problemList = (res && res.records) || []
+        this.total = (res && res.total) || 0
+      } catch (e) {
+        console.error(e)
+      }
     },
-    deleteProblem(index) {
-      this.$confirm("确认删除该问题吗?", "提示", {
-        type: "warning",
-      }).then(() => {
-        this.problemList.splice(index, 1);
-        this.$message.success("删除成功!");
-      });
+
+    // 标记整改
+    async markResolved(row) {
+      try {
+        await updateIssueStatus(row.id, 'RECTIFIED')
+        this.$message.success('已标记为整改完成')
+        this.loadList()
+      } catch (e) {
+        this.$message.error('操作失败')
+        console.error(e)
+      }
     },
-  },
-};
+
+    // 删除
+    async deleteProblem(row) {
+      try {
+        await this.$confirm('确认删除该问题吗?', '提示', { type: 'warning' })
+        await deleteIssue(row.id)
+        this.$message.success('删除成功')
+        this.loadList()
+      } catch (e) {
+        // 取消删除不提示错误
+        if (e !== 'cancel') console.error(e)
+      }
+    },
+
+    // 工具:中英映射
+    cnToRisk(cn) {
+      if (cn === '高') return 'HIGH'
+      if (cn === '中') return 'MEDIUM'
+      return 'LOW'
+    },
+    riskToCn(en) {
+      if (en === 'HIGH') return '高'
+      if (en === 'MEDIUM') return '中'
+      return '低'
+    },
+    formatDateTime(dt) {
+      if (!dt) return ''
+      const d = new Date(dt)
+      const pad = n => (n < 10 ? '0' + n : n)
+      return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
+    }
+  }
+}
 </script>
 
 <style scoped>
-.app-container {
-  padding: 20px;
-}
+.app-container { padding: 20px; }
 </style>