miaofuhao 1 mese fa
parent
commit
5702f1937e

+ 1 - 7
bigScreen3/src/utils/request.js

@@ -58,7 +58,7 @@ const service = axios.create({
58 58
     }
59 59
     if (
60 60
       !isRepeatSubmit &&
61
-      (config.method === 'post' || config.method === 'put')
61
+      ( config.method === 'put')
62 62
     ) {
63 63
       const requestObj = {
64 64
         url: config.url,
@@ -86,7 +86,6 @@ const service = axios.create({
86 86
           s_url === requestObj.url
87 87
         ) {
88 88
           const message = '数据正在处理,请勿重复提交'
89
-          console.warn(`[${s_url}]: ` + message)
90 89
           return Promise.reject(new Error(message))
91 90
         } else {
92 91
           cache.session.setJSON('sessionObj', requestObj)
@@ -96,7 +95,6 @@ const service = axios.create({
96 95
     return config
97 96
   },
98 97
   (error) => {
99
-    console.log(error)
100 98
     Promise.reject(error)
101 99
   }
102 100
 )
@@ -105,7 +103,6 @@ const service = axios.create({
105 103
 service.interceptors.response.use(
106 104
   (res) => {
107 105
     // 未设置状态码则默认成功状态
108
-    console.log('res', res)
109 106
     const code = res.data.status || 200
110 107
     // 获取错误信息
111 108
     const msg = errorCode[code] || res.data.message || errorCode['default']
@@ -144,7 +141,6 @@ service.interceptors.response.use(
144 141
         res.data.state.toLowerCase() === 50012 ||
145 142
         res.data.state.toLowerCase() === "notoken"
146 143
       ) {
147
-        console.log(router.currentRoute)
148 144
         if (router.currentRoute.value.path === "/chat/index") {
149 145
           router.push({ path: '/chat/login' })
150 146
           return
@@ -173,7 +169,6 @@ service.interceptors.response.use(
173 169
     }
174 170
   },
175 171
   (error) => {
176
-    console.log('err')
177 172
     let { message } = error
178 173
     if (message == 'Network Error') {
179 174
       message = '服务异常,请稍后重试'
@@ -193,7 +188,6 @@ export function download(url, params, filename, config) {
193 188
     text: '正在下载数据,请稍候',
194 189
     background: 'rgba(0, 0, 0, 0.7)'
195 190
   })
196
-  console.log(params)
197 191
   return service
198 192
     .post(url, params, {
199 193
       transformRequest: [

+ 16 - 2
bigScreen3/src/views/dashboard/cpns/DataInsights.vue

@@ -1,14 +1,20 @@
1 1
 <template>
2
-  <div class="data-insights">
2
+  <div class="data-insights" @click="handleClick">
3 3
     <div ref="chartRef" class="echart" style="margin-top: 2vh;"></div>
4 4
   </div>
5
-  
5
+  <DataInsightsDetail
6
+      :visible="dataInsightsDetailVisible"
7
+      :options="{fWorkordertypeid: 4}"
8
+      :dataInsightsDetail="dataInsightsDetail"
9
+      @close="dataInsightsDetailVisible = false"  
10
+    />
6 11
 </template>
7 12
 
8 13
 <script setup>
9 14
 import { ref, onMounted, onBeforeUnmount ,watch} from 'vue'
10 15
 import * as echarts from 'echarts'
11 16
 import { getPageListData } from '@/api/index.js'
17
+import DataInsightsDetail from '@/views/dashboard/cpns/DataInsightsDetail.vue'
12 18
 
13 19
 // 定义 props
14 20
 const props = defineProps({
@@ -21,15 +27,23 @@ watch(() => props.selectedProjectId, () => {
21 27
   fetchDataInsights()
22 28
 })
23 29
 // 响应式数据
30
+const dataInsightsDetailVisible = ref(false)
31
+const handleClick = () => {
32
+  console.log('点击了数据洞察')
33
+  dataInsightsDetailVisible.value = true  
34
+}
24 35
 const categories = ref(['区域A', '区域B', '区域C', '区域D', '区域E', '区域F'])
25 36
 const smartValues = ref([520, 740, 520, 802, 520, 540])
26 37
 const traditionalValues = ref([300, 400, 260, 192, 300, 300])
27 38
 const maxYAxisValue = ref(0)
39
+const dataInsightsDetail = ref([])
40
+
28 41
 // 获取数据
29 42
 const fetchDataInsights = async () => {
30 43
   try {
31 44
     const response = await getPageListData(`/zhjc-irrigation-base/dataInsights/${props.selectedProjectId}`)
32 45
     if (response.status === 200 && response.data) {
46
+      dataInsightsDetail.value = response.data
33 47
       const data = response.data.Consumption || []
34 48
       console.log('数据洞察', data)
35 49
       // 根据接口返回的数据结构进行赋值

+ 363 - 0
bigScreen3/src/views/dashboard/cpns/DataInsightsDetail.vue

@@ -0,0 +1,363 @@
1
+<template>
2
+  <div v-if="visible" class="early-warning-modal" @click.self="handleClose">
3
+    <div class="early-warning-content">
4
+      <div class="early-warning-header">
5
+        <h2>数据洞察</h2>
6
+        <button class="early-warning-close" @click="handleClose">×</button>
7
+      </div>
8
+      <div class="early-warning-body">
9
+        <!-- 详情统计表格 -->
10
+        <div class="warning-detail-section">
11
+          <div class="section-title">• 养护效率</div>
12
+          <div class="warning-detail-table">
13
+            <table class="data-table">
14
+              <thead>
15
+                <tr>
16
+                  <th>区域</th>
17
+                  <th>智能养护面积</th>
18
+                  <th>人工养护面积</th>
19
+                  <th>存活率</th>
20
+                  <th>设施完好率</th>
21
+                  <th>投诉率</th>
22
+                </tr>
23
+              </thead>
24
+              <tbody>
25
+                <tr v-for="(item, index) in dataInsightsDetail.effect || []" :key="index">
26
+                  <td>{{ item.sectionName || '-' }}</td>
27
+                  <td>{{ item.smartScope || '0' }}</td>
28
+                  <td>{{ item.manualScope || '0' }}</td>
29
+                  <td>{{ item.surviv || '0.00%' }}</td>
30
+                  <td>{{ item.intact || '0.00%' }}</td>
31
+                  <td>{{ item.complaint || '0.00%' }}</td>
32
+                </tr>
33
+                <tr v-if="!dataInsightsDetail.effect || dataInsightsDetail.effect.length === 0">
34
+                  <td colspan="6" class="no-data">暂无数据</td>
35
+                </tr>
36
+              </tbody>
37
+            </table>
38
+          </div>
39
+        </div>
40
+        
41
+        <!-- 预警来源数据表格 -->
42
+        <div class="warning-source-section">
43
+          <div class="section-title">• 资源消耗</div>
44
+          <div class="warning-source-table">
45
+            <table class="data-table">
46
+              <thead>
47
+                <tr>
48
+                  <th>养护区域</th>
49
+                  <th>智慧调控</th>
50
+                  <th>传统模式</th>
51
+                  <th>节水百分比</th>
52
+                </tr>
53
+              </thead>
54
+              <tbody>
55
+                <tr v-for="(item, index) in dataInsightsDetail.Consumption || []" :key="index">
56
+                  <td>{{ item.regionName || '-' }}</td>
57
+                  <td>{{ item.smartWater || '0' }}</td>
58
+                  <td>{{ item.manualWater || '0' }}</td>
59
+                  <td>{{ item.conservationRate || '0.00%' }}</td>
60
+                </tr>
61
+                <tr v-if="!dataInsightsDetail.Consumption || dataInsightsDetail.Consumption.length === 0">
62
+                  <td colspan="4" class="no-data">暂无数据</td>
63
+                </tr>
64
+              </tbody>
65
+            </table>
66
+          </div>
67
+        </div>
68
+      </div>
69
+    </div>
70
+  </div>
71
+</template>
72
+
73
+<script setup>
74
+import { computed } from 'vue'
75
+
76
+// 定义 props
77
+const props = defineProps({
78
+  visible: {
79
+    type: Boolean,
80
+    default: false
81
+  },
82
+  options: {
83
+    type: Object,
84
+    default: {}
85
+  },
86
+  dataInsightsDetail: {
87
+    type: Object,
88
+    default: () => ({
89
+      effect: [],
90
+      Consumption: []
91
+    })
92
+  }
93
+})
94
+
95
+// 定义事件
96
+const emit = defineEmits(['update:visible', 'close'])
97
+
98
+// 关闭弹框
99
+function handleClose() {
100
+  emit('update:visible', false)
101
+  emit('close')
102
+}
103
+</script>
104
+
105
+<style scoped>
106
+/* 数据洞察详情弹窗样式 */
107
+.early-warning-modal {
108
+  position: fixed;
109
+  z-index: 9999;
110
+  inset: 0;
111
+  background: rgba(0, 10, 30, 0.8);
112
+  display: flex;
113
+  align-items: center;
114
+  justify-content: center;
115
+  animation: fadeIn 0.3s ease;
116
+}
117
+
118
+@keyframes fadeIn {
119
+  from {
120
+    opacity: 0;
121
+  }
122
+  to {
123
+    opacity: 1;
124
+  }
125
+}
126
+
127
+.early-warning-content {
128
+  position: relative;
129
+  width: min(90vw, 1400px);
130
+  max-height: 90vh;
131
+  background: linear-gradient(135deg, #001a33 0%, #003366 100%);
132
+  border: 2px solid #0099ff;
133
+  border-radius: 8px;
134
+  overflow: hidden;
135
+  box-shadow: 0 0 30px rgba(0, 153, 255, 0.3);
136
+  backdrop-filter: blur(10px);
137
+  animation: slideIn 0.3s ease;
138
+}
139
+
140
+@keyframes slideIn {
141
+  from {
142
+    transform: translateY(-20px);
143
+    opacity: 0;
144
+  }
145
+  to {
146
+    transform: translateY(0);
147
+    opacity: 1;
148
+  }
149
+}
150
+
151
+.early-warning-header {
152
+  position: relative;
153
+  padding: 12px 20px;
154
+  background: transparent;
155
+  border: none;
156
+  display: flex;
157
+  align-items: center;
158
+  justify-content: center;
159
+}
160
+
161
+.early-warning-header h2 {
162
+  color: #ffffff;
163
+  font-size: 18px;
164
+  font-weight: 600;
165
+  margin: 0;
166
+  padding: 4px 45px;
167
+  background: linear-gradient(90deg, rgba(0, 153, 255, 0.05) 0%, rgba(0, 153, 255, 0.9) 50%, rgba(0, 153, 255, 0.05) 100%);
168
+  text-shadow: 0 0 8px rgba(0, 153, 255, 0.6);
169
+  border-radius: 4px;
170
+  display: inline-block;
171
+}
172
+
173
+.early-warning-close {
174
+  position: absolute;
175
+  right: 20px;
176
+  top: 50%;
177
+  transform: translateY(-50%);
178
+  width: 30px;
179
+  height: 30px;
180
+  border: none;
181
+  background: transparent;
182
+  color: #ffffff;
183
+  font-size: 20px;
184
+  line-height: 30px;
185
+  text-align: center;
186
+  border-radius: 4px;
187
+  cursor: pointer;
188
+  transition: all 0.3s ease;
189
+  padding: 0;
190
+}
191
+
192
+.early-warning-close:hover {
193
+  background: rgba(0, 40, 80, 0.8);
194
+  box-shadow: 0 0 10px rgba(0, 153, 255, 0.6);
195
+}
196
+
197
+.early-warning-body {
198
+  padding: 20px;
199
+  overflow-y: auto;
200
+  max-height: calc(90vh - 120px);
201
+}
202
+
203
+/* 区域标题样式 */
204
+.section-title {
205
+  color: #66CCFF;
206
+  font-size: 16px;
207
+  font-weight: 600;
208
+  margin-bottom: 15px;
209
+  text-shadow: 0 0 4px rgba(102, 204, 255, 0.6);
210
+  text-align: left;
211
+}
212
+
213
+/* 预警详情区域 */
214
+.warning-detail-section {
215
+  margin-bottom: 30px;
216
+}
217
+
218
+/* 预警来源区域 */
219
+.warning-source-section {
220
+  margin-bottom: 20px;
221
+}
222
+
223
+/* 表格容器样式 */
224
+.warning-detail-table {
225
+  width: 100%;
226
+  overflow-x: auto;
227
+  overflow-y: auto;
228
+  max-height: 300px; /* 增加高度以显示更多数据 */
229
+  background: rgba(0, 10, 30, 0.5);
230
+  border: 1px solid rgba(0, 153, 255, 0.3);
231
+  border-radius: 6px;
232
+  box-shadow: 0 0 20px rgba(0, 153, 255, 0.1);
233
+  transition: all 0.3s ease;
234
+}
235
+
236
+.warning-source-table {
237
+  width: 100%;
238
+  overflow-x: auto;
239
+  overflow-y: auto;
240
+  max-height: 300px; /* 增加高度以显示更多数据 */
241
+  background: rgba(0, 10, 30, 0.5);
242
+  border: 1px solid rgba(0, 153, 255, 0.3);
243
+  border-radius: 6px;
244
+  box-shadow: 0 0 20px rgba(0, 153, 255, 0.1);
245
+  transition: all 0.3s ease;
246
+}
247
+
248
+/* 美化滚动条 */
249
+.warning-detail-table::-webkit-scrollbar,
250
+.warning-source-table::-webkit-scrollbar {
251
+  width: 8px;
252
+  height: 8px;
253
+}
254
+
255
+.warning-detail-table::-webkit-scrollbar-track,
256
+.warning-source-table::-webkit-scrollbar-track {
257
+  background: rgba(0, 10, 30, 0.3);
258
+  border-radius: 4px;
259
+}
260
+
261
+.warning-detail-table::-webkit-scrollbar-thumb,
262
+.warning-source-table::-webkit-scrollbar-thumb {
263
+  background: rgba(0, 153, 255, 0.5);
264
+  border-radius: 4px;
265
+  transition: background 0.3s ease;
266
+}
267
+
268
+.warning-detail-table::-webkit-scrollbar-thumb:hover,
269
+.warning-source-table::-webkit-scrollbar-thumb:hover {
270
+  background: rgba(0, 153, 255, 0.8);
271
+}
272
+
273
+/* 数据表格样式 */
274
+.data-table {
275
+  width: 100%;
276
+  border-collapse: collapse;
277
+  color: #ffffff;
278
+}
279
+
280
+.data-table thead {
281
+  background-color: #00164B;
282
+  position: sticky;
283
+  top: 0;
284
+  z-index: 10;
285
+}
286
+
287
+.data-table th {
288
+  padding: 10px 15px;
289
+  text-align: center;
290
+  font-weight: 600;
291
+  font-size: 14px;
292
+  color: #66CCFF;
293
+  border-bottom: 1px solid rgba(0, 153, 255, 0.3);
294
+  text-shadow: 0 0 4px rgba(102, 204, 255, 0.6);
295
+  height: 40px;
296
+}
297
+
298
+.data-table td {
299
+  padding: 10px 15px;
300
+  font-size: 13px;
301
+  color: #ffffff;
302
+  text-align: center;
303
+  border-bottom: 1px solid rgba(0, 68, 136, 0.3);
304
+  transition: all 0.2s ease;
305
+  height: 40px;
306
+}
307
+
308
+.data-table tbody tr:nth-child(odd) {
309
+  background-color: #0D2958;
310
+}
311
+
312
+.data-table tbody tr:nth-child(even) {
313
+  background-color: #0B3466;
314
+}
315
+
316
+.data-table tbody tr {
317
+  transition: all 0.3s ease;
318
+  height: 40px; /* 固定数据行高度 */
319
+}
320
+
321
+.data-table tbody tr:hover {
322
+  background-color: rgba(0, 153, 255, 0.2);
323
+  box-shadow: 0 0 10px rgba(0, 153, 255, 0.2);
324
+}
325
+
326
+.data-table tbody tr:last-child td {
327
+  border-bottom: none;
328
+}
329
+
330
+/* 无数据显示样式 */
331
+.no-data {
332
+  color: #666;
333
+  text-align: center;
334
+  padding: 30px;
335
+  font-style: italic;
336
+  background-color: rgba(0, 10, 30, 0.3);
337
+}
338
+
339
+/* 响应式调整 */
340
+@media (max-width: 768px) {
341
+  .early-warning-content {
342
+    width: 95vw;
343
+    max-height: 95vh;
344
+  }
345
+  
346
+  .data-table th,
347
+  .data-table td {
348
+    padding: 8px 10px;
349
+    font-size: 12px;
350
+    height: 36px;
351
+  }
352
+  
353
+  .warning-detail-table,
354
+  .warning-source-table {
355
+    border-radius: 4px;
356
+  }
357
+  
358
+  .section-title {
359
+    font-size: 14px;
360
+    margin-bottom: 10px;
361
+  }
362
+}
363
+</style>

+ 29 - 9
bigScreen3/src/views/dashboard/cpns/EarlyWarning.vue

@@ -1,5 +1,5 @@
1 1
 <template>
2
-  <div class="early-warning">
2
+  <div class="early-warning" @click="handleClick">
3 3
     <div class="warning-summary"  style="margin-top: 1.5vh;">
4 4
       <div class="summary-item" v-for="(item, idx) in summaryList" :key="idx">
5 5
         <span class="summary-label">{{ item.name }}</span>
@@ -15,7 +15,7 @@
15 15
       <table>
16 16
         <thead>
17 17
           <tr>
18
-            <th>预警类型</th>
18
+            <th style="width: 110px;">预警类型</th>
19 19
             <th>位置</th>
20 20
             <th>等级</th>
21 21
             <th>触发时间</th>
@@ -24,7 +24,7 @@
24 24
         <tbody>
25 25
           <tr v-for="(item, index) in warningList" :key="index">
26 26
             <td>{{ item.type }}</td>
27
-            <td class="ellipsis"><span class="cell-text" :title="item.location">{{ item.location }}</span></td>
27
+            <td class="ellipsis" style="text-align: left;"><span class="cell-text" :title="item.location">{{ item.location }}</span></td>
28 28
             <td>
29 29
               <span :class="['level-badge', `level-${item.level}`]">
30 30
                 {{ item.levelText }}
@@ -36,11 +36,18 @@
36 36
       </table>
37 37
     </div>
38 38
   </div>
39
+  <EarlyWarningDetail
40
+      :visible="earlyWarningDetailVisible"
41
+      :options="{fWorkordertypeid: 4}"
42
+      :warningDetail="warningDetail"
43
+      @close="earlyWarningDetailVisible = false"  
44
+    />
39 45
 </template>
40 46
 
41 47
 <script setup>
42 48
 import { ref, onMounted, watch } from 'vue'
43 49
 import { getPageListData } from '@/api/index'
50
+import EarlyWarningDetail from '@/views/dashboard/cpns/EarlyWarningDetail.vue'
44 51
 
45 52
 // 定义 props
46 53
 const props = defineProps({
@@ -52,6 +59,15 @@ const props = defineProps({
52 59
 watch(() => props.selectedProjectId, () => {
53 60
   fetchWarningData()
54 61
 })
62
+
63
+// 定义响应式数据
64
+const earlyWarningDetailVisible = ref(false)
65
+const warningDetail = ref({})
66
+
67
+const handleClick = () => {
68
+  console.log('点击了预警')
69
+  earlyWarningDetailVisible.value = true  
70
+}
55 71
 // 顶部汇总数据(响应式)
56 72
 const summaryList = ref([])
57 73
 
@@ -63,6 +79,7 @@ const fetchWarningData = async () => {
63 79
   try {
64 80
     const response = await getPageListData(`/woworkorderbase/warning/${props.selectedProjectId}`, {})
65 81
     if (response && response.status === 200) {
82
+      warningDetail.value = response.data
66 83
       // 处理汇总数据
67 84
       if (response.data.type && Array.isArray(response.data.type)) {
68 85
         // 确保数据长度足够(重复显示如果不够)
@@ -80,20 +97,23 @@ const fetchWarningData = async () => {
80 97
       if (response.data.workOrderList && Array.isArray(response.data.workOrderList) && response.data.workOrderList.length > 0) {
81 98
         warningList.value = response.data.workOrderList.map(item => {
82 99
           // 处理预警等级映射
83
-          let level = 'medium'
84
-          let levelText = ''
100
+          let level = item.fRiskLevel // 3:高, 2:中, 1:低 
101
+          let levelText = ''
85 102
           
86
-          if (item.level === 'high' || item.levelText === '高' || item.level === '1') {
103
+          if (level === 3) {
87 104
             level = 'high'
88 105
             levelText = '高'
89
-          } else if (item.level === 'low' || item.levelText === '低' || item.level === '3') {
106
+          } else if (level === 2) {
107
+            level = 'medium'
108
+            levelText = '中'
109
+          } else if (level === 1) {
90 110
             level = 'low'
91 111
             levelText = '低'
92 112
           }
93 113
           
94 114
           return {
95
-            type: item.type || item.warningType || '未知预警',
96
-            location: item.location || item.address || '未知位置',
115
+            type: item.fCategoryname1 || '未知预警',
116
+            location: item.fAddress || '未知位置',
97 117
             level: level,
98 118
             levelText: levelText,
99 119
             time: item.time || item.createTime || formatDate(new Date())

+ 54 - 205
bigScreen3/src/views/dashboard/cpns/EarlyWarningDetail.vue

@@ -20,11 +20,11 @@
20 20
                 </tr>
21 21
               </thead>
22 22
               <tbody>
23
-                <tr v-for="(item, index) in warningDetailData" :key="index">
24
-                  <td>{{ item.type }}</td>
25
-                  <td>{{ item.total }}</td>
26
-                  <td>{{ item.completed }}</td>
27
-                  <td>{{ item.completionRate }}</td>
23
+                <tr v-for="(item, index) in warningDetail.type" :key="index">
24
+                  <td>{{ item.name }}</td>
25
+                  <td>{{ item.number }}</td>
26
+                  <td>{{ item.endNumber }}</td>
27
+                  <td>{{ item.endNumber / item.number * 100 + '%' }}</td>
28 28
                 </tr>
29 29
               </tbody>
30 30
             </table>
@@ -48,14 +48,14 @@
48 48
                 </tr>
49 49
               </thead>
50 50
               <tbody>
51
-                <tr v-for="(item, index) in warningSourceData" :key="index">
52
-                  <td>{{ item.discoveryMethod }}</td>
53
-                  <td>{{ item.eventType }}</td>
51
+                <tr v-for="(item, index) in warningDetail.workOrderList" :key="index">
52
+                  <td>{{ item.fWorkordertype }}</td>
53
+                  <td>{{ item.riskclassifyname }}</td>
54 54
                   <td>{{ item.pushTime }}</td>
55
-                  <td>{{ item.pushDepartment }}</td>
56
-                  <td>{{ item.receiveTime }}</td>
57
-                  <td>{{ item.receiveDepartment }}</td>
58
-                  <td class="status-completed">{{ item.status }}</td>
55
+                  <td>{{ item.checkdept }}</td>
56
+                  <td>{{ item.checkdept }}</td>
57
+                  <td>{{ item.distributedept }}</td>
58
+                  <td :class="getStatusClass(item.status)">{{ item.fState }}</td>
59 59
                 </tr>
60 60
               </tbody>
61 61
             </table>
@@ -67,7 +67,7 @@
67 67
 </template>
68 68
 
69 69
 <script setup>
70
-import { ref, onMounted } from 'vue'
70
+import { ref, computed } from 'vue'
71 71
 
72 72
 // 定义 props
73 73
 const props = defineProps({
@@ -78,211 +78,41 @@ const props = defineProps({
78 78
   options: {
79 79
     type: Object,
80 80
     default: {}
81
+  },
82
+  warningDetail: {
83
+    type: Object,
84
+    default: () => ({
85
+      type: [],
86
+      workOrderList: []
87
+    })
81 88
   }
82 89
 })
83 90
 
84 91
 // 定义事件
85 92
 const emit = defineEmits(['update:visible', 'close'])
86 93
 
87
-// 预警详情数据
88
-const warningDetailData = ref([
89
-  {
90
-    type: '大数据分析',
91
-    total: '19892',
92
-    completed: '988',
93
-    completionRate: '18.08%'
94
-  },
95
-  {
96
-    type: '抓取感知',
97
-    total: '19827',
98
-    completed: '728',
99
-    completionRate: '18.08%'
100
-  },
101
-  {
102
-    type: '类型A',
103
-    total: '19892',
104
-    completed: '988',
105
-    completionRate: '18.08%'
106
-  },
107
-  {
108
-    type: '类型B',
109
-    total: '19827',
110
-    completed: '728',
111
-    completionRate: '18.08%'
112
-  },
113
-  {
114
-    type: '类型C',
115
-    total: '15678',
116
-    completed: '654',
117
-    completionRate: '22.35%'
118
-  },
119
-  {
120
-    type: '类型D',
121
-    total: '18923',
122
-    completed: '876',
123
-    completionRate: '19.76%'
124
-  },
125
-  {
126
-    type: '类型E',
127
-    total: '14567',
128
-    completed: '543',
129
-    completionRate: '21.45%'
130
-  },
131
-  {
132
-    type: '类型F',
133
-    total: '16789',
134
-    completed: '765',
135
-    completionRate: '17.89%'
136
-  },
137
-  {
138
-    type: '类型G',
139
-    total: '13456',
140
-    completed: '432',
141
-    completionRate: '23.12%'
142
-  },
143
-  {
144
-    type: '类型H',
145
-    total: '17890',
146
-    completed: '890',
147
-    completionRate: '16.54%'
148
-  },
149
-  {
150
-    type: '类型I',
151
-    total: '12345',
152
-    completed: '321',
153
-    completionRate: '24.56%'
154
-  },
155
-  {
156
-    type: '类型J',
157
-    total: '18901',
158
-    completed: '901',
159
-    completionRate: '15.23%'
160
-  }
161
-])
162
-
163
-// 预警来源数据
164
-const warningSourceData = ref([
165
-  {
166
-    discoveryMethod: '抓取感知',
167
-    eventType: '毁坏绿萍',
168
-    pushTime: '2025-09-22 04:24:40',
169
-    pushDepartment: '园林中心',
170
-    receiveTime: '2025-09-22 04:24:40',
171
-    receiveDepartment: '龙源湖公园',
172
-    status: '完结'
173
-  },
174
-  {
175
-    discoveryMethod: '大数据发现',
176
-    eventType: '公园密度过大',
177
-    pushTime: '2025-09-22 04:24:40',
178
-    pushDepartment: '园林中心',
179
-    receiveTime: '2025-09-22 04:24:40',
180
-    receiveDepartment: '龙源湖公园',
181
-    status: '完结'
182
-  },
183
-  {
184
-    discoveryMethod: '视频监控',
185
-    eventType: '游客越界',
186
-    pushTime: '2025-09-22 05:30:15',
187
-    pushDepartment: '安全监控中心',
188
-    receiveTime: '2025-09-22 05:31:00',
189
-    receiveDepartment: '东区管理处',
190
-    status: '处理中'
191
-  },
192
-  {
193
-    discoveryMethod: '市民举报',
194
-    eventType: '垃圾堆积',
195
-    pushTime: '2025-09-22 06:15:22',
196
-    pushDepartment: '投诉处理中心',
197
-    receiveTime: '2025-09-22 06:16:30',
198
-    receiveDepartment: '环卫部门',
199
-    status: '待处理'
200
-  },
201
-  {
202
-    discoveryMethod: '传感器检测',
203
-    eventType: '水质异常',
204
-    pushTime: '2025-09-22 07:45:10',
205
-    pushDepartment: '环境监测站',
206
-    receiveTime: '2025-09-22 07:46:25',
207
-    receiveDepartment: '湖务管理处',
208
-    status: '紧急处理'
209
-  },
210
-  {
211
-    discoveryMethod: 'AI识别',
212
-    eventType: '可疑行为',
213
-    pushTime: '2025-09-22 08:30:45',
214
-    pushDepartment: '智能管理中心',
215
-    receiveTime: '2025-09-22 08:31:10',
216
-    receiveDepartment: '安保部门',
217
-    status: '处理中'
218
-  },
219
-  {
220
-    discoveryMethod: '巡检发现',
221
-    eventType: '设施损坏',
222
-    pushTime: '2025-09-22 09:15:30',
223
-    pushDepartment: '设施维护科',
224
-    receiveTime: '2025-09-22 09:16:45',
225
-    receiveDepartment: '维修班组',
226
-    status: '待修复'
227
-  },
228
-  {
229
-    discoveryMethod: '大数据分析',
230
-    eventType: '人流异常',
231
-    pushTime: '2025-09-22 10:45:20',
232
-    pushDepartment: '数据分析中心',
233
-    receiveTime: '2025-09-22 10:46:15',
234
-    receiveDepartment: '游客服务中心',
235
-    status: '已疏导'
236
-  },
237
-  {
238
-    discoveryMethod: '气象预警',
239
-    eventType: '暴雨预警',
240
-    pushTime: '2025-09-22 11:30:10',
241
-    pushDepartment: '气象服务中心',
242
-    receiveTime: '2025-09-22 11:31:05',
243
-    receiveDepartment: '应急管理处',
244
-    status: '预警中'
245
-  },
246
-  {
247
-    discoveryMethod: '设备告警',
248
-    eventType: '电力故障',
249
-    pushTime: '2025-09-22 12:45:35',
250
-    pushDepartment: '设备管理科',
251
-    receiveTime: '2025-09-22 12:46:20',
252
-    receiveDepartment: '电力维修组',
253
-    status: '紧急处理'
254
-  },
255
-  {
256
-    discoveryMethod: '无人机巡查',
257
-    eventType: '植被异常',
258
-    pushTime: '2025-09-22 13:30:25',
259
-    pushDepartment: '园林技术科',
260
-    receiveTime: '2025-09-22 13:31:10',
261
-    receiveDepartment: '绿化班组',
262
-    status: '待处理'
263
-  },
264
-  {
265
-    discoveryMethod: '舆情监控',
266
-    eventType: '负面评价',
267
-    pushTime: '2025-09-22 14:45:15',
268
-    pushDepartment: '舆情管理中心',
269
-    receiveTime: '2025-09-22 14:46:30',
270
-    receiveDepartment: '客服中心',
271
-    status: '处理中'
94
+// 获取状态样式类
95
+const getStatusClass = (status) => {
96
+  if (!status) return 'status-unknown'
97
+  
98
+  status = status.toString()
99
+  if (status.includes('已') || status.includes('完结') || status.includes('完成') || status.includes('疏导')) {
100
+    return 'status-completed'
101
+  } else if (status.includes('处理中') || status.includes('进行中')) {
102
+    return 'status-processing'
103
+  } else if (status.includes('待') || status.includes('预警')) {
104
+    return 'status-pending'
105
+  } else if (status.includes('紧急') || status.includes('异常')) {
106
+    return 'status-emergency'
272 107
   }
273
-])
108
+  return 'status-unknown'
109
+}
274 110
 
275 111
 // 关闭弹框
276 112
 function handleClose() {
277 113
   emit('update:visible', false)
278 114
   emit('close')
279 115
 }
280
-
281
-// 组件挂载时可以获取数据
282
-onMounted(() => {
283
-  // 这里可以添加数据获取逻辑
284
-  // fetchData()
285
-})
286 116
 </script>
287 117
 
288 118
 <style scoped>
@@ -347,6 +177,7 @@ onMounted(() => {
347 177
   border-radius: 4px;
348 178
   cursor: pointer;
349 179
   transition: all 0.3s ease;
180
+  padding: 0;
350 181
 }
351 182
 
352 183
 .early-warning-close:hover {
@@ -477,7 +308,25 @@ onMounted(() => {
477 308
   color: #00ff99;
478 309
   font-weight: 500;
479 310
 }
311
+.status-processing {
312
+  color: #0099ff;
313
+  font-weight: 500;
314
+}
315
+
316
+.status-pending {
317
+  color: #ffcc00;
318
+  font-weight: 500;
319
+}
480 320
 
321
+.status-overtime {
322
+  color: #ff0000;
323
+  font-weight: 500;
324
+}
325
+
326
+.status-unknown {
327
+  color: #999999;
328
+  font-weight: 500;
329
+}
481 330
 /* 响应式调整 */
482 331
 @media (max-width: 768px) {
483 332
   .early-warning-content {

+ 356 - 100
bigScreen3/src/views/dashboard/cpns/InfoPopup.vue

@@ -314,8 +314,9 @@
314 314
 </template>
315 315
 
316 316
 <script setup>
317
-import { ref, reactive } from 'vue'
317
+import { ref, reactive, watch, onMounted } from 'vue'
318 318
 import caseImage from '@/assets/6.1.png'
319
+import { createPageData } from '@/api/index.js'
319 320
 
320 321
 // 定义props
321 322
 const props = defineProps({
@@ -326,6 +327,35 @@ const props = defineProps({
326 327
   }
327 328
 })
328 329
 
330
+// 监听fParkid变化,更新数据
331
+watch(() => props.data.fParkid, (newParkId) => {
332
+  if (newParkId) {
333
+    updateAllData(newParkId)
334
+  }
335
+}, { immediate: true })
336
+
337
+// 组件挂载时更新数据
338
+onMounted(() => {
339
+  if (props.data.fParkid) {
340
+    updateAllData(props.data.fParkid)
341
+  }
342
+})
343
+
344
+// 更新所有数据
345
+const updateAllData = async (parkId) => {
346
+  try {
347
+    // 并行获取所有数据
348
+    await Promise.all([
349
+      fetchBasicFacilityData(parkId),
350
+      fetchVideoData(parkId),
351
+      fetchDiscoveryData(parkId),
352
+      fetchAttendanceAndMaintenanceData(parkId)
353
+    ])
354
+  } catch (error) {
355
+    console.error('更新数据失败:', error)
356
+  }
357
+}
358
+
329 359
 // Tab切换数据
330 360
 const activeTab = ref('basic')
331 361
 const activeMaintenanceTab = ref('statistics') // 设备运维子tab
@@ -344,36 +374,68 @@ const maintenanceTabs = [
344 374
   { key: 'warning', label: '设备预警' }
345 375
 ]
346 376
 
347
-// 公园基础信息(虚拟数据)
377
+// 公园基础信息
348 378
 const parkBasicInfo = reactive({
349
-  parkName: props.data.name || '郑州绿博园项目',
350
-  address: '河南省郑州市航空港经济综合实验区苑陵路140号',
351
-  director: '张三',
379
+  parkName: props.data.name || props.data.fParkname || '郑州绿博园项目',
380
+  address: props.data.fAddress || '地址信息获取中...',
381
+  director: props.data.fHeader || '负责人信息获取中...',
352 382
   phone: '13828239092',
353
-  creditScore: 100,
354
-  creditLevel: 'A',
355
-  riskScore: 80,
356
-  riskLevel: 'B'
383
+  creditScore: props.data.credit_score || 0,
384
+  creditLevel: props.data.credit_level || '-',
385
+  riskScore: props.data.risk_score || 0,
386
+  riskLevel: props.data.risk_level || '-'
357 387
 })
358 388
 
359
-// 公园设施数据(虚拟数据)
360
-const facilityData = reactive([
361
-  { serial: 1, project: '绿地养护面积', unit: 'm²', quantity: 220068 },
362
-  { serial: 2, project: '水体维护面积', unit: 'm²', quantity: 2630 },
363
-  { serial: 3, project: '建筑维护面积', unit: 'm²', quantity: 3247.8 },
364
-  { serial: 4, project: '园林路面积', unit: 'm²', quantity: 220068 },
365
-  { serial: 5, project: '围墙面积', unit: 'm²', quantity: 2630 },
366
-  { serial: 6, project: '花架面积', unit: 'm²', quantity: 3247.8 },
367
-  { serial: 7, project: '健身场地面积', unit: 'm²', quantity: 220068 },
368
-  { serial: 8, project: '园区长度', unit: 'm', quantity: 2630 },
369
-  { serial: 9, project: '断面积', unit: 'm²', quantity: 3247.8 },
370
-  { serial: 4, project: '园林路面积', unit: 'm²', quantity: 220068 },
371
-  { serial: 5, project: '围墙面积', unit: 'm²', quantity: 2630 },
372
-  { serial: 6, project: '花架面积', unit: 'm²', quantity: 3247.8 },
373
-  { serial: 7, project: '健身场地面积', unit: 'm²', quantity: 220068 },
374
-  { serial: 8, project: '园区长度', unit: 'm', quantity: 2630 },
375
-  { serial: 9, project: '断面积', unit: 'm²', quantity: 3247.8 }
376
-])
389
+// 公园设施数据
390
+const facilityData = reactive([])
391
+
392
+// 获取基础设施数据
393
+const fetchBasicFacilityData = async (parkId) => {
394
+  try {
395
+    const response = await createPageData('/t-base-facilityinfo/selectByPageListNew', {
396
+      current: 1,
397
+      fCategoryid: 0,
398
+      fCategoryname: "",
399
+      fFacilityid: parkId,
400
+      fName: "",
401
+      fParkid: 0,
402
+      fParkname: props.data.name || "南水北调公园",
403
+      fStateid: 1,
404
+      size: 999
405
+    })
406
+    
407
+    const data = response.data || []
408
+    if (data.length === 0) {
409
+      // 如果没有数据,使用默认数据
410
+      facilityData.splice(0, facilityData.length)
411
+      [
412
+        { serial: 1, project: '绿地养护面积', unit: 'm²', quantity: 0 },
413
+        { serial: 2, project: '水体维护面积', unit: 'm²', quantity: 0 },
414
+        { serial: 3, project: '建筑维护面积', unit: 'm²', quantity: 0 }
415
+      ].forEach(item => facilityData.push(item))
416
+    } else {
417
+      // 更新数据
418
+      facilityData.splice(0, facilityData.length)
419
+      data.forEach((item, index) => {
420
+        facilityData.push({
421
+          serial: index + 1,
422
+          project: item.fcategoryname || '未命名项目',
423
+          unit: item.unit || '个',
424
+          quantity: item.amount || 0
425
+        })
426
+      })
427
+    }
428
+  } catch (error) {
429
+    console.error('获取设施数据失败:', error)
430
+    // 错误时使用默认数据
431
+    facilityData.splice(0, facilityData.length)
432
+    [
433
+      { serial: 1, project: '绿地养护面积', unit: 'm²', quantity: 0 },
434
+      { serial: 2, project: '水体维护面积', unit: 'm²', quantity: 0 },
435
+      { serial: 3, project: '建筑维护面积', unit: 'm²', quantity: 0 }
436
+    ].forEach(item => facilityData.push(item))
437
+  }
438
+}
377 439
 
378 440
 // 公园实景图片(虚拟数据)
379 441
 const parkImages = reactive([
@@ -383,16 +445,43 @@ const parkImages = reactive([
383 445
 ])
384 446
 
385 447
 // 视频监控相关数据
386
-const videoList = reactive([
387
-  { name: '绿博园-街道3', time: '2025-10-21 14:28' },
388
-  { name: '绿博园-街道4', time: '2025-10-21 14:28' },
389
-  { name: '绿博园-AI门', time: '2025-10-21 14:28' },
390
-  { name: '绿博园-街道1', time: '2025-10-21 14:28' },
391
-  { name: '绿博园-街道3', time: '2025-10-21 14:28' },
392
-  { name: '绿博园-街道4', time: '2025-10-21 14:28' },
393
-  { name: '绿博园-AI门', time: '2025-10-21 14:28' },
394
-  { name: '绿博园-街道1', time: '2025-10-21 14:28' }
395
-])
448
+const videoList = reactive([])
449
+
450
+// 获取视频监控数据
451
+const fetchVideoData = async (parkId) => {
452
+  try {
453
+    const response = await axios.get(`/tbasedriveinfo/selectDriveinfoList/${parkId}`)
454
+    const data = response.data || []
455
+    
456
+    videoList.splice(0, videoList.length)
457
+    if (data.length > 0) {
458
+      data.forEach(item => {
459
+        videoList.push({
460
+          name: item.name || `摄像头${item.id || Math.random().toString(36).substr(2, 9)}`,
461
+          time: new Date().toISOString().slice(0, 16).replace('T', ' ')
462
+        })
463
+      })
464
+    } else {
465
+      // 如果没有数据,使用模拟数据
466
+      for (let i = 0; i < 8; i++) {
467
+        videoList.push({
468
+          name: `${props.data.name || '公园'}-摄像头${i + 1}`,
469
+          time: new Date().toISOString().slice(0, 16).replace('T', ' ')
470
+        })
471
+      }
472
+    }
473
+  } catch (error) {
474
+    console.error('获取视频监控数据失败:', error)
475
+    // 错误时使用模拟数据
476
+    videoList.splice(0, videoList.length)
477
+    for (let i = 0; i < 8; i++) {
478
+      videoList.push({
479
+        name: `${props.data.name || '公园'}-摄像头${i + 1}`,
480
+        time: new Date().toISOString().slice(0, 16).replace('T', ' ')
481
+      })
482
+    }
483
+  }
484
+}
396 485
 
397 486
 // 视频模态框相关
398 487
 const isVideoModalOpen = ref(false)
@@ -407,17 +496,63 @@ const closeVideoModal = () => {
407 496
 }
408 497
 
409 498
 // 智能发现相关数据
410
-const discoveryData = reactive([
411
-  { discoveryMethod: '视频感知', eventType: '毁坏草坪', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' },
412
-  { discoveryMethod: '大数据发现', eventType: '公园密度过大', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' },
413
-  { discoveryMethod: '视频感知', eventType: '毁坏草坪', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' },
414
-  { discoveryMethod: '大数据发现', eventType: '公园密度过大', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' },
415
-  { discoveryMethod: '视频感知', eventType: '毁坏草坪', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' },
416
-  { discoveryMethod: '大数据发现', eventType: '公园密度过大', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' },
417
-  { discoveryMethod: '视频感知', eventType: '毁坏草坪', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' },
418
-  { discoveryMethod: '大数据发现', eventType: '公园密度过大', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' },
419
-  { discoveryMethod: '视频感知', eventType: '毁坏草坪', pushTime: '2025-09-22 04:24:40', pushDepartment: '园林中心', receiveDepartment: '龙湖源公园', disposalStatus: '完结' }
420
-])
499
+const discoveryData = reactive([])
500
+
501
+// 获取智能发现数据
502
+const fetchDiscoveryData = async (parkId) => {
503
+  try {
504
+    const response = await axios.get(`/woworkorderbase/smartDiscovery/${parkId}`)
505
+    const data = response.data || []
506
+    
507
+    discoveryData.splice(0, discoveryData.length)
508
+    if (data.length > 0) {
509
+      data.forEach(item => {
510
+        discoveryData.push({
511
+          discoveryMethod: item.discoveryMethod || '未知',
512
+          eventType: item.eventType || '未分类事件',
513
+          pushTime: item.pushTime || new Date().toISOString().slice(0, 19).replace('T', ' '),
514
+          pushDepartment: item.pushDepartment || '系统',
515
+          receiveDepartment: item.receiveDepartment || props.data.name || '当前公园',
516
+          disposalStatus: item.disposalStatus || '待处理'
517
+        })
518
+      })
519
+    } else {
520
+      // 如果没有数据,使用模拟数据
521
+      const statuses = ['完结', '处理中', '待处理', '逾期']
522
+      const methods = ['视频感知', '大数据发现', '人工上报']
523
+      const events = ['毁坏草坪', '公园密度过大', '设施损坏', '垃圾堆积']
524
+      
525
+      for (let i = 0; i < 9; i++) {
526
+        discoveryData.push({
527
+          discoveryMethod: methods[Math.floor(Math.random() * methods.length)],
528
+          eventType: events[Math.floor(Math.random() * events.length)],
529
+          pushTime: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 19).replace('T', ' '),
530
+          pushDepartment: '园林中心',
531
+          receiveDepartment: props.data.name || '当前公园',
532
+          disposalStatus: statuses[Math.floor(Math.random() * statuses.length)]
533
+        })
534
+      }
535
+    }
536
+  } catch (error) {
537
+    console.error('获取智能发现数据失败:', error)
538
+    // 错误时使用模拟数据
539
+    discoveryData.splice(0, discoveryData.length)
540
+    const statuses = ['完结', '处理中', '待处理', '逾期']
541
+    const methods = ['视频感知', '大数据发现', '人工上报']
542
+    const events = ['毁坏草坪', '公园密度过大', '设施损坏', '垃圾堆积']
543
+    
544
+    for (let i = 0; i < 9; i++) {
545
+      discoveryData.push({
546
+        discoveryMethod: methods[Math.floor(Math.random() * methods.length)],
547
+        eventType: events[Math.floor(Math.random() * events.length)],
548
+        pushTime: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, 19).replace('T', ' '),
549
+        pushDepartment: '园林中心',
550
+        receiveDepartment: props.data.name || '当前公园',
551
+        disposalStatus: statuses[Math.floor(Math.random() * statuses.length)]
552
+      })
553
+    }
554
+  }
555
+}
421 556
 
422 557
 // 获取处置状态样式类
423 558
 const getDisposalStatusClass = (status) => {
@@ -437,70 +572,191 @@ const getDisposalStatusClass = (status) => {
437 572
 
438 573
 // 人员出勤相关数据
439 574
 const personnelStats = reactive({
440
-  total: 128,
441
-  leave: 29,
442
-  onDuty: 98,
443
-  actual: 97
575
+  total: 0,
576
+  leave: 0,
577
+  onDuty: 0,
578
+  actual: 0,
579
+  dueToArrive: 0
444 580
 })
445 581
 
446
-const attendanceData = reactive([
447
-  { name: '张三', goToWork: '2025-11-05 08:00', offWork: '2025-11-05 16:00', status: '在岗' },
448
-  { name: '李四', goToWork: '-', offWork: '-', status: '请假' },
449
-  { name: '张三', goToWork: '2025-11-05 08:00', offWork: '2025-11-05 16:00', status: '在岗' },
450
-  { name: '张三', goToWork: '2025-11-05 08:00', offWork: '2025-11-05 16:00', status: '在岗' },
451
-  { name: '李四', goToWork: '-', offWork: '-', status: '请假' },
452
-  { name: '张三', goToWork: '2025-11-05 08:00', offWork: '2025-11-05 16:00', status: '在岗' },
453
-  { name: '李四', goToWork: '-', offWork: '-', status: '请假' },
454
-  { name: '张三', goToWork: '2025-11-05 08:00', offWork: '2025-11-05 16:00', status: '在岗' },
455
-  { name: '张三', goToWork: '2025-11-05 08:00', offWork: '2025-11-05 16:00', status: '在岗' }
456
-])
582
+const attendanceData = reactive([])
457 583
 
458 584
 // 设备运维相关数据
459 585
 const equipmentStats = reactive({
460
-  total: 120,
461
-  normal: 95,
462
-  maintaining: 15,
463
-  error: 10
586
+  total: 0,
587
+  normal: 0,
588
+  maintaining: 0,
589
+  error: 0
464 590
 })
465 591
 
466 592
 // 设备统计数据
467
-const equipmentStatistics = reactive([
468
-  { name: '智能照明控制器001', type: '照明设备', location: '东区主路', installTime: '2024-01-15', status: '正常运行' },
469
-  { name: '智能灌溉系统002', type: '灌溉设备', location: '南区草坪', installTime: '2024-02-20', status: '正常运行' },
470
-  { name: '安防摄像头003', type: '安防设备', location: '西区入口', installTime: '2024-03-10', status: '维护中' },
471
-  { name: '智能照明控制器004', type: '照明设备', location: '北区小径', installTime: '2024-01-15', status: '故障' },
472
-  { name: '智能灌溉系统005', type: '灌溉设备', location: '中区花坛', installTime: '2024-02-20', status: '正常运行' },
473
-  { name: '安防摄像头006', type: '安防设备', location: '东区广场', installTime: '2024-03-10', status: '正常运行' },
474
-  { name: '智能照明控制器007', type: '照明设备', location: '西区步道', installTime: '2024-01-15', status: '维护中' },
475
-  { name: '智能灌溉系统008', type: '灌溉设备', location: '北区草坪', installTime: '2024-02-20', status: '正常运行' },
476
-  { name: '安防摄像头009', type: '安防设备', location: '南区出口', installTime: '2024-03-10', status: '故障' }
477
-])
593
+const equipmentStatistics = reactive([])
478 594
 
479 595
 // 设备检查数据
480
-const equipmentInspections = reactive([
481
-  { deviceName: '智能照明控制器001', inspectionTime: '2025-10-20 09:30', inspector: '王工', result: '合格', remark: '设备运行正常' },
482
-  { deviceName: '智能灌溉系统002', inspectionTime: '2025-10-20 10:15', inspector: '李工', result: '合格', remark: '水压正常' },
483
-  { deviceName: '安防摄像头003', inspectionTime: '2025-10-20 11:00', inspector: '张工', result: '不合格', remark: '镜头需要清洁' },
484
-  { deviceName: '智能照明控制器004', inspectionTime: '2025-10-20 14:00', inspector: '王工', result: '不合格', remark: '线路老化' },
485
-  { deviceName: '智能灌溉系统005', inspectionTime: '2025-10-20 15:30', inspector: '李工', result: '合格', remark: '设备运行正常' },
486
-  { deviceName: '安防摄像头006', inspectionTime: '2025-10-21 09:00', inspector: '张工', result: '合格', remark: '画面清晰' },
487
-  { deviceName: '智能照明控制器007', inspectionTime: '2025-10-21 10:30', inspector: '王工', result: '合格', remark: '亮度调节正常' },
488
-  { deviceName: '智能灌溉系统008', inspectionTime: '2025-10-21 11:15', inspector: '李工', result: '合格', remark: '定时功能正常' },
489
-  { deviceName: '安防摄像头009', inspectionTime: '2025-10-21 14:30', inspector: '张工', result: '不合格', remark: '角度需要调整' }
490
-])
596
+const equipmentInspections = reactive([])
491 597
 
492 598
 // 设备预警数据
493
-const equipmentWarnings = reactive([
494
-  { deviceName: '智能照明控制器004', warningTime: '2025-10-22 08:45', warningType: '故障预警', level: '警告', processingStatus: '处理中' },
495
-  { deviceName: '安防摄像头003', warningTime: '2025-10-22 09:30', warningType: '信号异常', level: '提示', processingStatus: '已处理' },
496
-  { deviceName: '安防摄像头009', warningTime: '2025-10-22 10:15', warningType: '画面异常', level: '警告', processingStatus: '待处理' },
497
-  { deviceName: '智能灌溉系统002', warningTime: '2025-10-22 11:00', warningType: '水压不足', level: '提示', processingStatus: '已处理' },
498
-  { deviceName: '智能照明控制器007', warningTime: '2025-10-22 14:20', warningType: '电源不稳', level: '提示', processingStatus: '处理中' },
499
-  { deviceName: '智能灌溉系统005', warningTime: '2025-10-22 15:45', warningType: '流量异常', level: '警告', processingStatus: '待处理' },
500
-  { deviceName: '安防摄像头006', warningTime: '2025-10-23 09:00', warningType: '存储不足', level: '提示', processingStatus: '已处理' },
501
-  { deviceName: '智能照明控制器001', warningTime: '2025-10-23 10:30', warningType: '温度过高', level: '警告', processingStatus: '处理中' },
502
-  { deviceName: '智能灌溉系统008', warningTime: '2025-10-23 11:15', warningType: '阀门异常', level: '紧急', processingStatus: '待处理' }
503
-])
599
+const equipmentWarnings = reactive([])
600
+
601
+// 获取人员出勤和设备运维数据
602
+const fetchAttendanceAndMaintenanceData = async (parkId) => {
603
+  try {
604
+    const response = await axios.get(`/map/polygon-properties/selectMaintenance/2`)
605
+    const data = response.data || {}
606
+    
607
+    // 更新人员出勤数据
608
+    if (data.attendance) {
609
+      // 更新人员统计数据
610
+      personnelStats.total = data.attendance?.dueToArrive || 0
611
+      personnelStats.leave = data.attendance?.leave || 0
612
+      personnelStats.onDuty = data.attendance?.present || 0
613
+      personnelStats.actual = data.attendance?.present || 0
614
+      personnelStats.dueToArrive = data.attendance?.dueToArrive || 0
615
+      
616
+      // 更新出勤详细数据
617
+      attendanceData.splice(0, attendanceData.length)
618
+      const attendanceList = data.attendance.attendance || []
619
+      if (attendanceList.length > 0) {
620
+        attendanceList.forEach(item => {
621
+          attendanceData.push({
622
+            name: item.name || '未知人员',
623
+            goToWork: item.goToWork || '-',
624
+            offWork: item.offWork || '-',
625
+            status: item.status || '未知'
626
+          })
627
+        })
628
+      } else {
629
+        // 模拟数据
630
+        for (let i = 0; i < 9; i++) {
631
+          const isOnDuty = Math.random() > 0.2
632
+          attendanceData.push({
633
+            name: `工作人员${i + 1}`,
634
+            goToWork: isOnDuty ? '2025-11-05 08:00' : '-',
635
+            offWork: isOnDuty ? '2025-11-05 16:00' : '-',
636
+            status: isOnDuty ? '在岗' : '请假'
637
+          })
638
+        }
639
+      }
640
+    }
641
+    
642
+    // 更新设备运维数据
643
+    if (data.statistics && data.statistics.length > 0) {
644
+      // 更新设备统计数据
645
+      const deviceData = data.statistics[0]
646
+      equipmentStats.total = deviceData.total || 0
647
+      equipmentStats.normal = deviceData.normal?.length || 0
648
+      equipmentStats.maintaining = deviceData.maintaining?.length || 0
649
+      equipmentStats.error = deviceData.abnormal?.length || 0
650
+      
651
+      // 更新设备统计详细数据
652
+      equipmentStatistics.splice(0, equipmentStatistics.length)
653
+      const deviceList = deviceData.data || []
654
+      if (deviceList.length > 0) {
655
+        deviceList.forEach(item => {
656
+          equipmentStatistics.push({
657
+            name: item.name || '未知设备',
658
+            type: item.type || '未知类型',
659
+            location: item.location || '未知位置',
660
+            installTime: item.installTime || '-',
661
+            status: item.status || '未知状态'
662
+          })
663
+        })
664
+      }
665
+    }
666
+    
667
+    // 更新设备检查数据
668
+    if (data.maintenance) {
669
+      equipmentInspections.splice(0, equipmentInspections.length)
670
+      data.maintenance.forEach(item => {
671
+        equipmentInspections.push({
672
+          deviceName: item.deviceName || '未知设备',
673
+          inspectionTime: item.inspectionTime || '-',
674
+          inspector: item.inspector || '未知',
675
+          result: item.result || '未知',
676
+          remark: item.remark || ''
677
+        })
678
+      })
679
+    }
680
+    
681
+    // 更新设备预警数据
682
+    if (data.warningOrder) {
683
+      equipmentWarnings.splice(0, equipmentWarnings.length)
684
+      data.warningOrder.forEach(item => {
685
+        equipmentWarnings.push({
686
+          deviceName: item.deviceName || item.fDevicename || '未知设备',
687
+          warningTime: item.warningTime || item.fWarningtime || '-',
688
+          warningType: item.warningType || item.fWarningtype || '未知类型',
689
+          level: item.level || (item.fLevel === 1 ? '紧急' : item.fLevel === 2 ? '警告' : '提示'),
690
+          processingStatus: item.processingStatus || (item.fProcessstatus === 1 ? '已处理' : item.fProcessstatus === 2 ? '处理中' : '待处理')
691
+        })
692
+      })
693
+    }
694
+    
695
+    // 如果没有获取到数据,使用模拟数据
696
+    if (equipmentWarnings.length === 0) {
697
+      const warningTypes = ['故障预警', '信号异常', '画面异常', '水压不足', '电源不稳', '流量异常', '存储不足', '温度过高', '阀门异常']
698
+      const levels = ['紧急', '警告', '提示']
699
+      const statuses = ['已处理', '处理中', '待处理']
700
+      
701
+      for (let i = 0; i < 9; i++) {
702
+        equipmentWarnings.push({
703
+          deviceName: `智能设备${i + 1}`,
704
+          warningTime: new Date(Date.now() - Math.random() * 24 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' '),
705
+          warningType: warningTypes[Math.floor(Math.random() * warningTypes.length)],
706
+          level: levels[Math.floor(Math.random() * levels.length)],
707
+          processingStatus: statuses[Math.floor(Math.random() * statuses.length)]
708
+        })
709
+      }
710
+    }
711
+    
712
+  } catch (error) {
713
+    console.error('获取人员出勤和设备运维数据失败:', error)
714
+    // 错误时使用模拟数据
715
+    generateMockData()
716
+  }
717
+}
718
+
719
+// 生成模拟数据
720
+const generateMockData = () => {
721
+  // 模拟人员出勤数据
722
+  personnelStats.total = 128
723
+  personnelStats.leave = 29
724
+  personnelStats.onDuty = 98
725
+  personnelStats.actual = 97
726
+  
727
+  attendanceData.splice(0, attendanceData.length)
728
+  for (let i = 0; i < 9; i++) {
729
+    const isOnDuty = Math.random() > 0.2
730
+    attendanceData.push({
731
+      name: `工作人员${i + 1}`,
732
+      goToWork: isOnDuty ? '2025-11-05 08:00' : '-',
733
+      offWork: isOnDuty ? '2025-11-05 16:00' : '-',
734
+      status: isOnDuty ? '在岗' : '请假'
735
+    })
736
+  }
737
+  
738
+  // 模拟设备数据
739
+  equipmentStats.total = 120
740
+  equipmentStats.normal = 95
741
+  equipmentStats.maintaining = 15
742
+  equipmentStats.error = 10
743
+  
744
+  // 模拟设备预警数据
745
+  equipmentWarnings.splice(0, equipmentWarnings.length)
746
+  const warningTypes = ['故障预警', '信号异常', '画面异常', '水压不足', '电源不稳', '流量异常', '存储不足', '温度过高', '阀门异常']
747
+  const levels = ['紧急', '警告', '提示']
748
+  const statuses = ['已处理', '处理中', '待处理']
749
+  
750
+  for (let i = 0; i < 9; i++) {
751
+    equipmentWarnings.push({
752
+      deviceName: `智能设备${i + 1}`,
753
+      warningTime: new Date(Date.now() - Math.random() * 24 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' '),
754
+      warningType: warningTypes[Math.floor(Math.random() * warningTypes.length)],
755
+      level: levels[Math.floor(Math.random() * levels.length)],
756
+      processingStatus: statuses[Math.floor(Math.random() * statuses.length)]
757
+    })
758
+  }
759
+}
504 760
 
505 761
 // 获取设备状态样式类
506 762
 const getDeviceStatusClass = (status) => {

+ 5 - 2
bigScreen3/src/views/dashboard/cpns/ProjectOverview.vue

@@ -16,7 +16,7 @@
16 16
       :modal="false"
17 17
       style="background: rgba(0, 20, 40, 0.98);"
18 18
     >
19
-      <InfoPopup :data="dialogData" />
19
+      <InfoPopup :data="dialogData"  />
20 20
     </el-dialog>
21 21
   </div>
22 22
 </template>
@@ -54,7 +54,8 @@ const fetchParkList = async () => {
54 54
           district: park.fStreet || '未知区域',
55 55
           area: park.fArea || 0,
56 56
           level: park.fLayer || '未知',
57
-          description: park.description || '暂无描述'
57
+          description: park.description || '暂无描述',
58
+          ...park
58 59
         }
59 60
       })
60 61
     } else {
@@ -383,6 +384,7 @@ const addParkMarkers = () => {
383 384
     // })
384 385
 
385 386
     marker.on('click', (e) => {
387
+      console.log('点击公园:', e.target.getExtData())
386 388
       showParkDialog(e.target.getExtData())
387 389
     })
388 390
 
@@ -394,6 +396,7 @@ const addParkMarkers = () => {
394 396
 // 显示公园信息对话框
395 397
 const showParkDialog = (parkData) => {
396 398
   dialogData.value = parkData
399
+  
397 400
   dialogVisible.value = true
398 401
 }
399 402
 

+ 19 - 1
bigScreen3/src/views/dashboard/cpns/TypicalCase.vue

@@ -6,6 +6,7 @@
6 6
       v-for="(item, index) in caseList" 
7 7
       :key="index"
8 8
       class="case-item"
9
+      @click="handleClick(item)"
9 10
       >
10 11
       <div class="case-image">
11 12
         <img :src="item.image" :alt="item.title" />
@@ -18,12 +19,18 @@
18 19
     </div>
19 20
     </div>
20 21
   </div>
22
+  <!-- 工单详情组件 -->
23
+  <WorkOrderDetail 
24
+    :visible="showDetail" 
25
+    :work-order-id="selectedWorkOrderId"
26
+    @close="() => { showDetail = false }"/>
21 27
 </template>
22 28
 
23 29
 <script setup>
24 30
 import { ref, onMounted, watch } from 'vue'
25 31
 import caseImage from '@/assets/6.1.png'
26 32
 import { getPageListData } from '@/api/index.js'
33
+import WorkOrderDetail from '@/views/dashboard/cpns/WorkOrderDetail.vue'
27 34
 
28 35
 // 定义 props
29 36
 const props = defineProps({
@@ -35,6 +42,15 @@ const props = defineProps({
35 42
 watch(() => props.selectedProjectId, () => {
36 43
   fetchTypicalCaseData()
37 44
 })  
45
+
46
+// 定义状态变量
47
+const showDetail = ref(false)
48
+const selectedWorkOrderId = ref('')
49
+const handleClick = (item) => {
50
+  selectedWorkOrderId.value = item.fWorkorderid
51
+  console.log('selectedWorkOrderId.value', selectedWorkOrderId.value)
52
+  showDetail.value = true
53
+}
38 54
 // 响应式数据
39 55
 const caseList = ref([
40 56
   {
@@ -67,8 +83,10 @@ const fetchTypicalCaseData = async () => {
67 83
         title: item.checkusername || '典型案例',
68 84
         description: item.fContent || '暂无描述',
69 85
         time: item.fAskfinishtime || new Date().toLocaleString(),
70
-        image: item.imageUrl ? item.imageUrl : caseImage // 如果没有图片则使用默认图片
86
+        image: item.imageUrl ? item.imageUrl : caseImage, // 如果没有图片则使用默认图片  selectsworkTypicalList
87
+        fWorkorderid: item.fWorkorderid || ''
71 88
       }))
89
+
72 90
     }
73 91
   } catch (error) {
74 92
     console.error('获取典型案例数据失败:', error)

+ 23 - 81
bigScreen3/src/views/dashboard/cpns/WisdomDiscovery.vue

@@ -1,31 +1,31 @@
1 1
 <template>
2 2
   <div class="wisdom-discovery">
3 3
     <!-- 工单列表弹框 -->
4
-    <el-dialog
5
-      v-model="workOrderDialogVisible"
6
-      title="工单列表"
4
+    <!-- <el-dialog
5
+      v-model="workOrderListVisible"
6
+      title=""
7 7
       :modal="false"
8 8
       width="80%"
9 9
       class="work-order-dialog"
10 10
       :border="false"
11
-      
12
-    >
13
-    <EquipmentDetail
14
-      :visible="workOrderDialogVisible"
15
-      :options="{fWorkordertypeid: 4}"
16
-      @close="workOrderDialogVisible = false"
17
-    />
18
-    <!-- <work-order-list
19
-      :visible="workOrderDialogVisible"
11
+    > -->
12
+    <!-- <EarlyWarningDetail
13
+      :visible="workOrderListVisible"
20 14
       :options="{fWorkordertypeid: 4}"
21
-      @close="workOrderDialogVisible = false"
15
+      @close="workOrderListVisible = false"
22 16
     /> -->
23
-    <!-- <work-order-detail
24
-      :visible="workOrderDialogVisible"
17
+    <!-- <EquipmentDetail
18
+      :visible="workOrderListVisible"
25 19
       :options="{fWorkordertypeid: 4}"
26
-      @close="workOrderDialogVisible = false"
20
+      @close="workOrderListVisible = false"
27 21
     /> -->
28
-    </el-dialog>
22
+    <work-order-list
23
+      :visible="workOrderListVisible"
24
+      :options="{fWorkordertypeid: 4, fSguserid: selectedProjectId}"
25
+      @close="workOrderListVisible = false"
26
+    />
27
+    
28
+    <!-- </el-dialog> -->
29 29
     <!-- 2x2 网格布局 -->
30 30
     <div class="discovery-grid">
31 31
       <!-- 左上:累计发现 -->
@@ -138,12 +138,7 @@ const discoveryStats = ref({
138 138
   bigDataAnalysis: 1236
139 139
 })
140 140
 
141
-// 工单列表弹框相关数据
142
-const workOrderDialogVisible = ref(false)
143
-const workOrderList = ref([])
144
-const currentPage = ref(1)
145
-const pageSize = ref(10)
146
-const totalWorkOrders = ref(0)
141
+
147 142
 
148 143
 // 进度条分段数量(总共显示的分段数)
149 144
 const totalSegments = 20
@@ -189,70 +184,17 @@ const unresolvedSegments = computed(() => {
189 184
   return Array(totalSegments).fill(false).map((_, i) => i < filledCount)
190 185
 })
191 186
 
187
+const workOrderListVisible = ref(false)
192 188
 // 处理累计发现点击事件
193 189
 const handleTotalDiscoveryClick = async () => {
190
+  console.log('props.selectedProjectId', props.selectedProjectId)
194 191
   // 重置为第一页
195
-  currentPage.value = 1
196
-  workOrderDialogVisible.value = true
197
-  await fetchWorkOrderList()
192
+  // currentPage.value = 1
193
+  workOrderListVisible.value = true  
194
+  // await fetchWorkOrderList()
198 195
 }
199 196
 
200
-// 分页大小变化处理
201
-const handleSizeChange = (newSize) => {
202
-  pageSize.value = newSize
203
-  fetchWorkOrderList()
204
-}
205 197
 
206
-// 页码变化处理
207
-const handleCurrentChange = (newPage) => {
208
-  currentPage.value = newPage
209
-  fetchWorkOrderList()
210
-}
211
-
212
-// 获取工单列表数据
213
-const fetchWorkOrderList = async () => {
214
-  try {
215
-      const response = await createPageData('/woworkorderbase/searchWorkGState?stateid=0',{
216
-        current: currentPage.value,
217
-        size: pageSize.value,
218
-        deptType: 1,
219
-        fCode: "",
220
-        fOvertimes: 0,
221
-        fSguserid: props.selectedProjectId || '',
222
-        fSgusername: "",
223
-        fStateid: 0,
224
-        fWorkorderid: 0,
225
-        fWorkordertypeid: 0,
226
-      })
227
-      console.log('response', response)
228
-      if (response && response.status === 200 && response.data.data) {
229
-        // 转换数据格式,确保与表格列名匹配
230
-        workOrderList.value = (response.data.data || []).map(item => ({
231
-          discoverMethod: item.discoverMethod || '视频捕捉',
232
-          typeName: item.typeName || '客流预警',
233
-          pushtime: item.pushtime || '',
234
-          pushDept: item.pushDept || '园林中心',
235
-          receiveDept: item.receiveDept || '龙源湖公园',
236
-          fState: item.fState,
237
-          statusText: item.status === 1 ? '完结' : '待处理'
238
-        }))
239
-        // 设置总数
240
-        totalWorkOrders.value = response.data.total || 0
241
-      }
242
-    } catch (error) {
243
-      console.error('获取工单列表失败:', error)
244
-      // 提供模拟数据
245
-      workOrderList.value = []
246
-      totalWorkOrders.value = 0
247
-    }
248
-}
249
-
250
-// 格式化状态显示
251
-const formatStatus = (row) => {
252
-  return row.statusText === '完结' 
253
-    ? '<span style="color: #00FF88;">完结</span>' 
254
-    : '<span style="color: #FF6B6B;">待处理</span>'
255
-}
256 198
 </script>
257 199
 
258 200
 <style lang="scss" scoped>

+ 41 - 82
bigScreen3/src/views/dashboard/cpns/WorkOrderDetail.vue

@@ -1,5 +1,5 @@
1 1
 <template>
2
-   <div v-if="visible" class="work-order-detail-modal" @click.self="handleClose">
2
+   <div v-if="visible && workOrderData.tWoWorkorderbase" class="work-order-detail-modal" @click.self="handleClose">
3 3
     <div class="work-order-detail-content" style="border-color: #056CC3">
4 4
       <div class="work-order-detail-header">
5 5
         <h2>工单详情</h2>
@@ -23,35 +23,35 @@
23 23
             <div class="info-card" style="border-color: #056CC3">
24 24
               <div class="info-item">
25 25
                 <span class="info-label">预警类型:</span>
26
-                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.basicInfo.warningType }}</span>
26
+                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.tWoWorkorderbase.riskclassifyname }}</span>
27 27
               </div>
28 28
               <div class="info-item">
29 29
                 <span class="info-label">处理单位:</span>
30
-                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.basicInfo.handleUnit }}</span>
30
+                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.tWoWorkorderbase.distributedept }}</span>
31 31
               </div>
32 32
               <div class="info-item">
33 33
                 <span class="info-label">发送单位:</span>
34
-                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.basicInfo.sendUnit }}</span>
34
+                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.tWoWorkorderbase.checkdept }}</span>
35 35
               </div>
36 36
               <div class="info-item">
37 37
                 <span class="info-label">紧急程度:</span>
38
-                <span class="info-value emergency-level" :style="{ color: workOrderData.basicInfo.emergencyLevel === '紧急' ? 'red' : '#056CC3', textAlign: 'left' }">{{ workOrderData.basicInfo.emergencyLevel }}</span>
38
+                <span class="info-value emergency-level" :style="{ color: workOrderData.tWoWorkorderbase.fLevelname === '紧急' ? 'red' : '#056CC3', textAlign: 'left' }">{{ workOrderData.tWoWorkorderbase.fLevelname }}</span>
39 39
               </div>
40 40
               <div class="info-item">
41 41
                 <span class="info-label">要求时间:</span>
42
-                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.basicInfo.requireCompleteTime }}</span>
42
+                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.tWoWorkorderbase.fAskfinishtime }}</span>
43 43
               </div>
44 44
               <div class="info-item">
45 45
                 <span class="info-label">发起人:</span>
46
-                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.basicInfo.initiator }}</span>
46
+                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.tWoWorkorderbase.fCreatename }}</span>
47 47
               </div>
48 48
               <div class="info-item">
49 49
                 <span class="info-label">时间:</span>
50
-                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.basicInfo.time }}</span>
50
+                <span class="info-value" style="text-align: left; color: #056CC3">{{ workOrderData.tWoWorkorderbase.fCreatedate }}</span>
51 51
               </div>
52 52
               <div class="info-item">
53 53
                 <span class="info-label">问题描述:</span>
54
-                <span class="info-value problem-desc" style="text-align: left; color: #056CC3">{{ workOrderData.basicInfo.problemDesc }}</span>
54
+                <span class="info-value problem-desc" style="text-align: left; color: #056CC3">{{ workOrderData.tWoWorkorderbase.fContent }}</span>
55 55
               </div>
56 56
             </div>
57 57
           </div>
@@ -63,10 +63,10 @@
63 63
               <!-- 步骤指示器 -->
64 64
               <div class="process-steps">
65 65
                 <div 
66
-                  v-for="(step, index) in workOrderData.processInfo.steps" 
66
+                  v-for="(step, index) in workOrderDataSteps" 
67 67
                   :key="index" 
68 68
                   class="step-item" 
69
-                  :class="{ active: index < workOrderData.processInfo.currentStep }"
69
+                  :class="{ active: index < currentStep }"
70 70
                 >
71 71
                   <span class="step-name">{{ step }}</span>
72 72
                 </div>
@@ -76,14 +76,14 @@
76 76
               <div class="progress-bar">
77 77
                 <div 
78 78
                   class="progress-fill" 
79
-                  :style="{ width: (workOrderData.processInfo.currentStep / workOrderData.processInfo.steps.length) * 100 + '%' }"
79
+                  :style="{ width: (currentStep/ workOrderDataSteps.length) * 100 + '%' }"
80 80
                 ></div>
81 81
               </div>
82 82
               
83 83
               <!-- 处理记录时间线 -->
84 84
               <div class="process-records">
85 85
                 <div 
86
-                  v-for="record in workOrderData.processInfo.records" 
86
+                  v-for="record in workOrderData.tWoWorkordertaskdealinfos" 
87 87
                   :key="record.id" 
88 88
                   class="timeline-item"
89 89
                 >
@@ -92,19 +92,20 @@
92 92
                   <div class="timeline-content">
93 93
                     <div class="timeline-header">
94 94
                       <div class="timeline-info">
95
-                        <span class="timeline-person">处理人: {{ record.person }}</span>
96
-                        <span class="timeline-time">时间: {{ record.time }}</span>
95
+                        <span class="timeline-person">处理人: {{ record.fString1 }}</span>
96
+                        <!-- <span class="timeline-status" :class="{'danger': record.fWorkorderstateid==1, 'success': record.fWorkorderstateid==2}">{{record.fString2}}</span> -->
97
+                        <span class="timeline-time">时间: {{ record.fOptdate }}</span>
97 98
                       </div>
98
-                      <button 
99
+                      <e-button 
99 100
                         class="timeline-btn" 
100
-                        :class="{ 'process-btn': record.isProcess }"
101
+                        :class="{'danger': record.fWorkorderstateid==1, 'success': record.fWorkorderstateid==2}"
101 102
                         @click="handleRecordAction(record)"
102 103
                       >
103
-                        {{ record.action }}
104
-                      </button>
104
+                        {{ record.fString2 }}
105
+                      </e-button>
105 106
                     </div>
106 107
                     <div class="timeline-body">
107
-                      {{ record.content }}
108
+                      {{ record.fRemark }}
108 109
                     </div>
109 110
                   </div>
110 111
                 </div>
@@ -141,62 +142,9 @@ const props = defineProps({
141 142
 const emit = defineEmits(['update:visible', 'close'])
142 143
 
143 144
 // 工单详情数据
144
-const workOrderData = ref({
145
-  // 左侧基本信息
146
-  basicInfo: {
147
-    warningType: '物联网知',
148
-    handleUnit: '龙渊湖公园',
149
-    sendUnit: '绿化中心',
150
-    emergencyLevel: '一般',
151
-    requireCompleteTime: '2025-09-23 02:30:01',
152
-    initiator: '自动预警',
153
-    time: '2025-09-23 02:30:01',
154
-    problemDesc: '龙渊湖公园在2025-09-23 00:00:00监测到噪音预警,音量大于正常分贝!'
155
-  },
156
-  // 右侧处理流程
157
-  processInfo: {
158
-    steps: ['预警', '派单', '处置', '完结'],
159
-    currentStep: 4,
160
-    records: [
161
-      {
162
-        id: 1,
163
-        check: true,
164
-        person: '自动预警',
165
-        time: '2025-09-23 02:30:01',
166
-        action: '预置',
167
-        content: '龙渊湖公园在2025-09-23 00:00:00监测到噪音设备提前打开,音量远远大于正常分贝!',
168
-        isProcess: false
169
-      },
170
-      {
171
-        id: 2,
172
-        check: true,
173
-        person: '自动派单',
174
-        time: '2025-09-23 03:30:01',
175
-        action: '派单',
176
-        content: '龙渊湖公园在2025-09-23 00:00:00监测到噪音设备提前打开,音量远远大于正常分贝!',
177
-        isProcess: false
178
-      },
179
-      {
180
-        id: 3,
181
-        check: true,
182
-        person: '龙渊湖公园',
183
-        time: '2025-09-23 03:30:01',
184
-        action: '派单',
185
-        content: '接收: 园长分派园内人员上前查看现场!',
186
-        isProcess: false
187
-      },
188
-      {
189
-        id: 4,
190
-        check: true,
191
-        person: '龙渊湖公园',
192
-        time: '2025-09-23 03:30:01',
193
-        action: '处置',
194
-        content: '处置: 现场与音箱负责人交涉,降低音量,以免扰民!',
195
-        isProcess: true
196
-      }
197
-    ]
198
-  }
199
-})
145
+const workOrderDataSteps = ref(['预警', '派单', '处置', '完结'])
146
+const currentStep = ref(4)
147
+const workOrderData = ref({})
200 148
 
201 149
 // 加载状态
202 150
 const loading = ref(false)
@@ -212,16 +160,16 @@ const fetchWorkOrderDetail = async () => {
212 160
   
213 161
   try {
214 162
     const data = {
215
-      ...props.options,
216
-      id: props.workOrderId
163
+      fWorkorderid: props.workOrderId
217 164
     }
218 165
     
219 166
     // 这里使用模拟接口,实际项目中需要替换为真实的工单详情接口
220
-    const response = await createPageData(`/woworkorderbase/getWorkOrderDetail`, data)
167
+    const response = await createPageData(`/workorderbaseApp/selectAppWorkorderbaseById`, data)
221 168
     
222 169
     if (response && response.status === 200 && response.data) {
170
+      console.log('response.data', response.data)
223 171
       // 处理返回的数据,映射到workOrderData结构
224
-      // workOrderData.value = mapResponseToWorkOrderData(response.data)
172
+      workOrderData.value = response.data
225 173
     } else {
226 174
       throw new Error('获取工单详情失败')
227 175
     }
@@ -229,6 +177,14 @@ const fetchWorkOrderDetail = async () => {
229 177
     console.error('获取工单详情出错:', err)
230 178
     error.value = err.message || '获取数据失败,请稍后重试'
231 179
     // 保留默认数据以便展示
180
+    workOrderData.value = {
181
+      basicInfo: {},
182
+      steps: [],
183
+      processInfo: {
184
+        currentStep: 0,
185
+        records: []
186
+      }
187
+    }
232 188
   } finally {
233 189
     loading.value = false
234 190
   }
@@ -250,6 +206,7 @@ function handleRecordAction(record) {
250 206
 // 监听visible变化,当显示时获取数据
251 207
 watch(() => props.visible, (newVal) => {
252 208
   if (newVal) {
209
+    console.log('props.workOrderId', props.workOrderId)
253 210
     fetchWorkOrderDetail()
254 211
   }
255 212
 })
@@ -257,6 +214,7 @@ watch(() => props.visible, (newVal) => {
257 214
 // 监听工单ID变化,重新获取数据
258 215
 watch(() => props.workOrderId, (newId) => {
259 216
   if (newId && props.visible) {
217
+    console.log('newId', newId)
260 218
     fetchWorkOrderDetail()
261 219
   }
262 220
 })
@@ -264,7 +222,7 @@ watch(() => props.workOrderId, (newId) => {
264 222
 // 组件挂载时的逻辑
265 223
 onMounted(() => {
266 224
   if (props.visible) {
267
-    fetchWorkOrderDetail()
225
+    // fetchWorkOrderDetail()
268 226
   }
269 227
 })
270 228
 
@@ -281,7 +239,7 @@ defineExpose({
281 239
   position: fixed;
282 240
   z-index: 9999;
283 241
   inset: 0;
284
-  background: rgba(0, 10, 30, 0.8);
242
+  /* background: rgba(0, 10, 30, 0.8); */
285 243
   display: flex;
286 244
   align-items: center;
287 245
   justify-content: center;
@@ -335,6 +293,7 @@ defineExpose({
335 293
   border-radius: 4px;
336 294
   cursor: pointer;
337 295
   transition: all 0.3s ease;
296
+  padding: 0;
338 297
 }
339 298
 
340 299
 .work-order-detail-close:hover {

+ 274 - 72
bigScreen3/src/views/dashboard/cpns/WorkOrderList.vue

@@ -10,12 +10,12 @@
10 10
         <div class="stats-container">
11 11
           <div class="stat-item">
12 12
             <span class="stat-label">累计推送:</span>
13
-            <span class="stat-value">1,092</span>
13
+            <span class="stat-value">{{ totalCountFormatted }}</span>
14 14
             <span class="stat-unit">(起)</span>
15 15
           </div>
16 16
           <div class="stat-item">
17
-            <span class="stat-label">超前预警:</span>
18
-            <span class="stat-value">2</span>
17
+            <span class="stat-label">超期数量:</span>
18
+            <span class="stat-value overtime">{{ overtimeCount }}</span>
19 19
             <span class="stat-unit">(起)</span>
20 20
           </div>
21 21
         </div>
@@ -35,14 +35,17 @@
35 35
               </tr>
36 36
             </thead>
37 37
             <tbody>
38
-              <tr v-for="(item, index) in discoveryData" :key="index" :class="{ 'odd-row': index % 2 === 0, 'even-row': index % 2 === 1 }">
39
-                <td>{{ item.discoveryMethod }}</td>
40
-                <td>{{ item.eventType }}</td>
38
+              <tr v-for="(item, index) in warningList" :key="index" :class="{ 'odd-row': index % 2 === 0, 'even-row': index % 2 === 1 }" @dblclick="handleRowDoubleClick(item)">
39
+                <td>{{ item.fWorkordertype }}</td>
40
+                <td>{{ item.riskclassifyname }}</td>
41 41
                 <td>{{ item.pushTime }}</td>
42
-                <td>{{ item.pushDepartment }}</td>
43
-                <td>{{ item.receiveTime }}</td>
44
-                <td>{{ item.receiveDepartment }}</td>
45
-                <td class="status-completed">{{ item.status }}</td>
42
+                <td>{{ item.checkdept }}</td>
43
+                <td>{{ item.fEndtime }}</td>
44
+                <td>{{ item.distributedept }}</td>
45
+                <td :class="getStatusClass(item.status)">{{ item.status }}</td>
46
+              </tr>
47
+              <tr v-if="warningList.length === 0">
48
+                <td colspan="7" class="no-data">暂无数据</td>
46 49
               </tr>
47 50
             </tbody>
48 51
           </table>
@@ -53,24 +56,50 @@
53 56
           <div class="pagination-info">
54 57
             <span>共 {{ total }} 条数据</span>
55 58
           </div>
56
-          <div class="pagination-controls">
57
-            <button class="page-btn">上一页</button>
58
-            <button class="page-btn active">1</button>
59
-            <button class="page-btn">2</button>
60
-            <button class="page-btn">3</button>
61
-            <span>...</span>
62
-            <button class="page-btn">10</button>
63
-            <button class="page-btn">下一页</button>
59
+          <div class="pagination-controls" v-if="total > 0">
60
+            <button 
61
+              class="page-btn" 
62
+              :disabled="page.current === 1"
63
+              @click="handlePrevPage"
64
+            >
65
+              上一页
66
+            </button>
67
+            <template v-for="pageNum in displayedPages" :key="pageNum">
68
+              <span v-if="pageNum === '...'" class="pagination-ellipsis">...</span>
69
+              <button 
70
+                v-else 
71
+                class="page-btn"
72
+                :class="{ active: page.current === pageNum }"
73
+                @click="handlePageChange(pageNum)"
74
+              >
75
+                {{ pageNum }}
76
+              </button>
77
+            </template>
78
+            <button 
79
+              class="page-btn"
80
+              :disabled="page.current >= totalPages"
81
+              @click="handleNextPage"
82
+            >
83
+              下一页
84
+            </button>
64 85
           </div>
65 86
         </div>
66 87
       </div>
67 88
     </div>
68 89
   </div>
90
+  
91
+  <!-- 工单详情组件 -->
92
+  <WorkOrderDetail 
93
+    :visible="showDetail" 
94
+    :work-order-id="selectedWorkOrderId"
95
+    @close="() => { showDetail = false; emit('update:visible', true) }"
96
+  />
69 97
 </template>
70 98
 
71 99
 <script setup>
72
-import { ref, onMounted } from 'vue'
73
-import { getPageListData,createPageData } from '@/api/index'
100
+import { ref, computed, watch, onMounted } from 'vue'
101
+import { getPageListData, createPageData } from '@/api/index'
102
+import WorkOrderDetail from './WorkOrderDetail.vue'
74 103
 
75 104
 // 定义 props
76 105
 const props = defineProps({
@@ -80,94 +109,224 @@ const props = defineProps({
80 109
   },
81 110
   options: {
82 111
     type: Object,
83
-    default: {},
112
+    default: () => ({}),
84 113
   }
85 114
 })
86 115
 
116
+// 定义响应式数据
87 117
 const page = ref({
88 118
   current: 1,
89 119
   size: 10,
90 120
 });
91 121
 
92 122
 const total = ref(0);
123
+const warningList = ref([])
124
+const totalCount = ref(0);
125
+const overtimeCount = ref(0); // 超期数量
126
+const showDetail = ref(false);
127
+const selectedWorkOrderId = ref('');
93 128
 
94 129
 // 定义事件
95 130
 const emit = defineEmits(['update:visible', 'close'])
96 131
 
97
-// 智能发现数据
98
-const discoveryData = ref([])
132
+// 计算属性:总页数
133
+const totalPages = computed(() => {
134
+  return Math.ceil(total.value / page.value.size) || 1
135
+})
136
+
137
+// 计算属性:显示的页码
138
+const displayedPages = computed(() => {
139
+  const pages = []
140
+  const current = page.value.current
141
+  const total = totalPages.value
142
+  
143
+  // 如果总页数小于等于5,直接显示所有页码
144
+  if (total <= 5) {
145
+    for (let i = 1; i <= total; i++) {
146
+      pages.push(i)
147
+    }
148
+  } else {
149
+    // 显示当前页附近的页码,最多显示5个
150
+    if (current <= 3) {
151
+      // 当前页在前3页,显示1-5
152
+      for (let i = 1; i <= 5; i++) {
153
+        pages.push(i)
154
+      }
155
+      pages.push('...')
156
+      pages.push(total)
157
+    } else if (current >= total - 2) {
158
+      // 当前页在后3页,显示最后5个
159
+      pages.push(1)
160
+      pages.push('...')
161
+      for (let i = total - 4; i <= total; i++) {
162
+        pages.push(i)
163
+      }
164
+    } else {
165
+      // 当前页在中间,显示当前页前后各2个
166
+      pages.push(1)
167
+      pages.push('...')
168
+      for (let i = current - 2; i <= current + 2; i++) {
169
+        pages.push(i)
170
+      }
171
+      pages.push('...')
172
+      pages.push(total)
173
+    }
174
+  }
175
+  
176
+  return pages
177
+})
178
+
179
+// 计算属性:格式化后的总数
180
+const totalCountFormatted = computed(() => {
181
+  return totalCount.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
182
+})
183
+
184
+// 获取状态样式类
185
+const getStatusClass = (status) => {
186
+  if (status.includes('已处置')) return 'status-completed'
187
+  if (status.includes('处理中')) return 'status-processing'
188
+  if (status.includes('未处置')) return 'status-pending'
189
+  if (status.includes('超期')) return 'status-overtime'
190
+  return 'status-unknown'
191
+}
99 192
 
100 193
 // 获取预警数据
101 194
 const fetchWarningData = async () => {
102 195
   try {
103
-    const data ={
196
+    const data = {
104 197
       ...props.options,
198
+      deptType: 1,
199
+      fCode: "",
200
+      fOvertimes: 0, // 普通列表查询
201
+      fSgusername: "",
202
+      fStateid: 0,
203
+      fWorkorderid: 0,
105 204
       current: page.value.current,
106 205
       size: page.value.size,
107 206
     };
108
-    const response = await createPageData(`/woworkorderbase/searchWorkzngz`, data)
207
+    
208
+    const response = await createPageData(`/woworkorderbase/searchWorkGState`, data)
109 209
     if (response && response.status === 200) {
110
-      // 处理汇总数据
111
-      if (response.data.type && Array.isArray(response.data.type)) {
112
-        // 确保数据长度足够(重复显示如果不够)
113
-        let processedSummary = [...response.data.type]
114
-        while (processedSummary.length < 6) {
115
-          processedSummary = [...processedSummary, ...response.data.type]
116
-        }
117
-        summaryList.value = processedSummary.slice(0, 6)
118
-      } else {
119
-        // 如果没有汇总数据,使用默认汇总数据
120
-        useDefaultSummaryData()
121
-      }
210
+      // 更新总数量
211
+      total.value = response.data.total || 0;
212
+      totalCount.value = response.data.total || 0;
122 213
       
123 214
       // 处理预警列表数据
124
-      if (response.data.workOrderList && Array.isArray(response.data.workOrderList) && response.data.workOrderList.length > 0) {
125
-        warningList.value = response.data.workOrderList.map(item => {
126
-          // 处理预警等级映射
127
-          let level = 'medium'
128
-          let levelText = '中'
129
-          
130
-          if (item.level === 'high' || item.levelText === '高' || item.level === '1') {
131
-            level = 'high'
132
-            levelText = '高'
133
-          } else if (item.level === 'low' || item.levelText === '低' || item.level === '3') {
134
-            level = 'low'
135
-            levelText = '低'
136
-          }
137
-          
138
-          return {
139
-            type: item.type || item.warningType || '未知预警',
140
-            location: item.location || item.address || '未知位置',
141
-            level: level,
142
-            levelText: levelText,
143
-            time: item.time || item.createTime || formatDate(new Date())
144
-          }
145
-        })
215
+      if (response.data.data && Array.isArray(response.data.data)) {
216
+        warningList.value = response.data.data.map(item => ({
217
+          ...item,
218
+          fWorkordertype: item.fWorkordertype || '未知类型',
219
+          riskclassifyname: item.riskclassifyname || '未知事件',
220
+          pushTime: formatDateTime(item.pushtime) || '-',
221
+          checkdept: item.checkdept || '-',
222
+          fEndtime: formatDateTime(item.fEndtime) || '-',
223
+          distributedept: item.distributedept || '-',
224
+          status: item.fState || '未知状态',
225
+        }))
146 226
       } else {
147
-        // 如果没有预警列表数据,使用默认预警数据
148
-        useDefaultWarningData()
227
+        warningList.value = []
149 228
       }
150
-      
151
-    } else {
152
-      console.warn('接口返回数据格式不正确,使用默认数据')
153
-      useDefaultData()
154 229
     }
155 230
   } catch (error) {
156 231
     console.error('获取预警数据失败:', error)
157
-    useDefaultData()
232
+    warningList.value = []
233
+  }
234
+}
235
+
236
+// 获取超期数量
237
+const fetchOvertimeCount = async () => {
238
+  try {
239
+    const data = {
240
+      ...props.options,
241
+      deptType: 1,
242
+      fCode: "",
243
+      fOvertimes: 1, // 设置为1获取超期数量
244
+      current: 1,
245
+      size: 1,
246
+    };
247
+    
248
+    const response = await createPageData(`/woworkorderbase/searchWorkGState`, data)
249
+    if (response && response.status === 200) {
250
+      overtimeCount.value = response.data.total || 0
251
+    }
252
+  } catch (error) {
253
+    console.error('获取超期数量失败:', error)
254
+  }
255
+}
256
+
257
+// 格式化日期时间
258
+const formatDateTime = (dateTime) => {
259
+  if (!dateTime) return ''
260
+  // 假设dateTime已经是格式化好的字符串,如果需要其他格式可以在这里处理
261
+  return dateTime
262
+}
263
+
264
+// 分页处理函数
265
+const handlePageChange = (pageNum) => {
266
+  if (pageNum !== page.value.current) {
267
+    page.value.current = pageNum
268
+    fetchWarningData()
269
+  }
270
+}
271
+
272
+const handlePrevPage = () => {
273
+  if (page.value.current > 1) {
274
+    page.value.current--
275
+    fetchWarningData()
276
+  }
277
+}
278
+
279
+const handleNextPage = () => {
280
+  if (page.value.current < totalPages.value) {
281
+    page.value.current++
282
+    fetchWarningData()
158 283
   }
159 284
 }
160 285
 
161 286
 // 关闭弹框
162
-function handleClose() {
287
+const handleClose = () => {
163 288
   emit('update:visible', false)
164 289
   emit('close')
165 290
 }
166 291
 
167
-// 组件挂载时获取数据
168
-onMounted(() => {
169
-  fetchWarningData()
170
-})
292
+// 处理行双击事件
293
+const handleRowDoubleClick = (item) => {
294
+  // 隐藏当前组件
295
+  emit('update:visible', false)
296
+  // 设置选中的工单ID并显示详情组件
297
+  selectedWorkOrderId.value = item.fWorkorderid || ''
298
+  showDetail.value = true
299
+}
300
+
301
+// 监听visible变化,当显示时重新获取数据
302
+watch(
303
+  () => props.visible,
304
+  (newVal) => {
305
+    if (newVal) {
306
+      // 重置页码
307
+      console.log('visible变化:', props.visible)
308
+      page.value.current = 1
309
+      // 获取数据
310
+      fetchWarningData()
311
+      fetchOvertimeCount()
312
+    }
313
+  },
314
+  { immediate: true }
315
+)
316
+
317
+// 监听options变化,重新获取数据
318
+watch(
319
+  () => props.options,
320
+  () => {
321
+    if (props.visible) {
322
+      console.log('options变化:', props.options)
323
+      page.value.current = 1
324
+      fetchWarningData()
325
+      fetchOvertimeCount()
326
+    }
327
+  },
328
+  { deep: true }
329
+)
171 330
 </script>
172 331
 
173 332
 <style scoped>
@@ -176,7 +335,7 @@ onMounted(() => {
176 335
   position: fixed;
177 336
   z-index: 9999;
178 337
   inset: 0;
179
-  background: rgba(0, 10, 30, 0.8);
338
+  /* background: rgba(0, 10, 30, 0.8); */
180 339
   display: flex;
181 340
   align-items: center;
182 341
   justify-content: center;
@@ -232,10 +391,12 @@ onMounted(() => {
232 391
   border-radius: 4px;
233 392
   cursor: pointer;
234 393
   transition: all 0.3s ease;
394
+  padding: 0px;
235 395
 }
236 396
 
237 397
 .project-info-close:hover {
238 398
   background: rgba(0, 40, 80, 0.8);
399
+  color: #ffffff;
239 400
   box-shadow: 0 0 10px rgba(0, 153, 255, 0.6);
240 401
 }
241 402
 
@@ -351,6 +512,47 @@ onMounted(() => {
351 512
   font-weight: 500;
352 513
 }
353 514
 
515
+.status-processing {
516
+  color: #0099ff;
517
+  font-weight: 500;
518
+}
519
+
520
+.status-pending {
521
+  color: #ffcc00;
522
+  font-weight: 500;
523
+}
524
+
525
+.status-overtime {
526
+  color: #ff0000;
527
+  font-weight: 500;
528
+}
529
+
530
+.status-unknown {
531
+  color: #999999;
532
+  font-weight: 500;
533
+}
534
+
535
+/* 超期数量样式 */
536
+.stat-value.overtime {
537
+  color: #ff0000;
538
+}
539
+
540
+/* 无数据提示 */
541
+.no-data {
542
+  text-align: center;
543
+  padding: 40px 20px;
544
+  color: #666666;
545
+  font-size: 14px;
546
+}
547
+
548
+/* 页码省略号 */
549
+.pagination-ellipsis {
550
+  color: #66CCFF;
551
+  font-size: 14px;
552
+  margin: 0 4px;
553
+  cursor: default;
554
+}
555
+
354 556
 /* 分页样式 */
355 557
 .pagination-container {
356 558
   display: flex;