st-comp 0.0.263 → 0.0.267
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 +301 -280
- 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-822859d6.cjs +4 -0
- package/es/aiTools-9a9c997f.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 +202 -201
- package/lib/{index-0969ca9d.js → index-7279dacd.js} +16066 -15973
- package/lib/{python-506107bf.js → python-60bc2922.js} +1 -1
- package/lib/style.css +1 -1
- package/package.json +1 -1
- package/packages/VarietyAiHelper/index.vue +252 -135
- package/public/aiTools.js +92 -1
- package/es/aiTools-2e1f92d2.cjs +0 -3
- package/es/aiTools-faa0a14d.js +0 -90
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
3
|
import { ElMessage } from "element-plus";
|
|
4
|
-
import { getUserData } from "st-func";
|
|
4
|
+
import { getUserData, getToken } 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,42 @@ 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
|
|
22
|
+
// ==================== 响应式数据 ====================
|
|
23
|
+
const isSending = ref(false); // 是否正在发送消息
|
|
24
|
+
const isThinking = ref(false); // AI是否正在思考(首包到达前)
|
|
25
|
+
const nodeProgressList = ref([]); // 工作流节点执行进度列表
|
|
26
|
+
const showFinalResult = ref(false); // 是否展示最终结果
|
|
22
27
|
|
|
23
|
-
//
|
|
24
|
-
const isSending = ref(false);
|
|
25
|
-
const isThinking = ref(false);
|
|
26
|
-
|
|
27
|
-
// 消息队列
|
|
28
|
+
// ==================== 消息队列 ====================
|
|
28
29
|
const messageListRef = ref(null);
|
|
29
30
|
const messageList = ref([
|
|
30
31
|
{
|
|
31
|
-
role: "assistant",
|
|
32
|
+
role: "assistant",
|
|
32
33
|
content: props.defaultMessage,
|
|
33
34
|
userContent: null,
|
|
34
|
-
showFeedback: false,
|
|
35
|
-
hasFeedback: false,
|
|
36
|
-
createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
|
37
|
-
resTime: 0,
|
|
38
|
-
firstPackageTime: 0,
|
|
35
|
+
showFeedback: false,
|
|
36
|
+
hasFeedback: false,
|
|
37
|
+
createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
|
38
|
+
resTime: 0,
|
|
39
|
+
firstPackageTime: 0,
|
|
39
40
|
},
|
|
40
41
|
]);
|
|
41
42
|
|
|
42
|
-
//
|
|
43
|
-
const userInput = ref("");
|
|
43
|
+
const userInput = ref(""); // 用户输入内容
|
|
44
44
|
|
|
45
|
-
// 反馈弹窗
|
|
45
|
+
// ==================== 反馈弹窗 ====================
|
|
46
46
|
const feedbackDialogVisible = ref(false);
|
|
47
47
|
const feedbackContent = ref("");
|
|
48
|
-
const feedbackMessageIndex = ref({});
|
|
48
|
+
const feedbackMessageIndex = ref({});
|
|
49
49
|
|
|
50
|
-
//
|
|
50
|
+
// ==================== 工具函数 ====================
|
|
51
|
+
|
|
52
|
+
// 判断字符串是否可解析为 JSON 对象
|
|
51
53
|
const isJSONSerializable = (str) => {
|
|
52
54
|
if (typeof str !== "string") return false;
|
|
53
55
|
try {
|
|
@@ -57,25 +59,23 @@ const isJSONSerializable = (str) => {
|
|
|
57
59
|
return false;
|
|
58
60
|
}
|
|
59
61
|
};
|
|
60
|
-
|
|
62
|
+
|
|
63
|
+
// 将 JSON 字符串解析为展示数据(parsedConditions + 其他参数)
|
|
61
64
|
const formatJSONContent = (content) => {
|
|
62
65
|
if (!isJSONSerializable(content)) return null;
|
|
63
66
|
try {
|
|
64
67
|
const jsonData = JSON.parse(content);
|
|
65
68
|
const { parsedConditions, ...webParams } = jsonData;
|
|
66
|
-
return {
|
|
67
|
-
parsedConditions: parsedConditions,
|
|
68
|
-
webParams,
|
|
69
|
-
};
|
|
69
|
+
return { parsedConditions, webParams };
|
|
70
70
|
} catch {
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
|
-
|
|
74
|
+
|
|
75
|
+
// 将解析后的数据渲染为 HTML 字符串
|
|
75
76
|
const renderJSONContent = (jsonData) => {
|
|
76
77
|
if (!jsonData) return "";
|
|
77
78
|
let html = "";
|
|
78
|
-
// 渲染 parsedConditions
|
|
79
79
|
if (jsonData.parsedConditions?.length) {
|
|
80
80
|
html += '<div class="parsed-conditions">';
|
|
81
81
|
jsonData.parsedConditions.forEach((text) => {
|
|
@@ -83,20 +83,17 @@ const renderJSONContent = (jsonData) => {
|
|
|
83
83
|
});
|
|
84
84
|
html += "</div>";
|
|
85
85
|
}
|
|
86
|
-
// 渲染 webParams
|
|
87
|
-
// html += '<div class="web-params">';
|
|
88
|
-
// html += `<div>${JSON.stringify(jsonData.webParams)}</div>`;
|
|
89
|
-
// html += "</div>";
|
|
90
86
|
return html;
|
|
91
87
|
};
|
|
92
88
|
|
|
93
|
-
//
|
|
89
|
+
// ==================== 反馈处理 ====================
|
|
90
|
+
|
|
91
|
+
// 处理用户反馈操作(满意 / 不满意 / 默认记录)
|
|
94
92
|
const handleFeedbackAction = async (action, messageIndex) => {
|
|
95
93
|
switch (action) {
|
|
96
|
-
// 窗口: 提交(满意)
|
|
97
94
|
case "satisfied": {
|
|
98
95
|
const message = messageList.value[messageIndex];
|
|
99
|
-
|
|
96
|
+
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", {
|
|
100
97
|
userName: userData.username,
|
|
101
98
|
userContent: message.userContent,
|
|
102
99
|
aiContent: message.content,
|
|
@@ -104,25 +101,21 @@ const handleFeedbackAction = async (action, messageIndex) => {
|
|
|
104
101
|
createTime: message.createTime,
|
|
105
102
|
resTime: message.resTime,
|
|
106
103
|
firstPackageTime: message.firstPackageTime,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", params);
|
|
104
|
+
isSolved: 1,
|
|
105
|
+
logOrigin: 1,
|
|
106
|
+
});
|
|
112
107
|
ElMessage.success("感谢您的评价!");
|
|
113
108
|
messageList.value[messageIndex].hasFeedback = true;
|
|
114
109
|
break;
|
|
115
110
|
}
|
|
116
|
-
// 窗口: 打开
|
|
117
111
|
case "open": {
|
|
118
112
|
feedbackMessageIndex.value = messageIndex;
|
|
119
113
|
feedbackDialogVisible.value = true;
|
|
120
114
|
break;
|
|
121
115
|
}
|
|
122
|
-
// 窗口: 提交(不满意)
|
|
123
116
|
case "unsatisfied": {
|
|
124
117
|
const message = messageList.value[messageIndex];
|
|
125
|
-
|
|
118
|
+
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", {
|
|
126
119
|
userName: userData.username,
|
|
127
120
|
userContent: message.userContent,
|
|
128
121
|
userSuggestion: feedbackContent.value,
|
|
@@ -131,20 +124,17 @@ const handleFeedbackAction = async (action, messageIndex) => {
|
|
|
131
124
|
resTime: message.resTime,
|
|
132
125
|
createTime: message.createTime,
|
|
133
126
|
firstPackageTime: message.firstPackageTime,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
};
|
|
138
|
-
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", params);
|
|
127
|
+
isSolved: 0,
|
|
128
|
+
logOrigin: 1,
|
|
129
|
+
});
|
|
139
130
|
ElMessage.success("感谢您的反馈!我们将持续跟踪并进行优化");
|
|
140
131
|
feedbackDialogVisible.value = false;
|
|
141
132
|
messageList.value[messageIndex].hasFeedback = true;
|
|
142
133
|
break;
|
|
143
134
|
}
|
|
144
|
-
// 自动提交记录跟踪日志
|
|
145
135
|
case "default": {
|
|
146
136
|
const message = messageList.value[messageIndex];
|
|
147
|
-
|
|
137
|
+
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", {
|
|
148
138
|
userName: userData.username,
|
|
149
139
|
userContent: message.userContent,
|
|
150
140
|
userSuggestion: feedbackContent.value,
|
|
@@ -153,21 +143,28 @@ const handleFeedbackAction = async (action, messageIndex) => {
|
|
|
153
143
|
resTime: message.resTime,
|
|
154
144
|
createTime: message.createTime,
|
|
155
145
|
firstPackageTime: message.firstPackageTime,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
};
|
|
160
|
-
await stConfig.request.post("/alarm/deliversign/addVarietyAiHelperLog", params);
|
|
146
|
+
isSolved: 1,
|
|
147
|
+
logOrigin: 1,
|
|
148
|
+
});
|
|
161
149
|
}
|
|
162
150
|
}
|
|
163
151
|
};
|
|
164
|
-
|
|
152
|
+
|
|
153
|
+
// ==================== 消息发送 ====================
|
|
154
|
+
|
|
155
|
+
// ==================== 消息发送 ====================
|
|
156
|
+
|
|
157
|
+
// 发送消息到工作流
|
|
165
158
|
const sendMessage = async () => {
|
|
166
159
|
const content = userInput.value.trim();
|
|
167
160
|
if (!content) return ElMessage.warning("请输入消息内容");
|
|
168
161
|
if (isSending.value) return;
|
|
169
162
|
|
|
170
|
-
//
|
|
163
|
+
// 重置状态
|
|
164
|
+
nodeProgressList.value = [];
|
|
165
|
+
showFinalResult.value = false;
|
|
166
|
+
|
|
167
|
+
// 添加用户消息
|
|
171
168
|
messageList.value.push({
|
|
172
169
|
role: "user",
|
|
173
170
|
content: content,
|
|
@@ -176,7 +173,7 @@ const sendMessage = async () => {
|
|
|
176
173
|
userInput.value = "";
|
|
177
174
|
await scrollToBottom();
|
|
178
175
|
|
|
179
|
-
//
|
|
176
|
+
// 添加 AI 占位消息
|
|
180
177
|
messageList.value.push({
|
|
181
178
|
role: "assistant",
|
|
182
179
|
content: "",
|
|
@@ -194,23 +191,23 @@ const sendMessage = async () => {
|
|
|
194
191
|
|
|
195
192
|
let fullResponse = "";
|
|
196
193
|
let resTime = new Date().getTime();
|
|
194
|
+
let firstNodeReceived = false; // 标记是否已收到首个节点推送
|
|
195
|
+
|
|
197
196
|
try {
|
|
198
|
-
const appId = "
|
|
197
|
+
const appId = "1977602b357e4dab9d0b74899d5323b3";
|
|
199
198
|
const apiKey = "sk-d995eb26a4334bdeb2ccb4cbfaf51de8";
|
|
200
|
-
await
|
|
199
|
+
await sendToBaiLianWorkflowStreaming({
|
|
200
|
+
mode: import.meta.env.MODE,
|
|
201
201
|
appId,
|
|
202
202
|
apiKey,
|
|
203
|
+
token: getToken(),
|
|
203
204
|
value: content,
|
|
204
205
|
callback: (type, data) => {
|
|
205
|
-
//
|
|
206
|
+
// 工作流返回错误
|
|
206
207
|
if (type === "error") {
|
|
207
208
|
isThinking.value = false;
|
|
208
209
|
isSending.value = false;
|
|
209
|
-
|
|
210
|
-
// 移除占位的AI消息
|
|
211
210
|
messageList.value.pop();
|
|
212
|
-
|
|
213
|
-
// 添加错误提示消息
|
|
214
211
|
messageList.value.push({
|
|
215
212
|
role: "assistant",
|
|
216
213
|
userContent: content,
|
|
@@ -225,51 +222,57 @@ const sendMessage = async () => {
|
|
|
225
222
|
scrollToBottom();
|
|
226
223
|
return;
|
|
227
224
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
//
|
|
225
|
+
|
|
226
|
+
// 节点执行进度更新
|
|
227
|
+
if (type === "node") {
|
|
228
|
+
// 首个节点推送时记录首包响应时间
|
|
229
|
+
if (!firstNodeReceived) {
|
|
230
|
+
firstNodeReceived = true;
|
|
231
|
+
const lastMessage = messageList.value[messageList.value.length - 1];
|
|
232
|
+
if (lastMessage && lastMessage.role === "assistant" && lastMessage.firstPackageTime === 0) {
|
|
233
|
+
lastMessage.firstPackageTime = new Date().getTime() - resTime;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const existing = nodeProgressList.value.find((n) => n.name === data.name);
|
|
238
|
+
if (existing) {
|
|
239
|
+
existing.status = data.status;
|
|
240
|
+
} else {
|
|
241
|
+
nodeProgressList.value.push({ name: data.name, status: data.status });
|
|
242
|
+
}
|
|
243
|
+
scrollToBottom();
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// 最终结果(工作流结束时一次性推送)
|
|
248
|
+
if (type === "message") {
|
|
249
|
+
fullResponse = data;
|
|
232
250
|
const lastMessage = messageList.value[messageList.value.length - 1];
|
|
233
251
|
if (lastMessage && lastMessage.role === "assistant") {
|
|
234
252
|
lastMessage.content = fullResponse;
|
|
235
253
|
scrollToBottom();
|
|
236
254
|
}
|
|
237
|
-
// 记录首包响应耗时
|
|
238
|
-
if (lastMessage.firstPackageTime === 0) {
|
|
239
|
-
lastMessage.firstPackageTime = new Date().getTime() - resTime;
|
|
240
|
-
}
|
|
241
255
|
}
|
|
242
|
-
|
|
243
|
-
|
|
256
|
+
|
|
257
|
+
// 流式结束,展示反馈按钮并触发回调
|
|
258
|
+
if (type === "finish") {
|
|
244
259
|
isThinking.value = false;
|
|
245
260
|
isSending.value = false;
|
|
261
|
+
showFinalResult.value = true;
|
|
246
262
|
const lastMessage = messageList.value[messageList.value.length - 1];
|
|
247
263
|
|
|
248
|
-
// 显示反馈按钮
|
|
249
264
|
if (lastMessage && lastMessage.role === "assistant") {
|
|
250
265
|
lastMessage.showFeedback = true;
|
|
251
266
|
lastMessage.resTime = new Date().getTime() - resTime;
|
|
252
267
|
handleFeedbackAction("default", messageList.value.length - 1);
|
|
253
268
|
}
|
|
254
269
|
|
|
255
|
-
//
|
|
256
|
-
console.log(fullResponse);
|
|
270
|
+
// 解析最终结果并回调父组件
|
|
257
271
|
try {
|
|
258
272
|
const jsonResponse = JSON.parse(fullResponse);
|
|
259
|
-
// 切割处理: parsedConditions, 这个字段仅做AI提炼展示使用
|
|
260
273
|
if (jsonResponse.parsedConditions) {
|
|
261
274
|
delete jsonResponse.parsedConditions;
|
|
262
275
|
}
|
|
263
|
-
// 切割处理: customTagNames, 这个字段需要被转换成customTag
|
|
264
|
-
if (jsonResponse.customTagNames?.length) {
|
|
265
|
-
const customTag = jsonResponse.customTagNames.reduce((result, item) => {
|
|
266
|
-
const id = tagMap.value[item];
|
|
267
|
-
if (id) result.push(id);
|
|
268
|
-
return result;
|
|
269
|
-
}, []);
|
|
270
|
-
delete jsonResponse.customTagNames;
|
|
271
|
-
jsonResponse.customTag = customTag;
|
|
272
|
-
}
|
|
273
276
|
emit("callBack", jsonResponse);
|
|
274
277
|
} catch (error) {
|
|
275
278
|
emit("callBack", fullResponse);
|
|
@@ -278,10 +281,9 @@ const sendMessage = async () => {
|
|
|
278
281
|
},
|
|
279
282
|
});
|
|
280
283
|
} catch (error) {
|
|
284
|
+
// 网络异常处理
|
|
281
285
|
ElMessage.error(`AI响应异常: ${error}`);
|
|
282
|
-
// 移除占位的AI消息
|
|
283
286
|
messageList.value.pop();
|
|
284
|
-
// 添加错误提示消息
|
|
285
287
|
messageList.value.push({
|
|
286
288
|
role: "assistant",
|
|
287
289
|
userContent: content,
|
|
@@ -299,53 +301,38 @@ const sendMessage = async () => {
|
|
|
299
301
|
}
|
|
300
302
|
};
|
|
301
303
|
|
|
302
|
-
//
|
|
304
|
+
// ==================== 键盘事件 ====================
|
|
305
|
+
|
|
306
|
+
// Enter 发送,Ctrl+Enter 换行
|
|
303
307
|
const handleKeydown = (event) => {
|
|
304
308
|
if (event.key === "Enter") {
|
|
305
309
|
if (event.ctrlKey) {
|
|
306
|
-
// Ctrl + Enter: 插入换行符
|
|
307
310
|
event.preventDefault();
|
|
308
311
|
const textarea = event.target;
|
|
309
312
|
const start = textarea.selectionStart;
|
|
310
313
|
const end = textarea.selectionEnd;
|
|
311
314
|
userInput.value = userInput.value.substring(0, start) + "\n" + userInput.value.substring(end);
|
|
312
|
-
// 将光标移动到新插入的换行符之后
|
|
313
315
|
nextTick(() => {
|
|
314
316
|
textarea.selectionStart = textarea.selectionEnd = start + 1;
|
|
315
317
|
});
|
|
316
318
|
} else {
|
|
317
|
-
// 单独的 Enter: 发送消息
|
|
318
319
|
event.preventDefault();
|
|
319
320
|
sendMessage();
|
|
320
321
|
}
|
|
321
322
|
}
|
|
322
323
|
};
|
|
323
|
-
|
|
324
|
+
|
|
325
|
+
// ==================== 滚动 & 初始化 ====================
|
|
326
|
+
|
|
327
|
+
// 自动滚动到消息列表底部
|
|
324
328
|
const scrollToBottom = async () => {
|
|
325
329
|
await nextTick();
|
|
326
330
|
if (messageListRef.value) {
|
|
327
331
|
messageListRef.value.scrollTop = messageListRef.value.scrollHeight;
|
|
328
332
|
}
|
|
329
333
|
};
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const res = await Promise.all([stConfig.request.post("/alarm/deliversign/findTagsByUserId"), stConfig.request.post("/alarm/deliversign/findSystemTagsByTagName")]);
|
|
333
|
-
tagMap.value = res.reduce((result, item) => {
|
|
334
|
-
return {
|
|
335
|
-
...result,
|
|
336
|
-
...item.body?.reduce((cR, cI) => {
|
|
337
|
-
return {
|
|
338
|
-
...cR,
|
|
339
|
-
[cI.tagName]: cI.id,
|
|
340
|
-
};
|
|
341
|
-
}, {}),
|
|
342
|
-
};
|
|
343
|
-
}, {});
|
|
344
|
-
};
|
|
345
|
-
onMounted(() => {
|
|
346
|
-
getTotalTagMap();
|
|
347
|
-
});
|
|
348
|
-
// 监视消息队列 => 自动滚动
|
|
334
|
+
|
|
335
|
+
// 监听消息队列变化,自动滚动
|
|
349
336
|
watch(
|
|
350
337
|
() => messageList.value,
|
|
351
338
|
() => {
|
|
@@ -353,13 +340,16 @@ watch(
|
|
|
353
340
|
},
|
|
354
341
|
{ deep: true },
|
|
355
342
|
);
|
|
356
|
-
|
|
343
|
+
|
|
344
|
+
// 关闭反馈弹窗时清空内容
|
|
357
345
|
watch(
|
|
358
346
|
() => feedbackDialogVisible.value,
|
|
359
347
|
(newValue) => {
|
|
360
348
|
if (!newValue) feedbackContent.value = "";
|
|
361
349
|
},
|
|
362
350
|
);
|
|
351
|
+
|
|
352
|
+
// 暴露 open 方法供父组件调用
|
|
363
353
|
defineExpose({
|
|
364
354
|
open: () => {
|
|
365
355
|
visible.value = true;
|
|
@@ -371,6 +361,7 @@ defineExpose({
|
|
|
371
361
|
</script>
|
|
372
362
|
|
|
373
363
|
<template>
|
|
364
|
+
<!-- ==================== 主对话框 ==================== -->
|
|
374
365
|
<el-dialog
|
|
375
366
|
class="ai-dialog"
|
|
376
367
|
v-model="visible"
|
|
@@ -383,6 +374,7 @@ defineExpose({
|
|
|
383
374
|
:modal-penetrable="true"
|
|
384
375
|
>
|
|
385
376
|
<div class="ai-dialog-body">
|
|
377
|
+
<!-- 消息列表 -->
|
|
386
378
|
<div
|
|
387
379
|
ref="messageListRef"
|
|
388
380
|
class="message-list"
|
|
@@ -393,30 +385,72 @@ defineExpose({
|
|
|
393
385
|
:class="message.role"
|
|
394
386
|
class="message-item"
|
|
395
387
|
>
|
|
396
|
-
|
|
397
|
-
|
|
388
|
+
<!-- 有内容 或 是最后一条AI消息且有节点进度时,渲染消息气泡 -->
|
|
389
|
+
<template v-if="message.content || (index === messageList.length - 1 && message.role === 'assistant' && nodeProgressList.length > 0)">
|
|
390
|
+
<!-- 头像 -->
|
|
398
391
|
<div class="avatar">
|
|
399
392
|
<el-avatar
|
|
400
393
|
:size="32"
|
|
401
394
|
:icon="message.role === 'user' ? UserFilled : Service"
|
|
402
395
|
/>
|
|
403
396
|
</div>
|
|
397
|
+
|
|
404
398
|
<div class="message-content">
|
|
405
|
-
<!--
|
|
399
|
+
<!-- 节点执行进度(仅最新AI消息 & 未完成时展示) -->
|
|
400
|
+
<div
|
|
401
|
+
v-if="index === messageList.length - 1 && message.role === 'assistant' && nodeProgressList.length > 0 && !showFinalResult"
|
|
402
|
+
class="node-progress"
|
|
403
|
+
>
|
|
404
|
+
<!-- 已完成/执行中节点 -->
|
|
405
|
+
<div
|
|
406
|
+
v-for="node in nodeProgressList"
|
|
407
|
+
:key="node.name"
|
|
408
|
+
class="node-progress-item"
|
|
409
|
+
>
|
|
410
|
+
<span class="node-status-icon">
|
|
411
|
+
<el-icon
|
|
412
|
+
v-if="node.status === 'success'"
|
|
413
|
+
class="node-icon done"
|
|
414
|
+
><SuccessFilled
|
|
415
|
+
/></el-icon>
|
|
416
|
+
<el-icon
|
|
417
|
+
v-else
|
|
418
|
+
class="node-icon running"
|
|
419
|
+
><Refresh
|
|
420
|
+
/></el-icon>
|
|
421
|
+
</span>
|
|
422
|
+
<span class="node-name">{{ node.name }}</span>
|
|
423
|
+
</div>
|
|
424
|
+
<!-- 所有节点完成,等待下一步 -->
|
|
425
|
+
<div
|
|
426
|
+
v-if="nodeProgressList.length > 0 && nodeProgressList.every((n) => n.status === 'success') && !showFinalResult"
|
|
427
|
+
class="node-progress-item next-step"
|
|
428
|
+
>
|
|
429
|
+
<span class="node-status-icon">
|
|
430
|
+
<el-icon class="node-icon waiting"><Refresh /></el-icon>
|
|
431
|
+
</span>
|
|
432
|
+
<span class="node-name">正在执行下一步...</span>
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
|
|
436
|
+
<!-- JSON 结果渲染 -->
|
|
406
437
|
<div
|
|
407
438
|
v-if="isJSONSerializable(message.content)"
|
|
408
439
|
class="message-json"
|
|
409
440
|
v-html="renderJSONContent(formatJSONContent(message.content))"
|
|
410
441
|
></div>
|
|
411
|
-
<!--
|
|
442
|
+
<!-- 普通文本 -->
|
|
412
443
|
<div
|
|
413
|
-
v-else
|
|
444
|
+
v-else-if="message.content"
|
|
414
445
|
class="message-text"
|
|
415
446
|
>
|
|
416
447
|
{{ message.content }}
|
|
417
448
|
</div>
|
|
449
|
+
|
|
450
|
+
<!-- 消息时间 -->
|
|
418
451
|
<div class="message-createTime">{{ message.createTime }}</div>
|
|
419
|
-
|
|
452
|
+
|
|
453
|
+
<!-- 反馈按钮(仅AI消息) -->
|
|
420
454
|
<template v-if="message.role === 'assistant'">
|
|
421
455
|
<template v-if="message.showFeedback && !message.hasFeedback">
|
|
422
456
|
<div class="message-createTime">请问对本轮查询结果是否满意?</div>
|
|
@@ -445,9 +479,9 @@ defineExpose({
|
|
|
445
479
|
</template>
|
|
446
480
|
</div>
|
|
447
481
|
|
|
448
|
-
<!--
|
|
482
|
+
<!-- 首包响应前的思考动画 -->
|
|
449
483
|
<div
|
|
450
|
-
v-if="isThinking && !messageList[messageList.length - 1]?.content"
|
|
484
|
+
v-if="isThinking && nodeProgressList.length === 0 && !messageList[messageList.length - 1]?.content"
|
|
451
485
|
class="message-item assistant"
|
|
452
486
|
>
|
|
453
487
|
<div class="avatar">
|
|
@@ -466,6 +500,7 @@ defineExpose({
|
|
|
466
500
|
</div>
|
|
467
501
|
</div>
|
|
468
502
|
|
|
503
|
+
<!-- 输入区域 -->
|
|
469
504
|
<div class="input-area">
|
|
470
505
|
<el-input
|
|
471
506
|
class="message-input"
|
|
@@ -495,7 +530,7 @@ defineExpose({
|
|
|
495
530
|
</div>
|
|
496
531
|
</el-dialog>
|
|
497
532
|
|
|
498
|
-
<!--
|
|
533
|
+
<!-- ==================== 反馈弹窗 ==================== -->
|
|
499
534
|
<el-dialog
|
|
500
535
|
v-model="feedbackDialogVisible"
|
|
501
536
|
title="📝 反馈意见"
|
|
@@ -531,10 +566,14 @@ defineExpose({
|
|
|
531
566
|
flex-direction: column;
|
|
532
567
|
height: 480px;
|
|
533
568
|
background: transparent;
|
|
569
|
+
|
|
570
|
+
// ========== 消息列表 ==========
|
|
534
571
|
.message-list {
|
|
535
572
|
flex: 1;
|
|
536
573
|
overflow-y: auto;
|
|
537
574
|
padding: 20px 24px;
|
|
575
|
+
|
|
576
|
+
// 滚动条
|
|
538
577
|
&::-webkit-scrollbar {
|
|
539
578
|
width: 6px;
|
|
540
579
|
}
|
|
@@ -545,16 +584,17 @@ defineExpose({
|
|
|
545
584
|
&::-webkit-scrollbar-thumb {
|
|
546
585
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
547
586
|
border-radius: 3px;
|
|
548
|
-
|
|
549
587
|
&:hover {
|
|
550
588
|
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
|
|
551
589
|
}
|
|
552
590
|
}
|
|
553
|
-
|
|
591
|
+
|
|
592
|
+
// 单条消息
|
|
554
593
|
.message-item {
|
|
555
594
|
display: flex;
|
|
556
595
|
animation: fadeInUp 0.3s ease-out;
|
|
557
596
|
margin-bottom: 20px;
|
|
597
|
+
|
|
558
598
|
.avatar {
|
|
559
599
|
flex-shrink: 0;
|
|
560
600
|
:deep(.el-avatar) {
|
|
@@ -566,10 +606,62 @@ defineExpose({
|
|
|
566
606
|
}
|
|
567
607
|
}
|
|
568
608
|
}
|
|
609
|
+
|
|
569
610
|
.message-content {
|
|
570
611
|
display: flex;
|
|
571
612
|
flex-direction: column;
|
|
572
613
|
max-width: 70%;
|
|
614
|
+
|
|
615
|
+
// 节点进度卡片
|
|
616
|
+
.node-progress {
|
|
617
|
+
padding: 12px 16px;
|
|
618
|
+
border-radius: 12px;
|
|
619
|
+
margin-bottom: 8px;
|
|
620
|
+
border: 1px solid rgba(102, 126, 234, 0.1);
|
|
621
|
+
|
|
622
|
+
.node-progress-item {
|
|
623
|
+
display: flex;
|
|
624
|
+
align-items: center;
|
|
625
|
+
gap: 8px;
|
|
626
|
+
padding: 4px 0;
|
|
627
|
+
font-size: 13px;
|
|
628
|
+
color: #6b7280;
|
|
629
|
+
|
|
630
|
+
.node-status-icon {
|
|
631
|
+
display: flex;
|
|
632
|
+
align-items: center;
|
|
633
|
+
justify-content: center;
|
|
634
|
+
width: 18px;
|
|
635
|
+
flex-shrink: 0;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.node-icon {
|
|
639
|
+
font-size: 16px;
|
|
640
|
+
&.done {
|
|
641
|
+
color: #22c55e;
|
|
642
|
+
} // 完成:绿色
|
|
643
|
+
&.running {
|
|
644
|
+
color: #667eea;
|
|
645
|
+
animation: spin 1.2s linear infinite;
|
|
646
|
+
} // 执行中:旋转
|
|
647
|
+
&.waiting {
|
|
648
|
+
color: #d1d5db;
|
|
649
|
+
animation: spin 1.2s linear infinite;
|
|
650
|
+
} // 等待中:灰色旋转
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.node-name {
|
|
654
|
+
white-space: nowrap;
|
|
655
|
+
overflow: hidden;
|
|
656
|
+
text-overflow: ellipsis;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.next-step .node-name {
|
|
661
|
+
color: #9ca3af;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
573
665
|
.message-json,
|
|
574
666
|
.message-text {
|
|
575
667
|
padding: 10px 16px;
|
|
@@ -578,15 +670,19 @@ defineExpose({
|
|
|
578
670
|
word-wrap: break-word;
|
|
579
671
|
white-space: pre-wrap;
|
|
580
672
|
}
|
|
673
|
+
|
|
581
674
|
.message-createTime {
|
|
582
675
|
font-size: 11px;
|
|
583
676
|
color: #9ca3af;
|
|
584
677
|
margin-top: 6px;
|
|
585
678
|
}
|
|
679
|
+
|
|
680
|
+
// 反馈按钮
|
|
586
681
|
.feedback-buttons {
|
|
587
682
|
display: flex;
|
|
588
683
|
gap: 12px;
|
|
589
684
|
margin-top: 12px;
|
|
685
|
+
|
|
590
686
|
.feedback-btn {
|
|
591
687
|
display: flex;
|
|
592
688
|
align-items: center;
|
|
@@ -618,24 +714,24 @@ defineExpose({
|
|
|
618
714
|
transform: translateY(0);
|
|
619
715
|
}
|
|
620
716
|
}
|
|
717
|
+
|
|
621
718
|
.satisfied-btn {
|
|
622
719
|
background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
|
|
623
720
|
color: #1890ff;
|
|
624
721
|
border: 1px solid rgba(24, 144, 255, 0.2);
|
|
625
722
|
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.1);
|
|
626
|
-
|
|
627
723
|
&:hover {
|
|
628
724
|
background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%);
|
|
629
725
|
border-color: #1890ff;
|
|
630
726
|
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.2);
|
|
631
727
|
}
|
|
632
728
|
}
|
|
729
|
+
|
|
633
730
|
.unsatisfied-btn {
|
|
634
731
|
background: linear-gradient(135deg, #fff1f0 0%, #ffe7e5 100%);
|
|
635
732
|
color: #ff4d4f;
|
|
636
733
|
border: 1px solid rgba(255, 77, 79, 0.2);
|
|
637
734
|
box-shadow: 0 2px 8px rgba(255, 77, 79, 0.1);
|
|
638
|
-
|
|
639
735
|
&:hover {
|
|
640
736
|
background: linear-gradient(135deg, #ffe7e5 0%, #ffccc7 100%);
|
|
641
737
|
border-color: #ff4d4f;
|
|
@@ -645,7 +741,8 @@ defineExpose({
|
|
|
645
741
|
}
|
|
646
742
|
}
|
|
647
743
|
}
|
|
648
|
-
|
|
744
|
+
|
|
745
|
+
// 用户消息靠右
|
|
649
746
|
.user {
|
|
650
747
|
flex-direction: row-reverse;
|
|
651
748
|
.avatar {
|
|
@@ -664,7 +761,8 @@ defineExpose({
|
|
|
664
761
|
}
|
|
665
762
|
}
|
|
666
763
|
}
|
|
667
|
-
|
|
764
|
+
|
|
765
|
+
// AI消息靠左
|
|
668
766
|
.assistant {
|
|
669
767
|
.avatar {
|
|
670
768
|
margin-right: 12px;
|
|
@@ -681,6 +779,8 @@ defineExpose({
|
|
|
681
779
|
border-radius: 18px 18px 18px 4px;
|
|
682
780
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
683
781
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
782
|
+
|
|
783
|
+
// 解析条件高亮卡片
|
|
684
784
|
:deep(.parsed-conditions) {
|
|
685
785
|
margin-top: 10px;
|
|
686
786
|
.parsed-conditions-item {
|
|
@@ -696,9 +796,12 @@ defineExpose({
|
|
|
696
796
|
}
|
|
697
797
|
}
|
|
698
798
|
}
|
|
799
|
+
|
|
800
|
+
// ========== 输入区域 ==========
|
|
699
801
|
.input-area {
|
|
700
802
|
padding: 16px 24px 24px;
|
|
701
803
|
backdrop-filter: blur(10px);
|
|
804
|
+
|
|
702
805
|
.message-input {
|
|
703
806
|
:deep(.el-textarea__inner) {
|
|
704
807
|
border-radius: 16px;
|
|
@@ -712,15 +815,18 @@ defineExpose({
|
|
|
712
815
|
}
|
|
713
816
|
}
|
|
714
817
|
}
|
|
818
|
+
|
|
715
819
|
.input-actions {
|
|
716
820
|
display: flex;
|
|
717
821
|
justify-content: space-between;
|
|
718
822
|
align-items: center;
|
|
719
823
|
margin-top: 12px;
|
|
824
|
+
|
|
720
825
|
.input-hint {
|
|
721
826
|
font-size: 11px;
|
|
722
827
|
color: var(--el-color-info);
|
|
723
828
|
}
|
|
829
|
+
|
|
724
830
|
.send-btn {
|
|
725
831
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
726
832
|
border: none;
|
|
@@ -739,6 +845,8 @@ defineExpose({
|
|
|
739
845
|
}
|
|
740
846
|
}
|
|
741
847
|
}
|
|
848
|
+
|
|
849
|
+
// ========== 思考动画(三个跳动点) ==========
|
|
742
850
|
.typing-indicator {
|
|
743
851
|
display: flex;
|
|
744
852
|
gap: 4px;
|
|
@@ -754,7 +862,6 @@ defineExpose({
|
|
|
754
862
|
border-radius: 50%;
|
|
755
863
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
756
864
|
animation: typing 1.4s infinite ease-in-out;
|
|
757
|
-
|
|
758
865
|
&:nth-child(1) {
|
|
759
866
|
animation-delay: -0.32s;
|
|
760
867
|
}
|
|
@@ -763,6 +870,8 @@ defineExpose({
|
|
|
763
870
|
}
|
|
764
871
|
}
|
|
765
872
|
}
|
|
873
|
+
|
|
874
|
+
// ========== 反馈弹窗 ==========
|
|
766
875
|
.feedback-dialog-content {
|
|
767
876
|
text-align: center;
|
|
768
877
|
padding: 12px 0;
|
|
@@ -772,7 +881,6 @@ defineExpose({
|
|
|
772
881
|
margin-bottom: 16px;
|
|
773
882
|
animation: shake 0.5s ease-in-out;
|
|
774
883
|
}
|
|
775
|
-
|
|
776
884
|
.feedback-tip {
|
|
777
885
|
font-size: 14px;
|
|
778
886
|
color: #666;
|
|
@@ -784,13 +892,14 @@ defineExpose({
|
|
|
784
892
|
border-radius: 12px;
|
|
785
893
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
786
894
|
font-size: 14px;
|
|
787
|
-
|
|
788
895
|
&:focus {
|
|
789
896
|
border-color: #667eea;
|
|
790
897
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
791
898
|
}
|
|
792
899
|
}
|
|
793
900
|
}
|
|
901
|
+
|
|
902
|
+
// ========== 动画关键帧 ==========
|
|
794
903
|
@keyframes fadeInUp {
|
|
795
904
|
from {
|
|
796
905
|
opacity: 0;
|
|
@@ -801,6 +910,7 @@ defineExpose({
|
|
|
801
910
|
transform: translateY(0);
|
|
802
911
|
}
|
|
803
912
|
}
|
|
913
|
+
|
|
804
914
|
@keyframes typing {
|
|
805
915
|
0%,
|
|
806
916
|
60%,
|
|
@@ -813,6 +923,7 @@ defineExpose({
|
|
|
813
923
|
opacity: 1;
|
|
814
924
|
}
|
|
815
925
|
}
|
|
926
|
+
|
|
816
927
|
@keyframes shake {
|
|
817
928
|
0%,
|
|
818
929
|
100% {
|
|
@@ -825,4 +936,10 @@ defineExpose({
|
|
|
825
936
|
transform: translateX(5px);
|
|
826
937
|
}
|
|
827
938
|
}
|
|
939
|
+
|
|
940
|
+
@keyframes spin {
|
|
941
|
+
to {
|
|
942
|
+
transform: rotate(360deg);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
828
945
|
</style>
|