huoyi недель назад: 4
Родитель
Сommit
1983e28be0

+ 9 - 0
src/api/assistant/assistant.js

@@ -106,4 +106,13 @@ export function getCalculateByTimeList(data) {
106
     method: 'post',
106
     method: 'post',
107
     data: data
107
     data: data
108
   })
108
   })
109
+}
110
+
111
+//使用报告
112
+export function getUsageReport(query) {
113
+  return request({
114
+    url: '/system/usageReport/report',
115
+    method: 'get',
116
+    params: query
117
+  })
109
 }
118
 }

+ 14 - 9
src/views/assistant/components/dutyOrganization.vue

@@ -177,26 +177,29 @@ const qualificationPieDescriptionPart2 = computed(() => {
177
   }
177
   }
178
 
178
 
179
   // 从资质柱状图数据中找出"一级"人员最多的科室
179
   // 从资质柱状图数据中找出"一级"人员最多的科室
180
-  let topDeptForLevel1 = '旅检三科'
181
-  let level1Count = 25
182
-  let totalDeptCount = 395
180
+  let topDeptForLevel1 = ''
181
+  let level1Count = 0
182
+  let totalDeptCount = 0
183
+  let allDeptNames = []
183
 
184
 
184
   if (qualificationBarData.value && Array.isArray(qualificationBarData.value)) {
185
   if (qualificationBarData.value && Array.isArray(qualificationBarData.value)) {
185
     const barData = qualificationBarData.value
186
     const barData = qualificationBarData.value
186
 
187
 
187
     // 找出"一级"人员最多的科室
188
     // 找出"一级"人员最多的科室
188
     let maxLevel1Count = 0
189
     let maxLevel1Count = 0
189
-    let maxDeptName = '旅检三科'
190
-    let totalCountForDept = 395
190
+    let maxDeptName = ''
191
+    let totalCountForDept = 0
192
+
191
 
193
 
192
     barData.forEach(dept => {
194
     barData.forEach(dept => {
195
+      allDeptNames.push(dept.deptName)
193
       if (dept.levelCounts && Array.isArray(dept.levelCounts)) {
196
       if (dept.levelCounts && Array.isArray(dept.levelCounts)) {
194
-        const level1Data = dept.levelCounts.find(level => level.levelName === '级')
197
+        const level1Data = dept.levelCounts.find(level => level.levelName === '级')
195
         const deptTotalCount = dept.levelCounts.reduce((sum, level) => sum + (level.count || 0), 0)
198
         const deptTotalCount = dept.levelCounts.reduce((sum, level) => sum + (level.count || 0), 0)
196
 
199
 
197
         if (level1Data && level1Data.count > maxLevel1Count) {
200
         if (level1Data && level1Data.count > maxLevel1Count) {
198
           maxLevel1Count = level1Data.count
201
           maxLevel1Count = level1Data.count
199
-          maxDeptName = dept.deptName || '未知科室'
202
+          maxDeptName = dept.deptName || ''
200
           totalCountForDept = deptTotalCount
203
           totalCountForDept = deptTotalCount
201
         }
204
         }
202
       }
205
       }
@@ -208,7 +211,7 @@ const qualificationPieDescriptionPart2 = computed(() => {
208
   }
211
   }
209
 
212
 
210
   // 生成第二部分描述文字
213
   // 生成第二部分描述文字
211
-  return `全站资质等级为"一级"的人员集中在${topDeptForLevel1}(${level1Count}人)${topDeptForLevel1}的人员规模(共${totalDeptCount}人)高于一科、二科`
214
+  return `全站资质等级为"一级"的人员集中在${topDeptForLevel1}(${level1Count}人)${topDeptForLevel1}的人员规模(共${totalDeptCount}人)高于${allDeptNames.filter(name => name !== topDeptForLevel1).join(', ')}`
212
 })
215
 })
213
 
216
 
214
 
217
 
@@ -658,7 +661,9 @@ const updateTrendBarChart = () => {
658
           label: {
661
           label: {
659
             show: true,
662
             show: true,
660
             position: 'top',
663
             position: 'top',
661
-            formatter: '{c}人'
664
+            formatter: function(params) {
665
+              return params.value > 0 ? params.value + '人' : '';
666
+            }
662
           },
667
           },
663
           data: levelCounts
668
           data: levelCounts
664
         })
669
         })

+ 23 - 6
src/views/assistant/components/performanceAnalysis.vue

@@ -297,6 +297,15 @@ const disabledStartMonth = (date) => {
297
     return true
297
     return true
298
   }
298
   }
299
 
299
 
300
+  // 如果已经选择了结束月份,开始月份不能晚于或等于结束月份
301
+  if (queryForm.value.endMonth) {
302
+    const endDate = new Date(queryForm.value.endMonth)
303
+    if (date.getFullYear() > endDate.getFullYear() ||
304
+      (date.getFullYear() === endDate.getFullYear() && date.getMonth() >= endDate.getMonth())) {
305
+      return true
306
+    }
307
+  }
308
+
300
   return false
309
   return false
301
 }
310
 }
302
 
311
 
@@ -316,11 +325,11 @@ const disabledEndMonth = (date) => {
316
     return true
325
     return true
317
   }
326
   }
318
 
327
 
319
-  // 如果已经选择了开始月份,结束月份不能早于开始月份
328
+  // 如果已经选择了开始月份,结束月份不能早于或等于开始月份
320
   if (queryForm.value.startMonth) {
329
   if (queryForm.value.startMonth) {
321
     const startDate = new Date(queryForm.value.startMonth)
330
     const startDate = new Date(queryForm.value.startMonth)
322
     if (date.getFullYear() < startDate.getFullYear() ||
331
     if (date.getFullYear() < startDate.getFullYear() ||
323
-      (date.getFullYear() === startDate.getFullYear() && date.getMonth() < startDate.getMonth())) {
332
+      (date.getFullYear() === startDate.getFullYear() && date.getMonth() <= startDate.getMonth())) {
324
       return true
333
       return true
325
     }
334
     }
326
   }
335
   }
@@ -409,7 +418,9 @@ const brigadeChartOptions = {
409
   },
418
   },
410
   yAxis: {
419
   yAxis: {
411
     type: 'value',
420
     type: 'value',
412
-    name: '得分'
421
+    name: '得分',
422
+    min: 80,
423
+    interval: 5
413
   },
424
   },
414
   series: [
425
   series: [
415
 
426
 
@@ -438,7 +449,9 @@ const departmentChartOptions = {
438
   },
449
   },
439
   yAxis: {
450
   yAxis: {
440
     type: 'value',
451
     type: 'value',
441
-    name: '得分'
452
+    name: '得分',
453
+    min: 80,
454
+    interval: 5
442
   },
455
   },
443
   series: [
456
   series: [
444
 
457
 
@@ -467,7 +480,9 @@ const teamListChartOptions = {
467
   },
480
   },
468
   yAxis: {
481
   yAxis: {
469
     type: 'value',
482
     type: 'value',
470
-    name: '得分'
483
+    name: '得分',
484
+    min: 80,
485
+    interval: 5
471
   },
486
   },
472
   series: []
487
   series: []
473
 }
488
 }
@@ -494,7 +509,9 @@ const personalChartOptions = {
494
   },
509
   },
495
   yAxis: {
510
   yAxis: {
496
     type: 'value',
511
     type: 'value',
497
-    name: '得分'
512
+    name: '得分',
513
+    min: 80,
514
+    interval: 5
498
   },
515
   },
499
   series: []
516
   series: []
500
 }
517
 }

+ 1 - 1
src/views/assistant/components/riskHazard.vue

@@ -285,7 +285,7 @@ const categoryStatsDescription = computed(() => {
285
     
285
     
286
   
286
   
287
     // 5. 生成描述文字
287
     // 5. 生成描述文字
288
-    return `${topDepartment.departmentName || 'XXXX'}是违禁品查获的主力大队,查获违禁物品数量为${topDepartment.seizureCount || 'XX'},占比${topDepartment.currentRatio}%。`
288
+    return `${topDepartment.brigadeName || 'XXXX'}是违禁品查获的主力大队,查获违禁物品数量为${topDepartment.seizureCount || 'XX'},占比${topDepartment.currentRatio}%。`
289
   })
289
   })
290
 
290
 
291
   // 计算属性:动态生成查获岗位分布描述
291
   // 计算属性:动态生成查获岗位分布描述

Разница между файлами не показана из-за своего большого размера
+ 680 - 0
src/views/assistant/components/useReports.vue


+ 32 - 40
src/views/assistant/index.vue

@@ -1,6 +1,6 @@
1
 <template>
1
 <template>
2
   <div class="ai-assist-wrapper">
2
   <div class="ai-assist-wrapper">
3
-    <div class="ai-assist-container" v-if="!showDataBoard && !showPerformanceAnalysis">
3
+    <div class="ai-assist-container" v-if="!showDataBoard && !showPerformanceAnalysis && !showUseReports">
4
 
4
 
5
       <!-- 消息区域 -->
5
       <!-- 消息区域 -->
6
       <div class="messages-area" ref="messagesRef">
6
       <div class="messages-area" ref="messagesRef">
@@ -20,10 +20,12 @@
20
                 <div v-for="step in msg.steps" :key="step.step" :class="['step-item', step.status]">
20
                 <div v-for="step in msg.steps" :key="step.step" :class="['step-item', step.status]">
21
                   <span class="step-icon">
21
                   <span class="step-icon">
22
                     <svg v-if="step.status === 'running'" class="spin-icon" viewBox="0 0 24 24" fill="none">
22
                     <svg v-if="step.status === 'running'" class="spin-icon" viewBox="0 0 24 24" fill="none">
23
-                      <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-dasharray="32" stroke-dashoffset="10"/>
23
+                      <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-dasharray="32"
24
+                        stroke-dashoffset="10" />
24
                     </svg>
25
                     </svg>
25
                     <svg v-else viewBox="0 0 24 24" fill="none">
26
                     <svg v-else viewBox="0 0 24 24" fill="none">
26
-                      <path d="M5 13l4 4L19 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
27
+                      <path d="M5 13l4 4L19 7" stroke="currentColor" stroke-width="2" stroke-linecap="round"
28
+                        stroke-linejoin="round" />
27
                     </svg>
29
                     </svg>
28
                   </span>
30
                   </span>
29
                   <span class="step-name">{{ step.name }}</span>
31
                   <span class="step-name">{{ step.name }}</span>
@@ -52,21 +54,10 @@
52
                     <div class="answer-text">{{ msg.result.answer }}</div>
54
                     <div class="answer-text">{{ msg.result.answer }}</div>
53
                   </div>
55
                   </div>
54
                   <div class="result-summary">{{ msg.result.message }}</div>
56
                   <div class="result-summary">{{ msg.result.message }}</div>
55
-                  <el-table
56
-                    v-if="msg.result.rows && msg.result.rows.length"
57
-                    :data="msg.result.rows"
58
-                    border
59
-                    size="small"
60
-                    max-height="360"
61
-                    class="result-table"
62
-                  >
63
-                    <el-table-column
64
-                      v-for="col in msg.result.columns"
65
-                      :key="col"
66
-                      :prop="col"
67
-                      :label="col"
68
-                      min-width="100"
69
-                    />
57
+                  <el-table v-if="msg.result.rows && msg.result.rows.length" :data="msg.result.rows" border size="small"
58
+                    max-height="360" class="result-table">
59
+                    <el-table-column v-for="col in msg.result.columns" :key="col" :prop="col" :label="col"
60
+                      min-width="100" />
70
                   </el-table>
61
                   </el-table>
71
                   <div v-else-if="msg.result.rows" class="no-data">暂无数据</div>
62
                   <div v-else-if="msg.result.rows" class="no-data">暂无数据</div>
72
                 </div>
63
                 </div>
@@ -80,8 +71,8 @@
80
               <!-- 错误 -->
71
               <!-- 错误 -->
81
               <div v-if="msg.status === 'error'" class="error-message">
72
               <div v-if="msg.status === 'error'" class="error-message">
82
                 <svg viewBox="0 0 24 24" fill="none" width="16" height="16">
73
                 <svg viewBox="0 0 24 24" fill="none" width="16" height="16">
83
-                  <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
84
-                  <path d="M12 8v4m0 4h.01" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
74
+                  <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" />
75
+                  <path d="M12 8v4m0 4h.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
85
                 </svg>
76
                 </svg>
86
                 {{ msg.errorText }}
77
                 {{ msg.errorText }}
87
               </div>
78
               </div>
@@ -95,24 +86,14 @@
95
         <div class="quick-action">
86
         <div class="quick-action">
96
           <el-button class="report-btn" @click="handleReportClick">质控分析报告</el-button>
87
           <el-button class="report-btn" @click="handleReportClick">质控分析报告</el-button>
97
           <el-button class="report-btn" @click="handlePerformanceClick">绩效分析报告</el-button>
88
           <el-button class="report-btn" @click="handlePerformanceClick">绩效分析报告</el-button>
89
+          <el-button class="report-btn" @click="handleUseReportClick">使用报表</el-button>
98
         </div>
90
         </div>
99
         <div class="input-container">
91
         <div class="input-container">
100
-          <el-input
101
-            v-model="inputMessage"
102
-            type="textarea"
103
-            :rows="3"
104
-            placeholder="请输入您的问题,按 Enter 发送..."
105
-            class="chat-input"
106
-            @keydown.enter.exact.prevent="handleSend"
107
-          />
108
-          <el-button
109
-            :disabled="!inputMessage.trim() || isLoading"
110
-            circle
111
-            class="send-btn"
112
-            @click="handleSend"
113
-          >
92
+          <el-input v-model="inputMessage" type="textarea" :rows="3" placeholder="请输入您的问题,按 Enter 发送..."
93
+            class="chat-input" @keydown.enter.exact.prevent="handleSend" />
94
+          <el-button :disabled="!inputMessage.trim() || isLoading" circle class="send-btn" @click="handleSend">
114
             <svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
95
             <svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
115
-              <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
96
+              <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
116
             </svg>
97
             </svg>
117
           </el-button>
98
           </el-button>
118
         </div>
99
         </div>
@@ -121,6 +102,7 @@
121
 
102
 
122
     <DataBoard v-if="showDataBoard" @back="handleBack" />
103
     <DataBoard v-if="showDataBoard" @back="handleBack" />
123
     <PerformanceAnalysis v-if="showPerformanceAnalysis" @back="handleBack" />
104
     <PerformanceAnalysis v-if="showPerformanceAnalysis" @back="handleBack" />
105
+    <UseReports v-if="showUseReports" @back="handleBack" />
124
   </div>
106
   </div>
125
 </template>
107
 </template>
126
 
108
 
@@ -129,7 +111,7 @@ import { ref, nextTick, onMounted, onActivated, onDeactivated } from 'vue'
129
 import DataBoard from './components/dataBoard.vue'
111
 import DataBoard from './components/dataBoard.vue'
130
 import PerformanceAnalysis from './components/performanceAnalysis.vue'
112
 import PerformanceAnalysis from './components/performanceAnalysis.vue'
131
 import useUserStore from '@/store/modules/user'
113
 import useUserStore from '@/store/modules/user'
132
-
114
+import UseReports from './components/useReports.vue'
133
 const userStore = useUserStore()
115
 const userStore = useUserStore()
134
 const AI_SERVICE_URL = import.meta.env.VITE_AI_SERVICE_URL || 'http://localhost:8000'
116
 const AI_SERVICE_URL = import.meta.env.VITE_AI_SERVICE_URL || 'http://localhost:8000'
135
 
117
 
@@ -139,6 +121,7 @@ const messages = ref([])
139
 const messagesRef = ref(null)
121
 const messagesRef = ref(null)
140
 const showDataBoard = ref(false)
122
 const showDataBoard = ref(false)
141
 const showPerformanceAnalysis = ref(false)
123
 const showPerformanceAnalysis = ref(false)
124
+const showUseReports = ref(false)
142
 
125
 
143
 onMounted(() => resetState())
126
 onMounted(() => resetState())
144
 onActivated(() => resetState())
127
 onActivated(() => resetState())
@@ -149,6 +132,7 @@ const resetState = () => {
149
   showPerformanceAnalysis.value = false
132
   showPerformanceAnalysis.value = false
150
   inputMessage.value = ''
133
   inputMessage.value = ''
151
   messages.value = []
134
   messages.value = []
135
+  showUseReports.value = false
152
   isLoading.value = false
136
   isLoading.value = false
153
 }
137
 }
154
 
138
 
@@ -270,9 +254,11 @@ const handleSend = async () => {
270
 
254
 
271
 const handleReportClick = () => { showDataBoard.value = true }
255
 const handleReportClick = () => { showDataBoard.value = true }
272
 const handlePerformanceClick = () => { showPerformanceAnalysis.value = true }
256
 const handlePerformanceClick = () => { showPerformanceAnalysis.value = true }
257
+const handleUseReportClick = () => { showUseReports.value = true }
273
 const handleBack = () => {
258
 const handleBack = () => {
274
   showDataBoard.value = false
259
   showDataBoard.value = false
275
   showPerformanceAnalysis.value = false
260
   showPerformanceAnalysis.value = false
261
+  showUseReports.value = false
276
 }
262
 }
277
 </script>
263
 </script>
278
 
264
 
@@ -349,7 +335,7 @@ const handleBack = () => {
349
   background: #fff;
335
   background: #fff;
350
   padding: 14px 16px;
336
   padding: 14px 16px;
351
   border-radius: 18px 18px 18px 4px;
337
   border-radius: 18px 18px 18px 4px;
352
-  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
338
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
353
   font-size: 14px;
339
   font-size: 14px;
354
   line-height: 1.6;
340
   line-height: 1.6;
355
 }
341
 }
@@ -392,8 +378,13 @@ const handleBack = () => {
392
 }
378
 }
393
 
379
 
394
 @keyframes spin {
380
 @keyframes spin {
395
-  from { transform: rotate(0deg); }
396
-  to { transform: rotate(360deg); }
381
+  from {
382
+    transform: rotate(0deg);
383
+  }
384
+
385
+  to {
386
+    transform: rotate(360deg);
387
+  }
397
 }
388
 }
398
 
389
 
399
 .step-detail {
390
 .step-detail {
@@ -536,7 +527,8 @@ const handleBack = () => {
536
     font-size: 24px;
527
     font-size: 24px;
537
   }
528
   }
538
 
529
 
539
-  .user-bubble, .assistant-bubble {
530
+  .user-bubble,
531
+  .assistant-bubble {
540
     max-width: 95%;
532
     max-width: 95%;
541
   }
533
   }
542
 }
534
 }