|
|
@@ -1,6 +1,6 @@
|
|
1
|
1
|
<template>
|
|
2
|
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
|
6
|
<div class="messages-area" ref="messagesRef">
|
|
|
@@ -20,10 +20,12 @@
|
|
20
|
20
|
<div v-for="step in msg.steps" :key="step.step" :class="['step-item', step.status]">
|
|
21
|
21
|
<span class="step-icon">
|
|
22
|
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
|
25
|
</svg>
|
|
25
|
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
|
29
|
</svg>
|
|
28
|
30
|
</span>
|
|
29
|
31
|
<span class="step-name">{{ step.name }}</span>
|
|
|
@@ -52,21 +54,10 @@
|
|
52
|
54
|
<div class="answer-text">{{ msg.result.answer }}</div>
|
|
53
|
55
|
</div>
|
|
54
|
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
|
61
|
</el-table>
|
|
71
|
62
|
<div v-else-if="msg.result.rows" class="no-data">暂无数据</div>
|
|
72
|
63
|
</div>
|
|
|
@@ -80,8 +71,8 @@
|
|
80
|
71
|
<!-- 错误 -->
|
|
81
|
72
|
<div v-if="msg.status === 'error'" class="error-message">
|
|
82
|
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
|
76
|
</svg>
|
|
86
|
77
|
{{ msg.errorText }}
|
|
87
|
78
|
</div>
|
|
|
@@ -95,24 +86,14 @@
|
|
95
|
86
|
<div class="quick-action">
|
|
96
|
87
|
<el-button class="report-btn" @click="handleReportClick">质控分析报告</el-button>
|
|
97
|
88
|
<el-button class="report-btn" @click="handlePerformanceClick">绩效分析报告</el-button>
|
|
|
89
|
+ <el-button class="report-btn" @click="handleUseReportClick">使用报表</el-button>
|
|
98
|
90
|
</div>
|
|
99
|
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
|
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
|
97
|
</svg>
|
|
117
|
98
|
</el-button>
|
|
118
|
99
|
</div>
|
|
|
@@ -121,6 +102,7 @@
|
|
121
|
102
|
|
|
122
|
103
|
<DataBoard v-if="showDataBoard" @back="handleBack" />
|
|
123
|
104
|
<PerformanceAnalysis v-if="showPerformanceAnalysis" @back="handleBack" />
|
|
|
105
|
+ <UseReports v-if="showUseReports" @back="handleBack" />
|
|
124
|
106
|
</div>
|
|
125
|
107
|
</template>
|
|
126
|
108
|
|
|
|
@@ -129,7 +111,7 @@ import { ref, nextTick, onMounted, onActivated, onDeactivated } from 'vue'
|
|
129
|
111
|
import DataBoard from './components/dataBoard.vue'
|
|
130
|
112
|
import PerformanceAnalysis from './components/performanceAnalysis.vue'
|
|
131
|
113
|
import useUserStore from '@/store/modules/user'
|
|
132
|
|
-
|
|
|
114
|
+import UseReports from './components/useReports.vue'
|
|
133
|
115
|
const userStore = useUserStore()
|
|
134
|
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
|
121
|
const messagesRef = ref(null)
|
|
140
|
122
|
const showDataBoard = ref(false)
|
|
141
|
123
|
const showPerformanceAnalysis = ref(false)
|
|
|
124
|
+const showUseReports = ref(false)
|
|
142
|
125
|
|
|
143
|
126
|
onMounted(() => resetState())
|
|
144
|
127
|
onActivated(() => resetState())
|
|
|
@@ -149,6 +132,7 @@ const resetState = () => {
|
|
149
|
132
|
showPerformanceAnalysis.value = false
|
|
150
|
133
|
inputMessage.value = ''
|
|
151
|
134
|
messages.value = []
|
|
|
135
|
+ showUseReports.value = false
|
|
152
|
136
|
isLoading.value = false
|
|
153
|
137
|
}
|
|
154
|
138
|
|
|
|
@@ -270,9 +254,11 @@ const handleSend = async () => {
|
|
270
|
254
|
|
|
271
|
255
|
const handleReportClick = () => { showDataBoard.value = true }
|
|
272
|
256
|
const handlePerformanceClick = () => { showPerformanceAnalysis.value = true }
|
|
|
257
|
+const handleUseReportClick = () => { showUseReports.value = true }
|
|
273
|
258
|
const handleBack = () => {
|
|
274
|
259
|
showDataBoard.value = false
|
|
275
|
260
|
showPerformanceAnalysis.value = false
|
|
|
261
|
+ showUseReports.value = false
|
|
276
|
262
|
}
|
|
277
|
263
|
</script>
|
|
278
|
264
|
|
|
|
@@ -349,7 +335,7 @@ const handleBack = () => {
|
|
349
|
335
|
background: #fff;
|
|
350
|
336
|
padding: 14px 16px;
|
|
351
|
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
|
339
|
font-size: 14px;
|
|
354
|
340
|
line-height: 1.6;
|
|
355
|
341
|
}
|
|
|
@@ -392,8 +378,13 @@ const handleBack = () => {
|
|
392
|
378
|
}
|
|
393
|
379
|
|
|
394
|
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
|
390
|
.step-detail {
|
|
|
@@ -536,7 +527,8 @@ const handleBack = () => {
|
|
536
|
527
|
font-size: 24px;
|
|
537
|
528
|
}
|
|
538
|
529
|
|
|
539
|
|
- .user-bubble, .assistant-bubble {
|
|
|
530
|
+ .user-bubble,
|
|
|
531
|
+ .assistant-bubble {
|
|
540
|
532
|
max-width: 95%;
|
|
541
|
533
|
}
|
|
542
|
534
|
}
|