st-comp 0.0.263 → 0.0.266
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/CustomFunction.cjs +1 -1
- package/es/CustomFunction.js +22 -22
- package/es/FactorWarning.cjs +1 -1
- package/es/FactorWarning.js +26 -26
- package/es/Kline.cjs +1 -1
- package/es/Kline.js +11 -11
- package/es/KlineBasic.cjs +1 -1
- package/es/KlineBasic.js +19 -19
- package/es/KlineConfig.cjs +1 -1
- package/es/KlineConfig.js +16 -16
- package/es/KlineNew.cjs +1 -1
- package/es/KlineNew.js +10 -10
- package/es/KlinePlus.cjs +1 -1
- package/es/KlinePlus.js +12 -12
- package/es/MonacoEditor.cjs +1 -1
- package/es/MonacoEditor.js +18 -18
- package/es/Pagination.cjs +1 -1
- package/es/Pagination.js +14 -14
- package/es/PasswordPrompt.cjs +1 -1
- package/es/PasswordPrompt.js +2 -2
- package/es/Table.cjs +1 -1
- package/es/Table.js +18 -18
- package/es/User.cjs +1 -1
- package/es/User.js +19 -19
- package/es/VarSelectDialog.cjs +1 -1
- package/es/VarSelectDialog.js +19 -19
- package/es/VarietyAiHelper.cjs +3 -3
- package/es/VarietyAiHelper.js +320 -279
- package/es/VarietyAutoComplete.cjs +1 -1
- package/es/VarietyAutoComplete.js +8 -8
- package/es/VarietySearch.cjs +1 -1
- package/es/VarietySearch.js +31 -31
- package/es/{VarietySelect-9f267958.js → VarietySelect-1ac10ec2.js} +3 -3
- package/es/{VarietySelect-5c845562.cjs → VarietySelect-639a8d9a.cjs} +1 -1
- package/es/VarietyTextCopy.cjs +1 -1
- package/es/VarietyTextCopy.js +10 -10
- package/es/VirtualTable.cjs +1 -1
- package/es/VirtualTable.js +66 -66
- package/es/{_initCloneObject-69c8ae23.js → _initCloneObject-6b8c230e.js} +2 -2
- package/es/{_initCloneObject-2b82e9f7.cjs → _initCloneObject-9eddcb0c.cjs} +1 -1
- package/es/aiTools-6c9c58bd.cjs +4 -0
- package/es/aiTools-ab67ffa0.js +149 -0
- package/es/aiTools.js +92 -1
- package/es/{config-provider-7860903c.js → config-provider-56193d47.js} +3 -3
- package/es/{config-provider-2ae47cc8.cjs → config-provider-ea286661.cjs} +1 -1
- package/es/{dropdown-497442b7.js → dropdown-4e541d60.js} +3 -3
- package/es/{dropdown-eba9eaf5.cjs → dropdown-f30148cc.cjs} +1 -1
- package/es/{el-autocomplete-b59eb529.cjs → el-autocomplete-3949e68c.cjs} +1 -1
- package/es/{el-autocomplete-27c60cc8.js → el-autocomplete-4b521c83.js} +6 -6
- package/es/{el-button-e2c63c08.js → el-button-4f201000.js} +3 -3
- package/es/{el-button-974ff9e9.cjs → el-button-dd065de0.cjs} +1 -1
- package/es/{el-checkbox-08185353.cjs → el-checkbox-02ce40f0.cjs} +1 -1
- package/es/{el-checkbox-71ebd862.js → el-checkbox-33cb73db.js} +3 -3
- package/es/{el-dialog-eedcfd3e.js → el-dialog-1939f7c5.js} +4 -4
- package/es/{el-dialog-1b185570.cjs → el-dialog-6501a151.cjs} +1 -1
- package/es/{el-form-item-c997b4fa.cjs → el-form-item-316b35d8.cjs} +1 -1
- package/es/{el-form-item-bdcfd297.js → el-form-item-ea11211d.js} +5 -5
- package/es/{el-input-fa18ef84.cjs → el-input-094afbe2.cjs} +1 -1
- package/es/{el-input-d47281da.js → el-input-58786d42.js} +32 -32
- package/es/{el-input-number-3d94fa58.cjs → el-input-number-a609a5e3.cjs} +1 -1
- package/es/{el-input-number-c8018cb1.js → el-input-number-a7971697.js} +14 -14
- package/es/{el-loading-969a79ca.cjs → el-loading-d171ce64.cjs} +1 -1
- package/es/{el-loading-0cd81d05.js → el-loading-f3815921.js} +1 -1
- package/es/{el-menu-item-26071fd6.cjs → el-menu-item-3d1e0aff.cjs} +1 -1
- package/es/{el-menu-item-dac65bb3.js → el-menu-item-a166b997.js} +5 -5
- package/es/{el-message-box-31e0aa98.cjs → el-message-box-b8ffcf34.cjs} +1 -1
- package/es/{el-message-box-fea4fca8.js → el-message-box-f2b667e9.js} +9 -9
- package/es/{el-overlay-1ee0338d.js → el-overlay-1dfe3675.js} +3 -3
- package/es/{el-overlay-ea65cb05.cjs → el-overlay-a94f8a98.cjs} +1 -1
- package/es/{el-popconfirm-9e232436.cjs → el-popconfirm-70e2849c.cjs} +1 -1
- package/es/{el-popconfirm-089b8bec.js → el-popconfirm-ca31ceea.js} +5 -5
- package/es/{el-popper-2d3914e4.cjs → el-popper-ce575c12.cjs} +1 -1
- package/es/{el-popper-c9b3d3cf.js → el-popper-db6c599f.js} +2 -2
- package/es/{el-segmented-140ac042.cjs → el-segmented-4d50b63f.cjs} +1 -1
- package/es/{el-segmented-9d3a9e11.js → el-segmented-741f2252.js} +2 -2
- package/es/{el-select-e51e11c1.js → el-select-086bcb6c.js} +9 -9
- package/es/{el-select-a11f33e8.cjs → el-select-2ebc8380.cjs} +1 -1
- package/es/{el-table-column-98570a4d.cjs → el-table-column-2f07fbdb.cjs} +1 -1
- package/es/{el-table-column-05d292a8.js → el-table-column-8a15378a.js} +10 -10
- package/es/{el-tag-17cd04a1.js → el-tag-65a99986.js} +2 -2
- package/es/{el-tag-6d8e653e.cjs → el-tag-f48b1190.cjs} +1 -1
- package/es/{el-text-a18106cb.cjs → el-text-33359f44.cjs} +1 -1
- package/es/{el-text-2710fff3.js → el-text-cbb693f2.js} +1 -1
- package/es/{index-f30561d3.js → index-072c4a65.js} +5 -5
- package/es/{index-299ee017.cjs → index-0f767104.cjs} +1 -1
- package/es/{index-eb99b188.cjs → index-18565979.cjs} +1 -1
- package/es/{index-0f79095c.js → index-1955f23d.js} +1 -1
- package/es/{index-0ee486ad.js → index-1e91e986.js} +1 -1
- package/es/{index-6ca95c8a.cjs → index-21e8d2bc.cjs} +1 -1
- package/es/{index-33f80550.cjs → index-3792552a.cjs} +1 -1
- package/es/{index-7dce9f59.cjs → index-4f900527.cjs} +1 -1
- package/es/{index-1d9b50de.js → index-57d82da0.js} +2 -2
- package/es/{index-e8eeea22.cjs → index-5b546f7d.cjs} +1 -1
- package/es/{index-c71e37dc.js → index-7db02db7.js} +2 -2
- package/es/{index-37b8d3c6.cjs → index-7eb88616.cjs} +1 -1
- package/es/{index-6b99def3.cjs → index-8439d2f9.cjs} +1 -1
- package/es/{index-40f05e2c.cjs → index-8b055879.cjs} +2 -2
- package/es/{index-b0117ba2.js → index-a4e252a0.js} +1 -1
- package/es/{index-95e5d454.js → index-b51915a2.js} +2 -2
- package/es/{index-28e03bad.cjs → index-bf98dd03.cjs} +1 -1
- package/es/{index-c2b9bbfd.js → index-ca91ac68.js} +82 -67
- package/es/{index-8a54ceeb.js → index-d857270a.js} +9 -9
- package/es/{index-bcd895a0.js → index-ff26b1a6.js} +2 -2
- package/es/{python-02c3937a.cjs → python-5b5c9c58.cjs} +1 -1
- package/es/{python-99011a53.js → python-ad9239f9.js} +18 -18
- package/es/style.css +1 -1
- package/es/{use-form-common-props-d3ed62c6.cjs → use-form-common-props-00ec25ac.cjs} +1 -1
- package/es/{use-form-common-props-cb0ca65c.js → use-form-common-props-c14990b9.js} +29 -29
- package/es/{use-global-config-c80f33a4.cjs → use-global-config-28efb416.cjs} +1 -1
- package/es/{use-global-config-cdaeca54.js → use-global-config-a01b5ce1.js} +2 -2
- package/es/{validator-07160325.cjs → validator-119fdaf4.cjs} +1 -1
- package/es/{validator-4ab9774f.js → validator-65de1caf.js} +1 -1
- package/es/{zh-cn-6a0f844c.cjs → zh-cn-9fb29a39.cjs} +1 -1
- package/es/{zh-cn-8a6390a4.js → zh-cn-c1c28e70.js} +1 -1
- package/lib/aiTools.js +92 -1
- package/lib/bundle.js +1 -1
- package/lib/bundle.umd.cjs +182 -181
- package/lib/{index-0969ca9d.js → index-53b3465c.js} +19675 -19562
- package/lib/{python-506107bf.js → python-4a270481.js} +1 -1
- package/lib/style.css +1 -1
- package/package.json +1 -1
- package/packages/VarietyAiHelper/index.vue +271 -290
- package/public/aiTools.js +92 -1
- package/es/aiTools-2e1f92d2.cjs +0 -3
- package/es/aiTools-faa0a14d.js +0 -90
|
@@ -3,9 +3,10 @@ import dayjs from "dayjs";
|
|
|
3
3
|
import { ElMessage } from "element-plus";
|
|
4
4
|
import { getUserData } from "st-func";
|
|
5
5
|
import { inject, ref, nextTick, watch, reactive, onMounted } from "vue";
|
|
6
|
-
import {
|
|
7
|
-
import { UserFilled, Service, Promotion } from "@element-plus/icons-vue";
|
|
6
|
+
import { sendToBaiLianWorkflowStreaming } from "../../public/aiTools";
|
|
7
|
+
import { UserFilled, Service, Promotion, Refresh, SuccessFilled } from "@element-plus/icons-vue";
|
|
8
8
|
|
|
9
|
+
// ==================== 基础配置 ====================
|
|
9
10
|
const stConfig = inject("stConfig");
|
|
10
11
|
const userData = reactive(getUserData());
|
|
11
12
|
const visible = ref(false);
|
|
@@ -13,41 +14,43 @@ const emit = defineEmits(["callBack"]);
|
|
|
13
14
|
const props = defineProps({
|
|
14
15
|
defaultMessage: {
|
|
15
16
|
type: String,
|
|
16
|
-
default:
|
|
17
|
+
default:
|
|
18
|
+
"你好呀!我是你的品种池AI助手,会根据您的自然语言去进行品种的条件查询\n\n示例: \n帮我查A股科创板下品种转价差上证50并且总市值大于1千万, 最近一次红箱的开盘价大于前5根K线的最高价, 并按照总市值升序进行排序",
|
|
17
19
|
},
|
|
18
20
|
});
|
|
19
21
|
|
|
20
|
-
//
|
|
21
|
-
const tagMap = ref();
|
|
22
|
+
// ==================== 响应式数据 ====================
|
|
23
|
+
const tagMap = ref(); // 自定义标签映射表
|
|
24
|
+
const isSending = ref(false); // 是否正在发送消息
|
|
25
|
+
const isThinking = ref(false); // AI是否正在思考(首包到达前)
|
|
26
|
+
const nodeProgressList = ref([]); // 工作流节点执行进度列表
|
|
27
|
+
const showFinalResult = ref(false); // 是否展示最终结果
|
|
22
28
|
|
|
23
|
-
//
|
|
24
|
-
const isSending = ref(false);
|
|
25
|
-
const isThinking = ref(false);
|
|
26
|
-
|
|
27
|
-
// 消息队列
|
|
29
|
+
// ==================== 消息队列 ====================
|
|
28
30
|
const messageListRef = ref(null);
|
|
29
31
|
const messageList = ref([
|
|
30
32
|
{
|
|
31
|
-
role: "assistant",
|
|
33
|
+
role: "assistant",
|
|
32
34
|
content: props.defaultMessage,
|
|
33
35
|
userContent: null,
|
|
34
|
-
showFeedback: false,
|
|
35
|
-
hasFeedback: false,
|
|
36
|
-
createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
|
37
|
-
resTime: 0,
|
|
38
|
-
firstPackageTime: 0,
|
|
36
|
+
showFeedback: false,
|
|
37
|
+
hasFeedback: false,
|
|
38
|
+
createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
|
39
|
+
resTime: 0,
|
|
40
|
+
firstPackageTime: 0,
|
|
39
41
|
},
|
|
40
42
|
]);
|
|
41
43
|
|
|
42
|
-
//
|
|
43
|
-
const userInput = ref("");
|
|
44
|
+
const userInput = ref(""); // 用户输入内容
|
|
44
45
|
|
|
45
|
-
// 反馈弹窗
|
|
46
|
+
// ==================== 反馈弹窗 ====================
|
|
46
47
|
const feedbackDialogVisible = ref(false);
|
|
47
48
|
const feedbackContent = ref("");
|
|
48
|
-
const feedbackMessageIndex = ref({});
|
|
49
|
+
const feedbackMessageIndex = ref({});
|
|
49
50
|
|
|
50
|
-
//
|
|
51
|
+
// ==================== 工具函数 ====================
|
|
52
|
+
|
|
53
|
+
// 判断字符串是否可解析为 JSON 对象
|
|
51
54
|
const isJSONSerializable = (str) => {
|
|
52
55
|
if (typeof str !== "string") return false;
|
|
53
56
|
try {
|
|
@@ -57,25 +60,23 @@ const isJSONSerializable = (str) => {
|
|
|
57
60
|
return false;
|
|
58
61
|
}
|
|
59
62
|
};
|
|
60
|
-
|
|
63
|
+
|
|
64
|
+
// 将 JSON 字符串解析为展示数据(parsedConditions + 其他参数)
|
|
61
65
|
const formatJSONContent = (content) => {
|
|
62
66
|
if (!isJSONSerializable(content)) return null;
|
|
63
67
|
try {
|
|
64
68
|
const jsonData = JSON.parse(content);
|
|
65
69
|
const { parsedConditions, ...webParams } = jsonData;
|
|
66
|
-
return {
|
|
67
|
-
parsedConditions: parsedConditions,
|
|
68
|
-
webParams,
|
|
69
|
-
};
|
|
70
|
+
return { parsedConditions, webParams };
|
|
70
71
|
} catch {
|
|
71
72
|
return null;
|
|
72
73
|
}
|
|
73
74
|
};
|
|
74
|
-
|
|
75
|
+
|
|
76
|
+
// 将解析后的数据渲染为 HTML 字符串
|
|
75
77
|
const renderJSONContent = (jsonData) => {
|
|
76
78
|
if (!jsonData) return "";
|
|
77
79
|
let html = "";
|
|
78
|
-
// 渲染 parsedConditions
|
|
79
80
|
if (jsonData.parsedConditions?.length) {
|
|
80
81
|
html += '<div class="parsed-conditions">';
|
|
81
82
|
jsonData.parsedConditions.forEach((text) => {
|
|
@@ -83,20 +84,17 @@ const renderJSONContent = (jsonData) => {
|
|
|
83
84
|
});
|
|
84
85
|
html += "</div>";
|
|
85
86
|
}
|
|
86
|
-
// 渲染 webParams
|
|
87
|
-
// html += '<div class="web-params">';
|
|
88
|
-
// html += `<div>${JSON.stringify(jsonData.webParams)}</div>`;
|
|
89
|
-
// html += "</div>";
|
|
90
87
|
return html;
|
|
91
88
|
};
|
|
92
89
|
|
|
93
|
-
//
|
|
90
|
+
// ==================== 反馈处理 ====================
|
|
91
|
+
|
|
92
|
+
// 处理用户反馈操作(满意 / 不满意 / 默认记录)
|
|
94
93
|
const handleFeedbackAction = async (action, messageIndex) => {
|
|
95
94
|
switch (action) {
|
|
96
|
-
// 窗口: 提交(满意)
|
|
97
95
|
case "satisfied": {
|
|
98
96
|
const message = messageList.value[messageIndex];
|
|
99
|
-
|
|
97
|
+
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", {
|
|
100
98
|
userName: userData.username,
|
|
101
99
|
userContent: message.userContent,
|
|
102
100
|
aiContent: message.content,
|
|
@@ -104,25 +102,21 @@ const handleFeedbackAction = async (action, messageIndex) => {
|
|
|
104
102
|
createTime: message.createTime,
|
|
105
103
|
resTime: message.resTime,
|
|
106
104
|
firstPackageTime: message.firstPackageTime,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", params);
|
|
105
|
+
isSolved: 1,
|
|
106
|
+
logOrigin: 1,
|
|
107
|
+
});
|
|
112
108
|
ElMessage.success("感谢您的评价!");
|
|
113
109
|
messageList.value[messageIndex].hasFeedback = true;
|
|
114
110
|
break;
|
|
115
111
|
}
|
|
116
|
-
// 窗口: 打开
|
|
117
112
|
case "open": {
|
|
118
113
|
feedbackMessageIndex.value = messageIndex;
|
|
119
114
|
feedbackDialogVisible.value = true;
|
|
120
115
|
break;
|
|
121
116
|
}
|
|
122
|
-
// 窗口: 提交(不满意)
|
|
123
117
|
case "unsatisfied": {
|
|
124
118
|
const message = messageList.value[messageIndex];
|
|
125
|
-
|
|
119
|
+
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", {
|
|
126
120
|
userName: userData.username,
|
|
127
121
|
userContent: message.userContent,
|
|
128
122
|
userSuggestion: feedbackContent.value,
|
|
@@ -131,20 +125,17 @@ const handleFeedbackAction = async (action, messageIndex) => {
|
|
|
131
125
|
resTime: message.resTime,
|
|
132
126
|
createTime: message.createTime,
|
|
133
127
|
firstPackageTime: message.firstPackageTime,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
};
|
|
138
|
-
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", params);
|
|
128
|
+
isSolved: 0,
|
|
129
|
+
logOrigin: 1,
|
|
130
|
+
});
|
|
139
131
|
ElMessage.success("感谢您的反馈!我们将持续跟踪并进行优化");
|
|
140
132
|
feedbackDialogVisible.value = false;
|
|
141
133
|
messageList.value[messageIndex].hasFeedback = true;
|
|
142
134
|
break;
|
|
143
135
|
}
|
|
144
|
-
// 自动提交记录跟踪日志
|
|
145
136
|
case "default": {
|
|
146
137
|
const message = messageList.value[messageIndex];
|
|
147
|
-
|
|
138
|
+
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", {
|
|
148
139
|
userName: userData.username,
|
|
149
140
|
userContent: message.userContent,
|
|
150
141
|
userSuggestion: feedbackContent.value,
|
|
@@ -153,21 +144,28 @@ const handleFeedbackAction = async (action, messageIndex) => {
|
|
|
153
144
|
resTime: message.resTime,
|
|
154
145
|
createTime: message.createTime,
|
|
155
146
|
firstPackageTime: message.firstPackageTime,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
};
|
|
160
|
-
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", params);
|
|
147
|
+
isSolved: 1,
|
|
148
|
+
logOrigin: 1,
|
|
149
|
+
});
|
|
161
150
|
}
|
|
162
151
|
}
|
|
163
152
|
};
|
|
164
|
-
|
|
153
|
+
|
|
154
|
+
// ==================== 消息发送 ====================
|
|
155
|
+
|
|
156
|
+
// ==================== 消息发送 ====================
|
|
157
|
+
|
|
158
|
+
// 发送消息到工作流
|
|
165
159
|
const sendMessage = async () => {
|
|
166
160
|
const content = userInput.value.trim();
|
|
167
161
|
if (!content) return ElMessage.warning("请输入消息内容");
|
|
168
162
|
if (isSending.value) return;
|
|
169
163
|
|
|
170
|
-
//
|
|
164
|
+
// 重置状态
|
|
165
|
+
nodeProgressList.value = [];
|
|
166
|
+
showFinalResult.value = false;
|
|
167
|
+
|
|
168
|
+
// 添加用户消息
|
|
171
169
|
messageList.value.push({
|
|
172
170
|
role: "user",
|
|
173
171
|
content: content,
|
|
@@ -176,7 +174,7 @@ const sendMessage = async () => {
|
|
|
176
174
|
userInput.value = "";
|
|
177
175
|
await scrollToBottom();
|
|
178
176
|
|
|
179
|
-
//
|
|
177
|
+
// 添加 AI 占位消息
|
|
180
178
|
messageList.value.push({
|
|
181
179
|
role: "assistant",
|
|
182
180
|
content: "",
|
|
@@ -194,23 +192,21 @@ const sendMessage = async () => {
|
|
|
194
192
|
|
|
195
193
|
let fullResponse = "";
|
|
196
194
|
let resTime = new Date().getTime();
|
|
195
|
+
let firstNodeReceived = false; // 标记是否已收到首个节点推送
|
|
196
|
+
|
|
197
197
|
try {
|
|
198
|
-
const appId = "
|
|
198
|
+
const appId = "1977602b357e4dab9d0b74899d5323b3";
|
|
199
199
|
const apiKey = "sk-d995eb26a4334bdeb2ccb4cbfaf51de8";
|
|
200
|
-
await
|
|
200
|
+
await sendToBaiLianWorkflowStreaming({
|
|
201
201
|
appId,
|
|
202
202
|
apiKey,
|
|
203
203
|
value: content,
|
|
204
204
|
callback: (type, data) => {
|
|
205
|
-
//
|
|
205
|
+
// 工作流返回错误
|
|
206
206
|
if (type === "error") {
|
|
207
207
|
isThinking.value = false;
|
|
208
208
|
isSending.value = false;
|
|
209
|
-
|
|
210
|
-
// 移除占位的AI消息
|
|
211
209
|
messageList.value.pop();
|
|
212
|
-
|
|
213
|
-
// 添加错误提示消息
|
|
214
210
|
messageList.value.push({
|
|
215
211
|
role: "assistant",
|
|
216
212
|
userContent: content,
|
|
@@ -225,42 +221,58 @@ const sendMessage = async () => {
|
|
|
225
221
|
scrollToBottom();
|
|
226
222
|
return;
|
|
227
223
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
//
|
|
224
|
+
|
|
225
|
+
// 节点执行进度更新
|
|
226
|
+
if (type === "node") {
|
|
227
|
+
// 首个节点推送时记录首包响应时间
|
|
228
|
+
if (!firstNodeReceived) {
|
|
229
|
+
firstNodeReceived = true;
|
|
230
|
+
const lastMessage = messageList.value[messageList.value.length - 1];
|
|
231
|
+
if (lastMessage && lastMessage.role === "assistant" && lastMessage.firstPackageTime === 0) {
|
|
232
|
+
lastMessage.firstPackageTime = new Date().getTime() - resTime;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const existing = nodeProgressList.value.find((n) => n.name === data.name);
|
|
237
|
+
if (existing) {
|
|
238
|
+
existing.status = data.status;
|
|
239
|
+
} else {
|
|
240
|
+
nodeProgressList.value.push({ name: data.name, status: data.status });
|
|
241
|
+
}
|
|
242
|
+
scrollToBottom();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 最终结果(工作流结束时一次性推送)
|
|
247
|
+
if (type === "message") {
|
|
248
|
+
fullResponse = data;
|
|
232
249
|
const lastMessage = messageList.value[messageList.value.length - 1];
|
|
233
250
|
if (lastMessage && lastMessage.role === "assistant") {
|
|
234
251
|
lastMessage.content = fullResponse;
|
|
235
252
|
scrollToBottom();
|
|
236
253
|
}
|
|
237
|
-
// 记录首包响应耗时
|
|
238
|
-
if (lastMessage.firstPackageTime === 0) {
|
|
239
|
-
lastMessage.firstPackageTime = new Date().getTime() - resTime;
|
|
240
|
-
}
|
|
241
254
|
}
|
|
242
|
-
|
|
243
|
-
|
|
255
|
+
|
|
256
|
+
// 流式结束,展示反馈按钮并触发回调
|
|
257
|
+
if (type === "finish") {
|
|
244
258
|
isThinking.value = false;
|
|
245
259
|
isSending.value = false;
|
|
260
|
+
showFinalResult.value = true;
|
|
246
261
|
const lastMessage = messageList.value[messageList.value.length - 1];
|
|
247
262
|
|
|
248
|
-
// 显示反馈按钮
|
|
249
263
|
if (lastMessage && lastMessage.role === "assistant") {
|
|
250
264
|
lastMessage.showFeedback = true;
|
|
251
265
|
lastMessage.resTime = new Date().getTime() - resTime;
|
|
252
266
|
handleFeedbackAction("default", messageList.value.length - 1);
|
|
253
267
|
}
|
|
254
268
|
|
|
255
|
-
//
|
|
256
|
-
console.log(fullResponse);
|
|
269
|
+
// 解析最终结果并回调父组件
|
|
257
270
|
try {
|
|
258
271
|
const jsonResponse = JSON.parse(fullResponse);
|
|
259
|
-
// 切割处理: parsedConditions, 这个字段仅做AI提炼展示使用
|
|
260
272
|
if (jsonResponse.parsedConditions) {
|
|
261
273
|
delete jsonResponse.parsedConditions;
|
|
262
274
|
}
|
|
263
|
-
//
|
|
275
|
+
// 将 customTagNames 转换为 customTag
|
|
264
276
|
if (jsonResponse.customTagNames?.length) {
|
|
265
277
|
const customTag = jsonResponse.customTagNames.reduce((result, item) => {
|
|
266
278
|
const id = tagMap.value[item];
|
|
@@ -278,10 +290,9 @@ const sendMessage = async () => {
|
|
|
278
290
|
},
|
|
279
291
|
});
|
|
280
292
|
} catch (error) {
|
|
293
|
+
// 网络异常处理
|
|
281
294
|
ElMessage.error(`AI响应异常: ${error}`);
|
|
282
|
-
// 移除占位的AI消息
|
|
283
295
|
messageList.value.pop();
|
|
284
|
-
// 添加错误提示消息
|
|
285
296
|
messageList.value.push({
|
|
286
297
|
role: "assistant",
|
|
287
298
|
userContent: content,
|
|
@@ -299,78 +310,83 @@ const sendMessage = async () => {
|
|
|
299
310
|
}
|
|
300
311
|
};
|
|
301
312
|
|
|
302
|
-
//
|
|
313
|
+
// ==================== 键盘事件 ====================
|
|
314
|
+
|
|
315
|
+
// Enter 发送,Ctrl+Enter 换行
|
|
303
316
|
const handleKeydown = (event) => {
|
|
304
317
|
if (event.key === "Enter") {
|
|
305
318
|
if (event.ctrlKey) {
|
|
306
|
-
// Ctrl + Enter: 插入换行符
|
|
307
319
|
event.preventDefault();
|
|
308
320
|
const textarea = event.target;
|
|
309
321
|
const start = textarea.selectionStart;
|
|
310
322
|
const end = textarea.selectionEnd;
|
|
311
323
|
userInput.value = userInput.value.substring(0, start) + "\n" + userInput.value.substring(end);
|
|
312
|
-
// 将光标移动到新插入的换行符之后
|
|
313
324
|
nextTick(() => {
|
|
314
325
|
textarea.selectionStart = textarea.selectionEnd = start + 1;
|
|
315
326
|
});
|
|
316
327
|
} else {
|
|
317
|
-
// 单独的 Enter: 发送消息
|
|
318
328
|
event.preventDefault();
|
|
319
329
|
sendMessage();
|
|
320
330
|
}
|
|
321
331
|
}
|
|
322
332
|
};
|
|
323
|
-
|
|
333
|
+
|
|
334
|
+
// ==================== 滚动 & 初始化 ====================
|
|
335
|
+
|
|
336
|
+
// 自动滚动到消息列表底部
|
|
324
337
|
const scrollToBottom = async () => {
|
|
325
338
|
await nextTick();
|
|
326
339
|
if (messageListRef.value) {
|
|
327
340
|
messageListRef.value.scrollTop = messageListRef.value.scrollHeight;
|
|
328
341
|
}
|
|
329
342
|
};
|
|
330
|
-
|
|
343
|
+
|
|
344
|
+
// 获取全部标签映射
|
|
331
345
|
const getTotalTagMap = async () => {
|
|
332
|
-
const res = await Promise.all([
|
|
346
|
+
const res = await Promise.all([
|
|
347
|
+
stConfig.request.post("/alarm/deliversign/findTagsByUserId"),
|
|
348
|
+
stConfig.request.post("/alarm/deliversign/findSystemTagsByTagName"),
|
|
349
|
+
]);
|
|
333
350
|
tagMap.value = res.reduce((result, item) => {
|
|
334
351
|
return {
|
|
335
352
|
...result,
|
|
336
353
|
...item.body?.reduce((cR, cI) => {
|
|
337
|
-
return {
|
|
338
|
-
...cR,
|
|
339
|
-
[cI.tagName]: cI.id,
|
|
340
|
-
};
|
|
354
|
+
return { ...cR, [cI.tagName]: cI.id };
|
|
341
355
|
}, {}),
|
|
342
356
|
};
|
|
343
357
|
}, {});
|
|
344
358
|
};
|
|
359
|
+
|
|
345
360
|
onMounted(() => {
|
|
346
361
|
getTotalTagMap();
|
|
347
362
|
});
|
|
348
|
-
|
|
363
|
+
|
|
364
|
+
// 监听消息队列变化,自动滚动
|
|
349
365
|
watch(
|
|
350
366
|
() => messageList.value,
|
|
351
|
-
() => {
|
|
352
|
-
scrollToBottom();
|
|
353
|
-
},
|
|
367
|
+
() => { scrollToBottom(); },
|
|
354
368
|
{ deep: true },
|
|
355
369
|
);
|
|
356
|
-
|
|
370
|
+
|
|
371
|
+
// 关闭反馈弹窗时清空内容
|
|
357
372
|
watch(
|
|
358
373
|
() => feedbackDialogVisible.value,
|
|
359
374
|
(newValue) => {
|
|
360
375
|
if (!newValue) feedbackContent.value = "";
|
|
361
376
|
},
|
|
362
377
|
);
|
|
378
|
+
|
|
379
|
+
// 暴露 open 方法供父组件调用
|
|
363
380
|
defineExpose({
|
|
364
381
|
open: () => {
|
|
365
382
|
visible.value = true;
|
|
366
|
-
nextTick(() => {
|
|
367
|
-
scrollToBottom();
|
|
368
|
-
});
|
|
383
|
+
nextTick(() => { scrollToBottom(); });
|
|
369
384
|
},
|
|
370
385
|
});
|
|
371
386
|
</script>
|
|
372
387
|
|
|
373
388
|
<template>
|
|
389
|
+
<!-- ==================== 主对话框 ==================== -->
|
|
374
390
|
<el-dialog
|
|
375
391
|
class="ai-dialog"
|
|
376
392
|
v-model="visible"
|
|
@@ -383,55 +399,65 @@ defineExpose({
|
|
|
383
399
|
:modal-penetrable="true"
|
|
384
400
|
>
|
|
385
401
|
<div class="ai-dialog-body">
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
class="message-list"
|
|
389
|
-
>
|
|
402
|
+
<!-- 消息列表 -->
|
|
403
|
+
<div ref="messageListRef" class="message-list">
|
|
390
404
|
<div
|
|
391
405
|
v-for="(message, index) in messageList"
|
|
392
406
|
:key="index"
|
|
393
407
|
:class="message.role"
|
|
394
408
|
class="message-item"
|
|
395
409
|
>
|
|
396
|
-
|
|
397
|
-
|
|
410
|
+
<!-- 有内容 或 是最后一条AI消息且有节点进度时,渲染消息气泡 -->
|
|
411
|
+
<template v-if="message.content || (index === messageList.length - 1 && message.role === 'assistant' && nodeProgressList.length > 0)">
|
|
412
|
+
<!-- 头像 -->
|
|
398
413
|
<div class="avatar">
|
|
399
|
-
<el-avatar
|
|
400
|
-
:size="32"
|
|
401
|
-
:icon="message.role === 'user' ? UserFilled : Service"
|
|
402
|
-
/>
|
|
414
|
+
<el-avatar :size="32" :icon="message.role === 'user' ? UserFilled : Service" />
|
|
403
415
|
</div>
|
|
416
|
+
|
|
404
417
|
<div class="message-content">
|
|
405
|
-
<!--
|
|
406
|
-
<div
|
|
407
|
-
v-if="isJSONSerializable(message.content)"
|
|
408
|
-
class="message-json"
|
|
409
|
-
v-html="renderJSONContent(formatJSONContent(message.content))"
|
|
410
|
-
></div>
|
|
411
|
-
<!-- 普通文本展示 -->
|
|
418
|
+
<!-- 节点执行进度(仅最新AI消息 & 未完成时展示) -->
|
|
412
419
|
<div
|
|
413
|
-
v-
|
|
414
|
-
class="
|
|
420
|
+
v-if="index === messageList.length - 1 && message.role === 'assistant' && nodeProgressList.length > 0 && !showFinalResult"
|
|
421
|
+
class="node-progress"
|
|
415
422
|
>
|
|
416
|
-
|
|
423
|
+
<!-- 已完成/执行中节点 -->
|
|
424
|
+
<div v-for="node in nodeProgressList" :key="node.name" class="node-progress-item">
|
|
425
|
+
<span class="node-status-icon">
|
|
426
|
+
<el-icon v-if="node.status === 'success'" class="node-icon done"><SuccessFilled /></el-icon>
|
|
427
|
+
<el-icon v-else class="node-icon running"><Refresh /></el-icon>
|
|
428
|
+
</span>
|
|
429
|
+
<span class="node-name">{{ node.name }}</span>
|
|
430
|
+
</div>
|
|
431
|
+
<!-- 所有节点完成,等待下一步 -->
|
|
432
|
+
<div
|
|
433
|
+
v-if="nodeProgressList.length > 0 && nodeProgressList.every((n) => n.status === 'success') && !showFinalResult"
|
|
434
|
+
class="node-progress-item next-step"
|
|
435
|
+
>
|
|
436
|
+
<span class="node-status-icon">
|
|
437
|
+
<el-icon class="node-icon waiting"><Refresh /></el-icon>
|
|
438
|
+
</span>
|
|
439
|
+
<span class="node-name">正在执行下一步...</span>
|
|
440
|
+
</div>
|
|
417
441
|
</div>
|
|
442
|
+
|
|
443
|
+
<!-- JSON 结果渲染 -->
|
|
444
|
+
<div v-if="isJSONSerializable(message.content)" class="message-json" v-html="renderJSONContent(formatJSONContent(message.content))"></div>
|
|
445
|
+
<!-- 普通文本 -->
|
|
446
|
+
<div v-else-if="message.content" class="message-text">{{ message.content }}</div>
|
|
447
|
+
|
|
448
|
+
<!-- 消息时间 -->
|
|
418
449
|
<div class="message-createTime">{{ message.createTime }}</div>
|
|
419
|
-
|
|
450
|
+
|
|
451
|
+
<!-- 反馈按钮(仅AI消息) -->
|
|
420
452
|
<template v-if="message.role === 'assistant'">
|
|
421
453
|
<template v-if="message.showFeedback && !message.hasFeedback">
|
|
422
454
|
<div class="message-createTime">请问对本轮查询结果是否满意?</div>
|
|
423
455
|
<div class="feedback-buttons">
|
|
424
|
-
<button
|
|
425
|
-
class="feedback-btn satisfied-btn"
|
|
426
|
-
@click="handleFeedbackAction('satisfied', index)"
|
|
427
|
-
>
|
|
456
|
+
<button class="feedback-btn satisfied-btn" @click="handleFeedbackAction('satisfied', index)">
|
|
428
457
|
<span class="btn-emoji">👍</span>
|
|
429
458
|
<span class="btn-text">满意</span>
|
|
430
459
|
</button>
|
|
431
|
-
<button
|
|
432
|
-
class="feedback-btn unsatisfied-btn"
|
|
433
|
-
@click="handleFeedbackAction('open', index)"
|
|
434
|
-
>
|
|
460
|
+
<button class="feedback-btn unsatisfied-btn" @click="handleFeedbackAction('open', index)">
|
|
435
461
|
<span class="btn-emoji">👎</span>
|
|
436
462
|
<span class="btn-text">不满意</span>
|
|
437
463
|
</button>
|
|
@@ -445,16 +471,13 @@ defineExpose({
|
|
|
445
471
|
</template>
|
|
446
472
|
</div>
|
|
447
473
|
|
|
448
|
-
<!--
|
|
474
|
+
<!-- 首包响应前的思考动画 -->
|
|
449
475
|
<div
|
|
450
|
-
v-if="isThinking && !messageList[messageList.length - 1]?.content"
|
|
476
|
+
v-if="isThinking && nodeProgressList.length === 0 && !messageList[messageList.length - 1]?.content"
|
|
451
477
|
class="message-item assistant"
|
|
452
478
|
>
|
|
453
479
|
<div class="avatar">
|
|
454
|
-
<el-avatar
|
|
455
|
-
:size="32"
|
|
456
|
-
:icon="Service"
|
|
457
|
-
/>
|
|
480
|
+
<el-avatar :size="32" :icon="Service" />
|
|
458
481
|
</div>
|
|
459
482
|
<div class="message-content">
|
|
460
483
|
<div class="typing-indicator">
|
|
@@ -466,6 +489,7 @@ defineExpose({
|
|
|
466
489
|
</div>
|
|
467
490
|
</div>
|
|
468
491
|
|
|
492
|
+
<!-- 输入区域 -->
|
|
469
493
|
<div class="input-area">
|
|
470
494
|
<el-input
|
|
471
495
|
class="message-input"
|
|
@@ -480,14 +504,7 @@ defineExpose({
|
|
|
480
504
|
<div class="input-hint">
|
|
481
505
|
<span>Enter 发送 | Ctrl + Enter 换行</span>
|
|
482
506
|
</div>
|
|
483
|
-
<el-button
|
|
484
|
-
class="send-btn"
|
|
485
|
-
type="primary"
|
|
486
|
-
:icon="Promotion"
|
|
487
|
-
:loading="isSending"
|
|
488
|
-
@click="sendMessage"
|
|
489
|
-
round
|
|
490
|
-
>
|
|
507
|
+
<el-button class="send-btn" type="primary" :icon="Promotion" :loading="isSending" @click="sendMessage" round>
|
|
491
508
|
发送
|
|
492
509
|
</el-button>
|
|
493
510
|
</div>
|
|
@@ -495,30 +512,17 @@ defineExpose({
|
|
|
495
512
|
</div>
|
|
496
513
|
</el-dialog>
|
|
497
514
|
|
|
498
|
-
<!--
|
|
499
|
-
<el-dialog
|
|
500
|
-
v-model="feedbackDialogVisible"
|
|
501
|
-
title="📝 反馈意见"
|
|
502
|
-
width="400px"
|
|
503
|
-
>
|
|
515
|
+
<!-- ==================== 反馈弹窗 ==================== -->
|
|
516
|
+
<el-dialog v-model="feedbackDialogVisible" title="📝 反馈意见" width="400px">
|
|
504
517
|
<div class="feedback-dialog-content">
|
|
505
518
|
<div class="feedback-emoji">😟</div>
|
|
506
519
|
<p class="feedback-tip">很抱歉没能帮到您,请告诉我们哪里需要改进:</p>
|
|
507
|
-
<el-input
|
|
508
|
-
v-model="feedbackContent"
|
|
509
|
-
type="textarea"
|
|
510
|
-
:rows="4"
|
|
511
|
-
placeholder="[非必填]例如:回答不够准确、查询结果有误、界面体验不佳..."
|
|
512
|
-
/>
|
|
520
|
+
<el-input v-model="feedbackContent" type="textarea" :rows="4" placeholder="[非必填]例如:回答不够准确、查询结果有误、界面体验不佳..." />
|
|
513
521
|
</div>
|
|
514
522
|
<template #footer>
|
|
515
523
|
<span class="dialog-footer">
|
|
516
524
|
<el-button @click="feedbackDialogVisible = false">取消</el-button>
|
|
517
|
-
<el-button
|
|
518
|
-
type="primary"
|
|
519
|
-
@click="handleFeedbackAction('unsatisfied', feedbackMessageIndex)"
|
|
520
|
-
>提交反馈</el-button
|
|
521
|
-
>
|
|
525
|
+
<el-button type="primary" @click="handleFeedbackAction('unsatisfied', feedbackMessageIndex)">提交反馈</el-button>
|
|
522
526
|
</span>
|
|
523
527
|
</template>
|
|
524
528
|
</el-dialog>
|
|
@@ -531,45 +535,78 @@ defineExpose({
|
|
|
531
535
|
flex-direction: column;
|
|
532
536
|
height: 480px;
|
|
533
537
|
background: transparent;
|
|
538
|
+
|
|
539
|
+
// ========== 消息列表 ==========
|
|
534
540
|
.message-list {
|
|
535
541
|
flex: 1;
|
|
536
542
|
overflow-y: auto;
|
|
537
543
|
padding: 20px 24px;
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
&::-webkit-scrollbar-track {
|
|
542
|
-
background: rgba(0, 0, 0, 0.05);
|
|
543
|
-
border-radius: 3px;
|
|
544
|
-
}
|
|
544
|
+
|
|
545
|
+
// 滚动条
|
|
546
|
+
&::-webkit-scrollbar { width: 6px; }
|
|
547
|
+
&::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.05); border-radius: 3px; }
|
|
545
548
|
&::-webkit-scrollbar-thumb {
|
|
546
549
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
547
550
|
border-radius: 3px;
|
|
548
|
-
|
|
549
|
-
&:hover {
|
|
550
|
-
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
|
|
551
|
-
}
|
|
551
|
+
&:hover { background: linear-gradient(135deg, #764ba2 0%, #667eea 100%); }
|
|
552
552
|
}
|
|
553
|
-
|
|
553
|
+
|
|
554
|
+
// 单条消息
|
|
554
555
|
.message-item {
|
|
555
556
|
display: flex;
|
|
556
557
|
animation: fadeInUp 0.3s ease-out;
|
|
557
558
|
margin-bottom: 20px;
|
|
559
|
+
|
|
558
560
|
.avatar {
|
|
559
561
|
flex-shrink: 0;
|
|
560
562
|
:deep(.el-avatar) {
|
|
561
563
|
background: linear-gradient(135deg, #f0f2ff 0%, #e8ecff 100%);
|
|
562
564
|
color: #667eea;
|
|
563
|
-
svg {
|
|
564
|
-
width: 18px;
|
|
565
|
-
height: 18px;
|
|
566
|
-
}
|
|
565
|
+
svg { width: 18px; height: 18px; }
|
|
567
566
|
}
|
|
568
567
|
}
|
|
568
|
+
|
|
569
569
|
.message-content {
|
|
570
570
|
display: flex;
|
|
571
571
|
flex-direction: column;
|
|
572
572
|
max-width: 70%;
|
|
573
|
+
|
|
574
|
+
// 节点进度卡片
|
|
575
|
+
.node-progress {
|
|
576
|
+
padding: 12px 16px;
|
|
577
|
+
border-radius: 12px;
|
|
578
|
+
margin-bottom: 8px;
|
|
579
|
+
border: 1px solid rgba(102, 126, 234, 0.1);
|
|
580
|
+
|
|
581
|
+
.node-progress-item {
|
|
582
|
+
display: flex;
|
|
583
|
+
align-items: center;
|
|
584
|
+
gap: 8px;
|
|
585
|
+
padding: 4px 0;
|
|
586
|
+
font-size: 13px;
|
|
587
|
+
color: #6b7280;
|
|
588
|
+
|
|
589
|
+
.node-status-icon {
|
|
590
|
+
display: flex;
|
|
591
|
+
align-items: center;
|
|
592
|
+
justify-content: center;
|
|
593
|
+
width: 18px;
|
|
594
|
+
flex-shrink: 0;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.node-icon {
|
|
598
|
+
font-size: 16px;
|
|
599
|
+
&.done { color: #22c55e; } // 完成:绿色
|
|
600
|
+
&.running { color: #667eea; animation: spin 1.2s linear infinite; } // 执行中:旋转
|
|
601
|
+
&.waiting { color: #d1d5db; animation: spin 1.2s linear infinite; } // 等待中:灰色旋转
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.node-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.next-step .node-name { color: #9ca3af; }
|
|
608
|
+
}
|
|
609
|
+
|
|
573
610
|
.message-json,
|
|
574
611
|
.message-text {
|
|
575
612
|
padding: 10px 16px;
|
|
@@ -578,15 +615,15 @@ defineExpose({
|
|
|
578
615
|
word-wrap: break-word;
|
|
579
616
|
white-space: pre-wrap;
|
|
580
617
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
}
|
|
618
|
+
|
|
619
|
+
.message-createTime { font-size: 11px; color: #9ca3af; margin-top: 6px; }
|
|
620
|
+
|
|
621
|
+
// 反馈按钮
|
|
586
622
|
.feedback-buttons {
|
|
587
623
|
display: flex;
|
|
588
624
|
gap: 12px;
|
|
589
625
|
margin-top: 12px;
|
|
626
|
+
|
|
590
627
|
.feedback-btn {
|
|
591
628
|
display: flex;
|
|
592
629
|
align-items: center;
|
|
@@ -601,57 +638,35 @@ defineExpose({
|
|
|
601
638
|
background: rgba(255, 255, 255, 0.9);
|
|
602
639
|
backdrop-filter: blur(10px);
|
|
603
640
|
|
|
604
|
-
.btn-emoji {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
.btn-text {
|
|
609
|
-
font-size: 13px;
|
|
610
|
-
}
|
|
611
|
-
&:hover {
|
|
612
|
-
transform: translateY(-2px);
|
|
613
|
-
.btn-emoji {
|
|
614
|
-
transform: scale(1.1);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
&:active {
|
|
618
|
-
transform: translateY(0);
|
|
619
|
-
}
|
|
641
|
+
.btn-emoji { font-size: 16px; transition: transform 0.2s ease; }
|
|
642
|
+
.btn-text { font-size: 13px; }
|
|
643
|
+
&:hover { transform: translateY(-2px); .btn-emoji { transform: scale(1.1); } }
|
|
644
|
+
&:active { transform: translateY(0); }
|
|
620
645
|
}
|
|
646
|
+
|
|
621
647
|
.satisfied-btn {
|
|
622
648
|
background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
|
|
623
649
|
color: #1890ff;
|
|
624
650
|
border: 1px solid rgba(24, 144, 255, 0.2);
|
|
625
651
|
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.1);
|
|
626
|
-
|
|
627
|
-
&:hover {
|
|
628
|
-
background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%);
|
|
629
|
-
border-color: #1890ff;
|
|
630
|
-
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.2);
|
|
631
|
-
}
|
|
652
|
+
&:hover { background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%); border-color: #1890ff; box-shadow: 0 4px 12px rgba(24, 144, 255, 0.2); }
|
|
632
653
|
}
|
|
654
|
+
|
|
633
655
|
.unsatisfied-btn {
|
|
634
656
|
background: linear-gradient(135deg, #fff1f0 0%, #ffe7e5 100%);
|
|
635
657
|
color: #ff4d4f;
|
|
636
658
|
border: 1px solid rgba(255, 77, 79, 0.2);
|
|
637
659
|
box-shadow: 0 2px 8px rgba(255, 77, 79, 0.1);
|
|
638
|
-
|
|
639
|
-
&:hover {
|
|
640
|
-
background: linear-gradient(135deg, #ffe7e5 0%, #ffccc7 100%);
|
|
641
|
-
border-color: #ff4d4f;
|
|
642
|
-
box-shadow: 0 4px 12px rgba(255, 77, 79, 0.2);
|
|
643
|
-
}
|
|
660
|
+
&:hover { background: linear-gradient(135deg, #ffe7e5 0%, #ffccc7 100%); border-color: #ff4d4f; box-shadow: 0 4px 12px rgba(255, 77, 79, 0.2); }
|
|
644
661
|
}
|
|
645
662
|
}
|
|
646
663
|
}
|
|
647
664
|
}
|
|
648
|
-
|
|
665
|
+
|
|
666
|
+
// 用户消息靠右
|
|
649
667
|
.user {
|
|
650
668
|
flex-direction: row-reverse;
|
|
651
|
-
.avatar {
|
|
652
|
-
margin-left: 12px;
|
|
653
|
-
margin-right: 0;
|
|
654
|
-
}
|
|
669
|
+
.avatar { margin-left: 12px; margin-right: 0; }
|
|
655
670
|
.message-content {
|
|
656
671
|
align-items: flex-end;
|
|
657
672
|
.message-text {
|
|
@@ -659,16 +674,13 @@ defineExpose({
|
|
|
659
674
|
border-radius: 18px 18px 4px 18px;
|
|
660
675
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
661
676
|
}
|
|
662
|
-
.message-createTime {
|
|
663
|
-
text-align: right;
|
|
664
|
-
}
|
|
677
|
+
.message-createTime { text-align: right; }
|
|
665
678
|
}
|
|
666
679
|
}
|
|
667
|
-
|
|
680
|
+
|
|
681
|
+
// AI消息靠左
|
|
668
682
|
.assistant {
|
|
669
|
-
.avatar {
|
|
670
|
-
margin-right: 12px;
|
|
671
|
-
}
|
|
683
|
+
.avatar { margin-right: 12px; }
|
|
672
684
|
.message-content {
|
|
673
685
|
align-items: flex-start;
|
|
674
686
|
.message-text {
|
|
@@ -681,6 +693,8 @@ defineExpose({
|
|
|
681
693
|
border-radius: 18px 18px 18px 4px;
|
|
682
694
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
683
695
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
696
|
+
|
|
697
|
+
// 解析条件高亮卡片
|
|
684
698
|
:deep(.parsed-conditions) {
|
|
685
699
|
margin-top: 10px;
|
|
686
700
|
.parsed-conditions-item {
|
|
@@ -696,9 +710,12 @@ defineExpose({
|
|
|
696
710
|
}
|
|
697
711
|
}
|
|
698
712
|
}
|
|
713
|
+
|
|
714
|
+
// ========== 输入区域 ==========
|
|
699
715
|
.input-area {
|
|
700
716
|
padding: 16px 24px 24px;
|
|
701
717
|
backdrop-filter: blur(10px);
|
|
718
|
+
|
|
702
719
|
.message-input {
|
|
703
720
|
:deep(.el-textarea__inner) {
|
|
704
721
|
border-radius: 16px;
|
|
@@ -706,39 +723,33 @@ defineExpose({
|
|
|
706
723
|
font-size: 14px;
|
|
707
724
|
padding: 12px 16px;
|
|
708
725
|
transition: all 0.3s ease;
|
|
709
|
-
&:focus {
|
|
710
|
-
border-color: #667eea;
|
|
711
|
-
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
712
|
-
}
|
|
726
|
+
&:focus { border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); }
|
|
713
727
|
}
|
|
714
728
|
}
|
|
729
|
+
|
|
715
730
|
.input-actions {
|
|
716
731
|
display: flex;
|
|
717
732
|
justify-content: space-between;
|
|
718
733
|
align-items: center;
|
|
719
734
|
margin-top: 12px;
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
}
|
|
735
|
+
|
|
736
|
+
.input-hint { font-size: 11px; color: var(--el-color-info); }
|
|
737
|
+
|
|
724
738
|
.send-btn {
|
|
725
739
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
726
740
|
border: none;
|
|
727
741
|
padding: 8px 20px;
|
|
728
742
|
font-weight: 500;
|
|
729
743
|
transition: all 0.3s ease;
|
|
730
|
-
&:hover {
|
|
731
|
-
|
|
732
|
-
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
733
|
-
}
|
|
734
|
-
&:active {
|
|
735
|
-
transform: translateY(0);
|
|
736
|
-
}
|
|
744
|
+
&:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); }
|
|
745
|
+
&:active { transform: translateY(0); }
|
|
737
746
|
}
|
|
738
747
|
}
|
|
739
748
|
}
|
|
740
749
|
}
|
|
741
750
|
}
|
|
751
|
+
|
|
752
|
+
// ========== 思考动画(三个跳动点) ==========
|
|
742
753
|
.typing-indicator {
|
|
743
754
|
display: flex;
|
|
744
755
|
gap: 4px;
|
|
@@ -754,75 +765,45 @@ defineExpose({
|
|
|
754
765
|
border-radius: 50%;
|
|
755
766
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
756
767
|
animation: typing 1.4s infinite ease-in-out;
|
|
757
|
-
|
|
758
|
-
&:nth-child(
|
|
759
|
-
animation-delay: -0.32s;
|
|
760
|
-
}
|
|
761
|
-
&:nth-child(2) {
|
|
762
|
-
animation-delay: -0.16s;
|
|
763
|
-
}
|
|
768
|
+
&:nth-child(1) { animation-delay: -0.32s; }
|
|
769
|
+
&:nth-child(2) { animation-delay: -0.16s; }
|
|
764
770
|
}
|
|
765
771
|
}
|
|
772
|
+
|
|
773
|
+
// ========== 反馈弹窗 ==========
|
|
766
774
|
.feedback-dialog-content {
|
|
767
775
|
text-align: center;
|
|
768
776
|
padding: 12px 0;
|
|
769
777
|
|
|
770
|
-
.feedback-emoji {
|
|
771
|
-
|
|
772
|
-
margin-bottom: 16px;
|
|
773
|
-
animation: shake 0.5s ease-in-out;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
.feedback-tip {
|
|
777
|
-
font-size: 14px;
|
|
778
|
-
color: #666;
|
|
779
|
-
margin-bottom: 20px;
|
|
780
|
-
line-height: 1.6;
|
|
781
|
-
}
|
|
778
|
+
.feedback-emoji { font-size: 48px; margin-bottom: 16px; animation: shake 0.5s ease-in-out; }
|
|
779
|
+
.feedback-tip { font-size: 14px; color: #666; margin-bottom: 20px; line-height: 1.6; }
|
|
782
780
|
|
|
783
781
|
:deep(.el-textarea__inner) {
|
|
784
782
|
border-radius: 12px;
|
|
785
783
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
786
784
|
font-size: 14px;
|
|
787
|
-
|
|
788
|
-
&:focus {
|
|
789
|
-
border-color: #667eea;
|
|
790
|
-
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
791
|
-
}
|
|
785
|
+
&:focus { border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); }
|
|
792
786
|
}
|
|
793
787
|
}
|
|
788
|
+
|
|
789
|
+
// ========== 动画关键帧 ==========
|
|
794
790
|
@keyframes fadeInUp {
|
|
795
|
-
from {
|
|
796
|
-
|
|
797
|
-
transform: translateY(10px);
|
|
798
|
-
}
|
|
799
|
-
to {
|
|
800
|
-
opacity: 1;
|
|
801
|
-
transform: translateY(0);
|
|
802
|
-
}
|
|
791
|
+
from { opacity: 0; transform: translateY(10px); }
|
|
792
|
+
to { opacity: 1; transform: translateY(0); }
|
|
803
793
|
}
|
|
794
|
+
|
|
804
795
|
@keyframes typing {
|
|
805
|
-
0%,
|
|
806
|
-
|
|
807
|
-
100% {
|
|
808
|
-
transform: translateY(0);
|
|
809
|
-
opacity: 0.4;
|
|
810
|
-
}
|
|
811
|
-
30% {
|
|
812
|
-
transform: translateY(-8px);
|
|
813
|
-
opacity: 1;
|
|
814
|
-
}
|
|
796
|
+
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
|
|
797
|
+
30% { transform: translateY(-8px); opacity: 1; }
|
|
815
798
|
}
|
|
799
|
+
|
|
816
800
|
@keyframes shake {
|
|
817
|
-
0%,
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}
|
|
824
|
-
75% {
|
|
825
|
-
transform: translateX(5px);
|
|
826
|
-
}
|
|
801
|
+
0%, 100% { transform: translateX(0); }
|
|
802
|
+
25% { transform: translateX(-5px); }
|
|
803
|
+
75% { transform: translateX(5px); }
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
@keyframes spin {
|
|
807
|
+
to { transform: rotate(360deg); }
|
|
827
808
|
}
|
|
828
|
-
</style>
|
|
809
|
+
</style>
|