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.
Files changed (125) hide show
  1. package/es/CustomFunction.cjs +1 -1
  2. package/es/CustomFunction.js +22 -22
  3. package/es/FactorWarning.cjs +1 -1
  4. package/es/FactorWarning.js +26 -26
  5. package/es/Kline.cjs +1 -1
  6. package/es/Kline.js +11 -11
  7. package/es/KlineBasic.cjs +1 -1
  8. package/es/KlineBasic.js +19 -19
  9. package/es/KlineConfig.cjs +1 -1
  10. package/es/KlineConfig.js +16 -16
  11. package/es/KlineNew.cjs +1 -1
  12. package/es/KlineNew.js +10 -10
  13. package/es/KlinePlus.cjs +1 -1
  14. package/es/KlinePlus.js +12 -12
  15. package/es/MonacoEditor.cjs +1 -1
  16. package/es/MonacoEditor.js +18 -18
  17. package/es/Pagination.cjs +1 -1
  18. package/es/Pagination.js +14 -14
  19. package/es/PasswordPrompt.cjs +1 -1
  20. package/es/PasswordPrompt.js +2 -2
  21. package/es/Table.cjs +1 -1
  22. package/es/Table.js +18 -18
  23. package/es/User.cjs +1 -1
  24. package/es/User.js +19 -19
  25. package/es/VarSelectDialog.cjs +1 -1
  26. package/es/VarSelectDialog.js +19 -19
  27. package/es/VarietyAiHelper.cjs +3 -3
  28. package/es/VarietyAiHelper.js +301 -280
  29. package/es/VarietyAutoComplete.cjs +1 -1
  30. package/es/VarietyAutoComplete.js +8 -8
  31. package/es/VarietySearch.cjs +1 -1
  32. package/es/VarietySearch.js +31 -31
  33. package/es/{VarietySelect-9f267958.js → VarietySelect-1ac10ec2.js} +3 -3
  34. package/es/{VarietySelect-5c845562.cjs → VarietySelect-639a8d9a.cjs} +1 -1
  35. package/es/VarietyTextCopy.cjs +1 -1
  36. package/es/VarietyTextCopy.js +10 -10
  37. package/es/VirtualTable.cjs +1 -1
  38. package/es/VirtualTable.js +66 -66
  39. package/es/{_initCloneObject-69c8ae23.js → _initCloneObject-6b8c230e.js} +2 -2
  40. package/es/{_initCloneObject-2b82e9f7.cjs → _initCloneObject-9eddcb0c.cjs} +1 -1
  41. package/es/aiTools-822859d6.cjs +4 -0
  42. package/es/aiTools-9a9c997f.js +149 -0
  43. package/es/aiTools.js +92 -1
  44. package/es/{config-provider-7860903c.js → config-provider-56193d47.js} +3 -3
  45. package/es/{config-provider-2ae47cc8.cjs → config-provider-ea286661.cjs} +1 -1
  46. package/es/{dropdown-497442b7.js → dropdown-4e541d60.js} +3 -3
  47. package/es/{dropdown-eba9eaf5.cjs → dropdown-f30148cc.cjs} +1 -1
  48. package/es/{el-autocomplete-b59eb529.cjs → el-autocomplete-3949e68c.cjs} +1 -1
  49. package/es/{el-autocomplete-27c60cc8.js → el-autocomplete-4b521c83.js} +6 -6
  50. package/es/{el-button-e2c63c08.js → el-button-4f201000.js} +3 -3
  51. package/es/{el-button-974ff9e9.cjs → el-button-dd065de0.cjs} +1 -1
  52. package/es/{el-checkbox-08185353.cjs → el-checkbox-02ce40f0.cjs} +1 -1
  53. package/es/{el-checkbox-71ebd862.js → el-checkbox-33cb73db.js} +3 -3
  54. package/es/{el-dialog-eedcfd3e.js → el-dialog-1939f7c5.js} +4 -4
  55. package/es/{el-dialog-1b185570.cjs → el-dialog-6501a151.cjs} +1 -1
  56. package/es/{el-form-item-c997b4fa.cjs → el-form-item-316b35d8.cjs} +1 -1
  57. package/es/{el-form-item-bdcfd297.js → el-form-item-ea11211d.js} +5 -5
  58. package/es/{el-input-fa18ef84.cjs → el-input-094afbe2.cjs} +1 -1
  59. package/es/{el-input-d47281da.js → el-input-58786d42.js} +32 -32
  60. package/es/{el-input-number-3d94fa58.cjs → el-input-number-a609a5e3.cjs} +1 -1
  61. package/es/{el-input-number-c8018cb1.js → el-input-number-a7971697.js} +14 -14
  62. package/es/{el-loading-969a79ca.cjs → el-loading-d171ce64.cjs} +1 -1
  63. package/es/{el-loading-0cd81d05.js → el-loading-f3815921.js} +1 -1
  64. package/es/{el-menu-item-26071fd6.cjs → el-menu-item-3d1e0aff.cjs} +1 -1
  65. package/es/{el-menu-item-dac65bb3.js → el-menu-item-a166b997.js} +5 -5
  66. package/es/{el-message-box-31e0aa98.cjs → el-message-box-b8ffcf34.cjs} +1 -1
  67. package/es/{el-message-box-fea4fca8.js → el-message-box-f2b667e9.js} +9 -9
  68. package/es/{el-overlay-1ee0338d.js → el-overlay-1dfe3675.js} +3 -3
  69. package/es/{el-overlay-ea65cb05.cjs → el-overlay-a94f8a98.cjs} +1 -1
  70. package/es/{el-popconfirm-9e232436.cjs → el-popconfirm-70e2849c.cjs} +1 -1
  71. package/es/{el-popconfirm-089b8bec.js → el-popconfirm-ca31ceea.js} +5 -5
  72. package/es/{el-popper-2d3914e4.cjs → el-popper-ce575c12.cjs} +1 -1
  73. package/es/{el-popper-c9b3d3cf.js → el-popper-db6c599f.js} +2 -2
  74. package/es/{el-segmented-140ac042.cjs → el-segmented-4d50b63f.cjs} +1 -1
  75. package/es/{el-segmented-9d3a9e11.js → el-segmented-741f2252.js} +2 -2
  76. package/es/{el-select-e51e11c1.js → el-select-086bcb6c.js} +9 -9
  77. package/es/{el-select-a11f33e8.cjs → el-select-2ebc8380.cjs} +1 -1
  78. package/es/{el-table-column-98570a4d.cjs → el-table-column-2f07fbdb.cjs} +1 -1
  79. package/es/{el-table-column-05d292a8.js → el-table-column-8a15378a.js} +10 -10
  80. package/es/{el-tag-17cd04a1.js → el-tag-65a99986.js} +2 -2
  81. package/es/{el-tag-6d8e653e.cjs → el-tag-f48b1190.cjs} +1 -1
  82. package/es/{el-text-a18106cb.cjs → el-text-33359f44.cjs} +1 -1
  83. package/es/{el-text-2710fff3.js → el-text-cbb693f2.js} +1 -1
  84. package/es/{index-f30561d3.js → index-072c4a65.js} +5 -5
  85. package/es/{index-299ee017.cjs → index-0f767104.cjs} +1 -1
  86. package/es/{index-eb99b188.cjs → index-18565979.cjs} +1 -1
  87. package/es/{index-0f79095c.js → index-1955f23d.js} +1 -1
  88. package/es/{index-0ee486ad.js → index-1e91e986.js} +1 -1
  89. package/es/{index-6ca95c8a.cjs → index-21e8d2bc.cjs} +1 -1
  90. package/es/{index-33f80550.cjs → index-3792552a.cjs} +1 -1
  91. package/es/{index-7dce9f59.cjs → index-4f900527.cjs} +1 -1
  92. package/es/{index-1d9b50de.js → index-57d82da0.js} +2 -2
  93. package/es/{index-e8eeea22.cjs → index-5b546f7d.cjs} +1 -1
  94. package/es/{index-c71e37dc.js → index-7db02db7.js} +2 -2
  95. package/es/{index-37b8d3c6.cjs → index-7eb88616.cjs} +1 -1
  96. package/es/{index-6b99def3.cjs → index-8439d2f9.cjs} +1 -1
  97. package/es/{index-40f05e2c.cjs → index-8b055879.cjs} +2 -2
  98. package/es/{index-b0117ba2.js → index-a4e252a0.js} +1 -1
  99. package/es/{index-95e5d454.js → index-b51915a2.js} +2 -2
  100. package/es/{index-28e03bad.cjs → index-bf98dd03.cjs} +1 -1
  101. package/es/{index-c2b9bbfd.js → index-ca91ac68.js} +82 -67
  102. package/es/{index-8a54ceeb.js → index-d857270a.js} +9 -9
  103. package/es/{index-bcd895a0.js → index-ff26b1a6.js} +2 -2
  104. package/es/{python-02c3937a.cjs → python-5b5c9c58.cjs} +1 -1
  105. package/es/{python-99011a53.js → python-ad9239f9.js} +18 -18
  106. package/es/style.css +1 -1
  107. package/es/{use-form-common-props-d3ed62c6.cjs → use-form-common-props-00ec25ac.cjs} +1 -1
  108. package/es/{use-form-common-props-cb0ca65c.js → use-form-common-props-c14990b9.js} +29 -29
  109. package/es/{use-global-config-c80f33a4.cjs → use-global-config-28efb416.cjs} +1 -1
  110. package/es/{use-global-config-cdaeca54.js → use-global-config-a01b5ce1.js} +2 -2
  111. package/es/{validator-07160325.cjs → validator-119fdaf4.cjs} +1 -1
  112. package/es/{validator-4ab9774f.js → validator-65de1caf.js} +1 -1
  113. package/es/{zh-cn-6a0f844c.cjs → zh-cn-9fb29a39.cjs} +1 -1
  114. package/es/{zh-cn-8a6390a4.js → zh-cn-c1c28e70.js} +1 -1
  115. package/lib/aiTools.js +92 -1
  116. package/lib/bundle.js +1 -1
  117. package/lib/bundle.umd.cjs +202 -201
  118. package/lib/{index-0969ca9d.js → index-7279dacd.js} +16066 -15973
  119. package/lib/{python-506107bf.js → python-60bc2922.js} +1 -1
  120. package/lib/style.css +1 -1
  121. package/package.json +1 -1
  122. package/packages/VarietyAiHelper/index.vue +252 -135
  123. package/public/aiTools.js +92 -1
  124. package/es/aiTools-2e1f92d2.cjs +0 -3
  125. 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 { sendToBaiLianAppStreaming } from "../../public/aiTools";
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: "你好呀!我是你的品种池AI助手,会根据您的自然语言去进行品种的条件查询\n\n示例: \n帮我查A股科创板下品种转价差上证50并且总市值大于1千万, 最近一次红箱的开盘价大于前5根K线的最高价, 并按照总市值升序进行排序",
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 isSending = ref(false); // 是否正在发送消息
24
+ const isThinking = ref(false); // AI是否正在思考(首包到达前)
25
+ const nodeProgressList = ref([]); // 工作流节点执行进度列表
26
+ const showFinalResult = ref(false); // 是否展示最终结果
22
27
 
23
- // loading状态
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", // AI-assistant, 用户-user
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({}); // 当前点击要反馈的message的索引
48
+ const feedbackMessageIndex = ref({});
49
49
 
50
- // 辅助函数:判断是否可JSON序列化
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
- // 辅助函数:解析并格式化JSON内容
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
- // 辅助函数:渲染JSON内容为HTML
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
- const params = {
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
- isSolved: 1, // 是否解决
109
- logOrigin: 1, // 日志来源
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
- const params = {
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
- isSolved: 0, // 是否解决
136
- logOrigin: 1, // 日志来源
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
- const params = {
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
- isSolved: 1, // 是否解决
158
- logOrigin: 1, // 日志来源
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
- // 创建AI消息占位符,记录对应的用户输入
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 = "9e54d112acfe4531bd1fc4fee8827fef";
197
+ const appId = "1977602b357e4dab9d0b74899d5323b3";
199
198
  const apiKey = "sk-d995eb26a4334bdeb2ccb4cbfaf51de8";
200
- await sendToBaiLianAppStreaming({
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
- else if (type === "message") {
230
- fullResponse += data;
231
- // 直接更新最后一条AI消息
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
- else if (type === "finish") {
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
- // 辅助函数: Ctrl+Enter 换行,Enter 发送
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
- const getTotalTagMap = async () => {
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
- <template v-if="message.content">
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
- <!-- 判断是否为可JSON序列化的数据 -->
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
- <!-- 反馈按钮(仅AI侧展示) -->
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
- <!-- AI首包响应思考Loading -->
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
- // AI消息
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>