@blueking/chat-helper 0.0.1-beta.8 → 0.0.2

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 (42) hide show
  1. package/README.md +701 -167
  2. package/dist/agent/type.d.ts +27 -2
  3. package/dist/agent/type.ts.js +1 -1
  4. package/dist/agent/use-agent.d.ts +460 -413
  5. package/dist/agent/use-agent.ts.js +191 -44
  6. package/dist/event/ag-ui.d.ts +37 -7
  7. package/dist/event/ag-ui.ts.js +225 -165
  8. package/dist/event/type.d.ts +99 -107
  9. package/dist/event/type.ts.js +34 -11
  10. package/dist/http/fetch/fetch.d.ts +2 -1
  11. package/dist/http/fetch/fetch.ts.js +38 -8
  12. package/dist/http/fetch/index.d.ts +1 -0
  13. package/dist/http/fetch/index.ts.js +17 -1
  14. package/dist/http/index.d.ts +14 -5
  15. package/dist/http/index.ts.js +59 -3
  16. package/dist/http/module/index.d.ts +13 -5
  17. package/dist/http/module/index.ts.js +2 -1
  18. package/dist/http/module/message.d.ts +22 -5
  19. package/dist/http/module/message.ts.js +51 -4
  20. package/dist/http/module/session.d.ts +4 -1
  21. package/dist/http/module/session.ts.js +66 -4
  22. package/dist/http/transform/agent.ts.js +11 -8
  23. package/dist/http/transform/message.d.ts +6 -6
  24. package/dist/http/transform/message.ts.js +566 -118
  25. package/dist/http/transform/session.ts.js +9 -1
  26. package/dist/index.d.ts +2989 -2845
  27. package/dist/index.ts.js +27 -5
  28. package/dist/mediator/index.d.ts +2 -0
  29. package/dist/mediator/index.ts.js +26 -0
  30. package/dist/mediator/type.d.ts +50 -0
  31. package/dist/mediator/type.ts.js +28 -0
  32. package/dist/mediator/use-mediator.d.ts +7 -0
  33. package/dist/mediator/use-mediator.ts.js +47 -0
  34. package/dist/message/type.d.ts +280 -142
  35. package/dist/message/type.ts.js +16 -15
  36. package/dist/message/use-message.d.ts +839 -819
  37. package/dist/message/use-message.ts.js +230 -37
  38. package/dist/session/type.d.ts +10 -0
  39. package/dist/session/use-session.d.ts +1921 -1857
  40. package/dist/session/use-session.ts.js +198 -33
  41. package/dist/type.d.ts +1 -1
  42. package/package.json +11 -6
package/README.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  一个用于 Vue3 的聊天助手工具包,提供完整的 AI 对话功能,支持流式响应、会话管理、消息管理等功能。
4
4
 
5
+ ## 特性
6
+
7
+ - 🚀 **Vue3 Composition API**:基于 Vue3 的响应式系统,提供流畅的开发体验
8
+ - 💬 **完整的会话管理**:支持多会话创建、切换、更新和删除
9
+ - 📨 **消息管理**:支持消息列表、添加、修改、删除和批量操作
10
+ - 🤖 **AI Agent 集成**:轻松接入 AI 代理,获取代理信息和进行对话
11
+ - 🌊 **流式响应**:支持 SSE(Server-Sent Events)流式响应,实时展示 AI 回复
12
+ - 🎯 **中介者模式**:模块间通过中介者协调通信,降低耦合度
13
+ - 🔧 **高度可定制**:支持自定义拦截器、自定义 Protocol、自定义请求配置
14
+ - 📝 **TypeScript 支持**:完整的类型定义,提供良好的类型提示
15
+
5
16
  ## 安装
6
17
 
7
18
  ```bash
@@ -27,6 +38,34 @@ const chatHelper = useChatHelper({
27
38
  const { agent, session, message, http } = chatHelper;
28
39
  ```
29
40
 
41
+ ## 架构设计
42
+
43
+ `@blueking/chat-helper` 采用**中介者模式(Mediator Pattern)**进行架构设计,将复杂的模块间通信集中到中介者对象中,降低了各模块之间的耦合度。
44
+
45
+ ### 核心模块
46
+
47
+ - **Agent 模块**:管理 AI 代理相关功能,包括获取代理信息、开始聊天、停止聊天等
48
+ - **Session 模块**:管理聊天会话,包括会话列表、创建、切换、更新和删除
49
+ - **Message 模块**:管理聊天消息,包括消息列表、添加、修改和删除
50
+ - **HTTP 模块**:底层 HTTP 请求封装,提供各种 API 调用方法和 SSE 流式响应支持
51
+ - **Mediator 模块**:中介者,协调各模块之间的通信,避免模块间直接依赖
52
+
53
+ ### 数据流向
54
+
55
+ ```
56
+ 用户操作 → Agent/Session/Message 模块 → Mediator → HTTP 模块 → 后端 API
57
+ ↑ ↓
58
+ ←────────── 响应数据/流式事件 ←─────────────
59
+ ```
60
+
61
+ ### Protocol 系统
62
+
63
+ `@blueking/chat-helper` 提供了可扩展的 Protocol 系统来处理 SSE 流式响应:
64
+
65
+ - **ISSEProtocol 接口**:定义了流式响应的处理接口
66
+ - **AGUIProtocol 实现**:默认的 AGUI 协议实现,支持丰富的事件类型
67
+ - **自定义 Protocol**:用户可以实现自己的 Protocol 来处理特定的流式响应格式
68
+
30
69
  ## useChatHelper 返回数据说明
31
70
 
32
71
  `useChatHelper` 返回一个包含四个模块的对象:
@@ -41,16 +80,16 @@ const { agent } = chatHelper;
41
80
  // 响应式数据
42
81
  agent.info; // Ref<IAgentInfo | null> - agent 信息
43
82
  agent.isInfoLoading; // Ref<boolean> - agent 信息加载状态
44
- agent.userInput; // Ref<string> - 用户输入内容
45
83
 
46
84
  // 方法
47
85
  agent.getAgentInfo(); // () => Promise<void> - 获取 agent 信息
48
- agent.chat(sessionCode, url?, config?); // (sessionCode: string, url?: string, config?: IRequestConfig) => Promise<void> - 开始聊天(流式响应)
86
+ agent.chat(userInput, sessionCode, url?, config?); // (userInput: string, sessionCode: string, url?: string, config?: IRequestConfig) => Promise<void> - 开始聊天(流式响应)
49
87
  agent.stopChat(); // () => Promise<void> - 停止聊天
50
88
  ```
51
89
 
52
90
  **参数说明**:
53
91
 
92
+ - `userInput`: 用户输入的消息内容,必需参数
54
93
  - `sessionCode`: 会话代码,必需参数,标识当前聊天会话
55
94
  - `url`: 可选参数,自定义聊天接口地址,默认为 `'chat_completion/'`
56
95
  - `config`: 可选参数,额外的请求配置(如自定义 headers、data 等)
@@ -62,11 +101,11 @@ agent.stopChat(); // () => Promise<void> - 停止聊天
62
101
  <div>
63
102
  <div v-if="agent.isInfoLoading">加载中...</div>
64
103
  <div v-else-if="agent.info">
65
- <h2>{{ agent.info.name }}</h2>
66
- <p>{{ agent.info.description }}</p>
104
+ <h2>{{ agent.info.agentName }}</h2>
105
+ <p>{{ agent.info.conversationSettings?.openingRemark }}</p>
67
106
  </div>
68
107
  <input
69
- v-model="agent.userInput"
108
+ v-model="userInput"
70
109
  placeholder="请输入消息..."
71
110
  />
72
111
  <button @click="startChat">开始对话</button>
@@ -75,20 +114,23 @@ agent.stopChat(); // () => Promise<void> - 停止聊天
75
114
  </template>
76
115
 
77
116
  <script setup lang="ts">
78
- import { onMounted } from 'vue';
117
+ import { onMounted, ref } from 'vue';
79
118
  import { useChatHelper } from '@blueking/chat-helper';
80
119
 
81
120
  const { agent, session } = useChatHelper({
82
121
  /* ... */
83
122
  });
84
123
 
124
+ const userInput = ref('');
125
+
85
126
  onMounted(() => {
86
127
  agent.getAgentInfo();
87
128
  });
88
129
 
89
130
  const startChat = () => {
90
- if (session.current?.sessionCode) {
91
- agent.chat(session.current.sessionCode);
131
+ if (session.current?.sessionCode && userInput.value.trim()) {
132
+ agent.chat(userInput.value, session.current.sessionCode);
133
+ userInput.value = ''; // 清空输入框
92
134
  }
93
135
  };
94
136
  </script>
@@ -98,10 +140,10 @@ agent.stopChat(); // () => Promise<void> - 停止聊天
98
140
 
99
141
  ```typescript
100
142
  // 使用自定义 URL
101
- agent.chat(sessionCode, 'custom_chat_endpoint/');
143
+ agent.chat('你好', sessionCode, 'custom_chat_endpoint/');
102
144
 
103
145
  // 使用自定义请求配置
104
- agent.chat(sessionCode, undefined, {
146
+ agent.chat('你好', sessionCode, undefined, {
105
147
  headers: {
106
148
  'X-Custom-Header': 'value',
107
149
  },
@@ -112,7 +154,7 @@ agent.chat(sessionCode, undefined, {
112
154
  });
113
155
 
114
156
  // 同时使用自定义 URL 和配置
115
- agent.chat(sessionCode, 'custom_chat_endpoint/', {
157
+ agent.chat('你好', sessionCode, 'custom_chat_endpoint/', {
116
158
  headers: {
117
159
  'X-Custom-Header': 'value',
118
160
  },
@@ -156,7 +198,7 @@ session.deleteSession(sessionCode); // (sessionCode: string) => Promise<void> -
156
198
  @click="session.chooseSession(item.sessionCode)"
157
199
  :class="{ active: session.current?.sessionCode === item.sessionCode }"
158
200
  >
159
- {{ item.title }}
201
+ {{ item.sessionName }}
160
202
  </li>
161
203
  </ul>
162
204
  <button @click="createNewSession">新建会话</button>
@@ -177,7 +219,8 @@ session.deleteSession(sessionCode); // (sessionCode: string) => Promise<void> -
177
219
 
178
220
  const createNewSession = () => {
179
221
  session.createSession({
180
- title: '新会话',
222
+ sessionName: '新会话',
223
+ sessionCode: `session_${Date.now()}`,
181
224
  // ... 其他会话属性
182
225
  });
183
226
  };
@@ -199,25 +242,23 @@ message.isBatchDeleteLoading; // Ref<boolean> - 批量删除加载状态
199
242
 
200
243
  // 方法
201
244
  message.getMessages(sessionCode); // (sessionCode: string) => Promise<void> - 获取消息列表
202
- message.plusLatestMessage(sessionCode); // (sessionCode: string) => Promise<void> - 从接口获取最新的消息并添加到列表前面
203
245
  message.plusMessage(message); // (message: IMessage) => Promise<void> - 添加消息
204
246
  message.modifyMessage(message); // (message: IMessage) => void - 修改消息
205
- message.deleteMessage(id); // (id: number) => Promise<void> - 删除消息
206
- message.batchDeleteMessages(ids); // (ids: number[]) => Promise<void> - 批量删除消息
247
+ message.deleteMessage(id); // (id: string) => Promise<void> - 删除消息
248
+ message.batchDeleteMessages(ids); // (ids: string[]) => Promise<void> - 批量删除消息
207
249
  message.getCurrentLoadingMessage(); // () => IMessage | undefined - 获取当前加载中的消息
208
- message.getMessageById(id); // (id: number) => IMessage | undefined - 根据 ID 获取消息
250
+ message.getMessageByMessageId(id); // (id: string) => IMessage | undefined - 根据消息 ID 获取消息
209
251
  ```
210
252
 
211
253
  **方法说明**:
212
254
 
213
255
  - `getMessages`: 获取指定会话的所有消息列表
214
- - `plusLatestMessage`: 从后端获取最新的一条消息并添加到列表最前面(常用于消息同步或刷新)
215
- - `plusMessage`: 主动添加一条新消息到列表(会先调用后端接口创建消息)
216
- - `modifyMessage`: 修改已存在的消息
217
- - `deleteMessage`: 删除单条消息
218
- - `batchDeleteMessages`: 批量删除多条消息
219
- - `getCurrentLoadingMessage`: 获取当前正在加载/流式传输中的 AI 响应消息
220
- - `getMessageById`: 根据 ID 从列表中查找消息
256
+ - `plusMessage`: 主动添加一条新消息到列表(会先调用后端接口创建消息,然后添加到列表最前面)
257
+ - `modifyMessage`: 修改已存在的消息(本地更新,不调用接口)
258
+ - `deleteMessage`: 删除单条消息(调用接口删除并从列表中移除)
259
+ - `batchDeleteMessages`: 批量删除多条消息(调用接口批量删除并从列表中移除)
260
+ - `getCurrentLoadingMessage`: 获取当前正在加载/流式传输中的 AI 响应消息(状态为 Pending 或 Streaming)
261
+ - `getMessageByMessageId`: 根据消息 ID 从列表中查找消息
221
262
 
222
263
  **使用示例**:
223
264
 
@@ -226,21 +267,20 @@ message.getMessageById(id); // (id: number) => IMessage | undefined - 根据 ID
226
267
  <div class="message-list">
227
268
  <div
228
269
  v-for="msg in message.list"
229
- :key="msg.id"
270
+ :key="msg.messageId"
230
271
  class="message-item"
231
272
  >
232
273
  <div class="role">{{ msg.role }}</div>
233
274
  <div class="content">
234
- <div
235
- v-for="(content, index) in msg.content"
236
- :key="index"
237
- >
238
- <template v-if="content.type === 'text'">
239
- {{ content.data }}
240
- </template>
275
+ <!-- 根据消息类型显示不同内容 -->
276
+ <div v-if="msg.role === 'user'">
277
+ {{ typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content) }}
278
+ </div>
279
+ <div v-else-if="msg.role === 'assistant'">
280
+ {{ msg.content || '' }}
241
281
  </div>
242
282
  </div>
243
- <button @click="message.deleteMessage(msg.id)">删除</button>
283
+ <button @click="message.deleteMessage(msg.messageId)">删除</button>
244
284
  </div>
245
285
  <button
246
286
  @click="batchDelete"
@@ -259,20 +299,13 @@ message.getMessageById(id); // (id: number) => IMessage | undefined - 根据 ID
259
299
  /* ... */
260
300
  });
261
301
 
262
- const selectedIds = ref<number[]>([]);
302
+ const selectedIds = ref<string[]>([]);
263
303
 
264
304
  const batchDelete = () => {
265
305
  if (selectedIds.value.length > 0) {
266
306
  message.batchDeleteMessages(selectedIds.value);
267
307
  }
268
308
  };
269
-
270
- // 刷新最新消息(例如在多端同步场景)
271
- const refreshLatestMessage = () => {
272
- if (session.current?.sessionCode) {
273
- message.plusLatestMessage(session.current.sessionCode);
274
- }
275
- };
276
309
  </script>
277
310
  ```
278
311
 
@@ -758,7 +791,7 @@ class SafeProtocol extends AGUIProtocol {
758
791
 
759
792
  ### 场景 1:带输入框的聊天界面
760
793
 
761
- 使用 `agent.userInput` 实现双向绑定:
794
+ 使用独立的 `ref` 管理用户输入:
762
795
 
763
796
  ```vue
764
797
  <template>
@@ -773,7 +806,7 @@ class SafeProtocol extends AGUIProtocol {
773
806
  </div>
774
807
  <div class="input-box">
775
808
  <textarea
776
- v-model="agent.userInput"
809
+ v-model="userInput"
777
810
  @keydown.enter.prevent="handleSend"
778
811
  placeholder="输入消息,按 Enter 发送..."
779
812
  :disabled="isStreaming"
@@ -796,15 +829,18 @@ class SafeProtocol extends AGUIProtocol {
796
829
  /* ... */
797
830
  });
798
831
  const isStreaming = ref(false);
832
+ const userInput = ref('');
799
833
 
800
834
  const canSend = computed(
801
- () => !isStreaming.value && agent.userInput.value.trim().length > 0 && session.current?.sessionCode,
835
+ () => !isStreaming.value && userInput.value.trim().length > 0 && session.current?.sessionCode,
802
836
  );
803
837
 
804
838
  const handleSend = () => {
805
839
  if (canSend.value) {
806
840
  isStreaming.value = true;
807
- agent.chat(session.current!.sessionCode).finally(() => {
841
+ const input = userInput.value;
842
+ userInput.value = ''; // 立即清空输入框
843
+ agent.chat(input, session.current!.sessionCode).finally(() => {
808
844
  isStreaming.value = false;
809
845
  });
810
846
  }
@@ -833,13 +869,13 @@ class SafeProtocol extends AGUIProtocol {
833
869
  <div class="messages">
834
870
  <div
835
871
  v-for="msg in message.list"
836
- :key="msg.id"
837
- @click="toggleSelection(msg.id)"
838
- :class="{ selected: selectedIds.includes(msg.id) }"
872
+ :key="msg.messageId"
873
+ @click="toggleSelection(msg.messageId)"
874
+ :class="{ selected: selectedIds.includes(msg.messageId) }"
839
875
  >
840
876
  <input
841
877
  type="checkbox"
842
- :checked="selectedIds.includes(msg.id)"
878
+ :checked="selectedIds.includes(msg.messageId)"
843
879
  @click.stop
844
880
  />
845
881
  <!-- 消息内容 -->
@@ -855,9 +891,9 @@ class SafeProtocol extends AGUIProtocol {
855
891
  const { message } = useChatHelper({
856
892
  /* ... */
857
893
  });
858
- const selectedIds = ref<number[]>([]);
894
+ const selectedIds = ref<string[]>([]);
859
895
 
860
- const toggleSelection = (id: number) => {
896
+ const toggleSelection = (id: string) => {
861
897
  const index = selectedIds.value.indexOf(id);
862
898
  if (index > -1) {
863
899
  selectedIds.value.splice(index, 1);
@@ -867,7 +903,7 @@ class SafeProtocol extends AGUIProtocol {
867
903
  };
868
904
 
869
905
  const selectAll = () => {
870
- selectedIds.value = message.list.value.filter(msg => msg.id).map(msg => msg.id!);
906
+ selectedIds.value = message.list.value.filter(msg => msg.messageId).map(msg => msg.messageId!);
871
907
  };
872
908
 
873
909
  const clearSelection = () => {
@@ -883,76 +919,7 @@ class SafeProtocol extends AGUIProtocol {
883
919
  </script>
884
920
  ```
885
921
 
886
- ### 场景 3:消息实时同步
887
-
888
- 在多端同步场景下,使用 `plusLatestMessage` 获取最新消息:
889
-
890
- ```vue
891
- <template>
892
- <div class="sync-chat">
893
- <div class="header">
894
- <button
895
- @click="syncLatest"
896
- :disabled="isSyncing"
897
- >
898
- {{ isSyncing ? '同步中...' : '同步最新消息' }}
899
- </button>
900
- <span v-if="lastSyncTime"> 上次同步: {{ lastSyncTime.toLocaleTimeString() }} </span>
901
- </div>
902
-
903
- <div class="messages">
904
- <div
905
- v-for="msg in message.list"
906
- :key="msg.id"
907
- >
908
- <!-- 消息渲染 -->
909
- </div>
910
- </div>
911
- </div>
912
- </template>
913
-
914
- <script setup lang="ts">
915
- import { ref, onMounted, onUnmounted } from 'vue';
916
- import { useChatHelper } from '@blueking/chat-helper';
917
-
918
- const { message, session } = useChatHelper({
919
- /* ... */
920
- });
921
- const isSyncing = ref(false);
922
- const lastSyncTime = ref<Date | null>(null);
923
- let syncTimer: number | null = null;
924
-
925
- // 手动同步
926
- const syncLatest = async () => {
927
- if (!session.current?.sessionCode || isSyncing.value) return;
928
-
929
- isSyncing.value = true;
930
- try {
931
- await message.plusLatestMessage(session.current.sessionCode);
932
- lastSyncTime.value = new Date();
933
- } catch (error) {
934
- console.error('同步失败:', error);
935
- } finally {
936
- isSyncing.value = false;
937
- }
938
- };
939
-
940
- // 自动同步(每30秒)
941
- onMounted(() => {
942
- syncTimer = window.setInterval(() => {
943
- syncLatest();
944
- }, 30000);
945
- });
946
-
947
- onUnmounted(() => {
948
- if (syncTimer) {
949
- clearInterval(syncTimer);
950
- }
951
- });
952
- </script>
953
- ```
954
-
955
- ### 场景 4:自定义聊天参数
922
+ ### 场景 3:自定义聊天参数
956
923
 
957
924
  根据不同场景使用不同的聊天配置:
958
925
 
@@ -989,7 +956,7 @@ class SafeProtocol extends AGUIProtocol {
989
956
  </div>
990
957
 
991
958
  <textarea
992
- v-model="agent.userInput"
959
+ v-model="userInput"
993
960
  placeholder="输入消息..."
994
961
  />
995
962
  <button @click="sendWithConfig">发送</button>
@@ -1004,12 +971,13 @@ class SafeProtocol extends AGUIProtocol {
1004
971
  /* ... */
1005
972
  });
1006
973
 
974
+ const userInput = ref('');
1007
975
  const temperature = ref(0.7);
1008
976
  const maxTokens = ref(1000);
1009
977
  const mode = ref('default');
1010
978
 
1011
979
  const sendWithConfig = () => {
1012
- if (!session.current?.sessionCode) return;
980
+ if (!session.current?.sessionCode || !userInput.value.trim()) return;
1013
981
 
1014
982
  // 根据模式设置不同的端点
1015
983
  const endpoint =
@@ -1020,7 +988,7 @@ class SafeProtocol extends AGUIProtocol {
1020
988
  : undefined;
1021
989
 
1022
990
  // 自定义请求参数
1023
- agent.chat(session.current.sessionCode, endpoint, {
991
+ agent.chat(userInput.value, session.current.sessionCode, endpoint, {
1024
992
  data: {
1025
993
  temperature: temperature.value,
1026
994
  max_tokens: maxTokens.value,
@@ -1030,6 +998,8 @@ class SafeProtocol extends AGUIProtocol {
1030
998
  'X-Chat-Mode': mode.value,
1031
999
  },
1032
1000
  });
1001
+
1002
+ userInput.value = ''; // 清空输入框
1033
1003
  };
1034
1004
  </script>
1035
1005
  ```
@@ -1051,7 +1021,7 @@ class SafeProtocol extends AGUIProtocol {
1051
1021
  @click="session.chooseSession(item.sessionCode)"
1052
1022
  :class="{ active: session.current?.sessionCode === item.sessionCode }"
1053
1023
  >
1054
- {{ item.title }}
1024
+ {{ item.sessionName }}
1055
1025
  <button @click.stop="session.deleteSession(item.sessionCode)">删除</button>
1056
1026
  </li>
1057
1027
  </ul>
@@ -1066,49 +1036,25 @@ class SafeProtocol extends AGUIProtocol {
1066
1036
  >
1067
1037
  <div
1068
1038
  v-for="msg in message.list"
1069
- :key="msg.id"
1039
+ :key="msg.messageId"
1070
1040
  :class="['message-item', msg.role]"
1071
1041
  >
1072
1042
  <div class="message-content">
1073
- <template
1074
- v-for="(content, index) in msg.content"
1075
- :key="index"
1076
- >
1077
- <!-- 文本消息 -->
1078
- <div
1079
- v-if="content.type === 'text'"
1080
- class="text-content"
1081
- >
1082
- {{ content.data }}
1083
- </div>
1084
-
1085
- <!-- 思考过程 -->
1086
- <div
1087
- v-else-if="content.type === 'thinking'"
1088
- class="thinking-content"
1089
- >
1090
- <strong>{{ content.data.title }}</strong>
1091
- <p>{{ content.data.text }}</p>
1092
- </div>
1093
-
1094
- <!-- 工具调用 -->
1095
- <div
1096
- v-else-if="content.type === 'tool_call'"
1097
- class="tool-call-content"
1098
- >
1099
- <strong>调用工具: {{ content.data.toolCallName }}</strong>
1100
- <pre>{{ content.data.args }}</pre>
1101
- </div>
1043
+ <template v-if="msg.role === 'user'">
1044
+ {{ typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content) }}
1045
+ </template>
1046
+ <template v-else-if="msg.role === 'assistant'">
1047
+ {{ msg.content || '' }}
1102
1048
  </template>
1103
1049
  </div>
1104
- <button @click="message.deleteMessage(msg.id)">删除</button>
1050
+ <button @click="message.deleteMessage(msg.messageId)">删除</button>
1105
1051
  </div>
1106
1052
  </div>
1107
1053
 
1108
1054
  <!-- 输入区域 -->
1109
1055
  <div class="input-area">
1110
1056
  <input
1111
- v-model="agent.userInput"
1057
+ v-model="userInput"
1112
1058
  @keyup.enter="sendMessage"
1113
1059
  placeholder="输入消息..."
1114
1060
  :disabled="isStreaming"
@@ -1135,6 +1081,7 @@ class SafeProtocol extends AGUIProtocol {
1135
1081
  import { useChatHelper, AGUIProtocol } from '@blueking/chat-helper';
1136
1082
 
1137
1083
  const isStreaming = ref(false);
1084
+ const userInput = ref('');
1138
1085
 
1139
1086
  const { agent, session, message } = useChatHelper({
1140
1087
  requestData: {
@@ -1186,14 +1133,15 @@ class SafeProtocol extends AGUIProtocol {
1186
1133
 
1187
1134
  const createNewSession = () => {
1188
1135
  session.createSession({
1189
- title: `会话 ${new Date().toLocaleString()}`,
1136
+ sessionName: `会话 ${new Date().toLocaleString()}`,
1190
1137
  sessionCode: `session_${Date.now()}`,
1191
1138
  });
1192
1139
  };
1193
1140
 
1194
1141
  const sendMessage = () => {
1195
- if (session.current?.sessionCode && agent.userInput.value.trim()) {
1196
- agent.chat(session.current.sessionCode);
1142
+ if (session.current?.sessionCode && userInput.value.trim()) {
1143
+ agent.chat(userInput.value, session.current.sessionCode);
1144
+ userInput.value = ''; // 清空输入框
1197
1145
  }
1198
1146
  };
1199
1147
  </script>
@@ -1267,7 +1215,7 @@ class SafeProtocol extends AGUIProtocol {
1267
1215
  <div class="messages">
1268
1216
  <div
1269
1217
  v-for="msg in message.list"
1270
- :key="msg.id"
1218
+ :key="msg.messageId"
1271
1219
  class="message"
1272
1220
  >
1273
1221
  <template
@@ -1306,6 +1254,12 @@ class SafeProtocol extends AGUIProtocol {
1306
1254
 
1307
1255
  <!-- 输入区域 -->
1308
1256
  <div class="input-area">
1257
+ <input
1258
+ v-model="userInput"
1259
+ @keyup.enter="sendMessage"
1260
+ placeholder="输入消息..."
1261
+ :disabled="isStreaming"
1262
+ />
1309
1263
  <button
1310
1264
  @click="sendMessage"
1311
1265
  :disabled="isStreaming"
@@ -1334,6 +1288,7 @@ class SafeProtocol extends AGUIProtocol {
1334
1288
 
1335
1289
  const isStreaming = ref(false);
1336
1290
  const status = ref<{ type: string; message: string } | null>(null);
1291
+ const userInput = ref('');
1337
1292
 
1338
1293
  // 自定义 Protocol,添加各种钩子
1339
1294
  class CustomChatProtocol extends AGUIProtocol {
@@ -1370,7 +1325,7 @@ class SafeProtocol extends AGUIProtocol {
1370
1325
  }
1371
1326
  }
1372
1327
 
1373
- const { agent } = useChatHelper({
1328
+ const { agent, session, message } = useChatHelper({
1374
1329
  requestData: {
1375
1330
  urlPrefix: 'https://your-api-domain.com/api/',
1376
1331
  },
@@ -1410,7 +1365,10 @@ class SafeProtocol extends AGUIProtocol {
1410
1365
  });
1411
1366
 
1412
1367
  const sendMessage = () => {
1413
- agent.chat();
1368
+ if (session.current?.sessionCode && userInput.value.trim()) {
1369
+ agent.chat(userInput.value, session.current.sessionCode);
1370
+ userInput.value = ''; // 清空输入框
1371
+ }
1414
1372
  };
1415
1373
 
1416
1374
  const stopStreaming = () => {
@@ -1464,19 +1422,595 @@ class SafeProtocol extends AGUIProtocol {
1464
1422
 
1465
1423
  ```typescript
1466
1424
  import type {
1425
+ // 主要配置和模块类型
1467
1426
  IUseChatHelperOptions,
1468
1427
  IAgentModule,
1469
1428
  ISessionModule,
1470
1429
  IMessageModule,
1471
1430
  IHttpModule,
1431
+
1432
+ // 核心数据类型
1472
1433
  IMessage,
1473
1434
  ISession,
1474
- MessageRole,
1475
- MessageStatus,
1476
- MessageContentType,
1435
+ IAgentInfo,
1436
+
1437
+ // 消息相关枚举
1438
+ MessageRole, // 消息角色:user, assistant, system, tool, activity, reasoning 等
1439
+ MessageStatus, // 消息状态:pending, streaming, complete, error, stop
1440
+ MessageType, // 消息类型:text, binary, function
1441
+
1442
+ // 协议相关
1443
+ ISSEProtocol,
1444
+ AGUIProtocol,
1445
+
1446
+ // 事件类型(用于自定义 Protocol)
1447
+ IEvent,
1448
+ IRunStartedEvent,
1449
+ IRunFinishedEvent,
1450
+ IRunErrorEvent,
1451
+ ITextMessageChunkEvent,
1452
+ IThinkingStartEvent,
1453
+ IThinkingEndEvent,
1454
+ IToolCallStartEvent,
1455
+ // ... 更多事件类型
1477
1456
  } from '@blueking/chat-helper';
1478
1457
  ```
1479
1458
 
1459
+ ### MessageRole 枚举
1460
+
1461
+ 消息支持多种角色类型:
1462
+
1463
+ ```typescript
1464
+ enum MessageRole {
1465
+ User = 'user', // 用户消息
1466
+ Assistant = 'assistant', // AI 助手消息
1467
+ System = 'system', // 系统消息
1468
+ Tool = 'tool', // 工具调用结果
1469
+ Activity = 'activity', // 活动消息
1470
+ Reasoning = 'reasoning', // 推理过程
1471
+ Developer = 'developer', // 开发者消息
1472
+ Guide = 'guide', // 引导消息
1473
+ Info = 'info', // 信息消息
1474
+ Pause = 'pause', // 暂停消息
1475
+ Placeholder = 'placeholder', // 占位消息
1476
+ Hidden = 'hidden', // 隐藏消息
1477
+ HiddenUser = 'hidden-user', // 隐藏的用户消息
1478
+ HiddenAssistant = 'hidden-assistant', // 隐藏的助手消息
1479
+ HiddenSystem = 'hidden-system', // 隐藏的系统消息
1480
+ HiddenGuide = 'hidden-guide', // 隐藏的引导消息
1481
+ TemplateUser = 'template-user', // 用户消息模板
1482
+ TemplateAssistant = 'template-assistant', // 助手消息模板
1483
+ TemplateSystem = 'template-system', // 系统消息模板
1484
+ TemplateGuide = 'template-guide', // 引导消息模板
1485
+ TemplateHidden = 'template-hidden', // 隐藏消息模板
1486
+ }
1487
+ ```
1488
+
1489
+ ### MessageStatus 枚举
1490
+
1491
+ 消息状态定义:
1492
+
1493
+ ```typescript
1494
+ enum MessageStatus {
1495
+ Pending = 'pending', // 等待中
1496
+ Streaming = 'streaming', // 流式传输中
1497
+ Complete = 'complete', // 已完成
1498
+ Error = 'error', // 错误
1499
+ Stop = 'stop', // 已停止
1500
+ }
1501
+ ```
1502
+
1503
+ ### IMessage 类型
1504
+
1505
+ 消息是联合类型,根据 `role` 字段不同有不同的结构:
1506
+
1507
+ ```typescript
1508
+ // 用户消息
1509
+ interface IUserMessage {
1510
+ messageId?: string;
1511
+ role: MessageRole.User;
1512
+ status: MessageStatus;
1513
+ content: string | IInputContent[]; // 支持文本或多媒体内容
1514
+ }
1515
+
1516
+ // AI 助手消息
1517
+ interface IAssistantMessage {
1518
+ messageId?: string;
1519
+ role: MessageRole.Assistant;
1520
+ status: MessageStatus;
1521
+ content?: string;
1522
+ toolCalls?: IToolCall[]; // 工具调用信息
1523
+ }
1524
+
1525
+ // 工具消息
1526
+ interface IToolMessage {
1527
+ messageId?: string;
1528
+ role: MessageRole.Tool;
1529
+ status: MessageStatus;
1530
+ content: string;
1531
+ toolCallId: string;
1532
+ duration?: number;
1533
+ error?: string;
1534
+ }
1535
+
1536
+ // ... 更多消息类型
1537
+ ```
1538
+
1539
+ ## API 参考
1540
+
1541
+ ### useChatHelper(options)
1542
+
1543
+ 创建 Chat Helper 实例。
1544
+
1545
+ **参数:**
1546
+
1547
+ - `options.requestData` (必需)
1548
+
1549
+ - `urlPrefix: string` - API 基础路径
1550
+ - `data?: Record<string, unknown> | (() => Record<string, unknown>)` - 额外的请求数据
1551
+ - `headers?: Record<string, string> | (() => Record<string, string>)` - 额外的请求头
1552
+
1553
+ - `options.interceptors` (可选)
1554
+
1555
+ - `request?: (config: IRequestConfig) => IRequestConfig` - 请求拦截器
1556
+ - `response?: (response: IResponse) => IResponse` - 响应拦截器
1557
+
1558
+ - `options.protocol` (可选) - 自定义 SSE Protocol 实例,默认使用 `AGUIProtocol`
1559
+
1560
+ **返回值:**
1561
+
1562
+ ```typescript
1563
+ {
1564
+ agent: IAgentModule, // Agent 模块
1565
+ session: ISessionModule, // Session 模块
1566
+ message: IMessageModule, // Message 模块
1567
+ http: IHttpModule // HTTP 模块
1568
+ }
1569
+ ```
1570
+
1571
+ ### Agent 模块 API
1572
+
1573
+ #### agent.info
1574
+
1575
+ - **类型**:`Ref<IAgentInfo | null>`
1576
+ - **说明**:当前 Agent 的信息,包括名称、会话设置、预定义问题等
1577
+
1578
+ #### agent.isInfoLoading
1579
+
1580
+ - **类型**:`Ref<boolean>`
1581
+ - **说明**:Agent 信息是否正在加载
1582
+
1583
+ #### agent.getAgentInfo()
1584
+
1585
+ - **返回值**:`Promise<void>`
1586
+ - **说明**:获取 Agent 信息,结果保存在 `agent.info` 中
1587
+
1588
+ #### agent.chat(userInput, sessionCode, url?, config?)
1589
+
1590
+ - **参数**:
1591
+ - `userInput: string` - 用户输入的消息内容
1592
+ - `sessionCode: string` - 会话代码
1593
+ - `url?: string` - 自定义聊天接口地址,默认为 `'chat_completion/'`
1594
+ - `config?: IRequestConfig` - 额外的请求配置
1595
+ - **返回值**:`Promise<void>`
1596
+ - **说明**:开始与 AI 进行聊天,支持流式响应。会先创建一条用户消息,然后发起流式请求
1597
+
1598
+ #### agent.stopChat()
1599
+
1600
+ - **返回值**:`Promise<void>`
1601
+ - **说明**:停止当前的聊天流式响应
1602
+
1603
+ ### Session 模块 API
1604
+
1605
+ #### session.list
1606
+
1607
+ - **类型**:`Ref<ISession[]>`
1608
+ - **说明**:会话列表
1609
+
1610
+ #### session.current
1611
+
1612
+ - **类型**:`Ref<ISession | null>`
1613
+ - **说明**:当前选中的会话
1614
+
1615
+ #### session.isListLoading / isCurrentLoading / isCreateLoading / isUpdateLoading / isDeleteLoading
1616
+
1617
+ - **类型**:`Ref<boolean>`
1618
+ - **说明**:各操作的加载状态
1619
+
1620
+ #### session.getSessions()
1621
+
1622
+ - **返回值**:`Promise<void>`
1623
+ - **说明**:获取会话列表,结果保存在 `session.list` 中
1624
+
1625
+ #### session.chooseSession(sessionCode)
1626
+
1627
+ - **参数**:`sessionCode: string` - 会话代码
1628
+ - **返回值**:`Promise<void>`
1629
+ - **说明**:选择会话,会停止当前聊天、设置 `session.current`,并自动加载该会话的消息列表
1630
+
1631
+ #### session.getSession(sessionCode)
1632
+
1633
+ - **参数**:`sessionCode: string` - 会话代码
1634
+ - **返回值**:`Promise<void>`
1635
+ - **说明**:获取单个会话信息,结果保存在 `session.current` 中
1636
+
1637
+ #### session.createSession(session)
1638
+
1639
+ - **参数**:`session: ISession` - 会话数据
1640
+ - **返回值**:`Promise<void>`
1641
+ - **说明**:创建新会话,创建成功后会添加到列表并自动选中
1642
+
1643
+ #### session.updateSession(session)
1644
+
1645
+ - **参数**:`session: ISession` - 会话数据
1646
+ - **返回值**:`Promise<void>`
1647
+ - **说明**:更新会话信息
1648
+
1649
+ #### session.deleteSession(sessionCode)
1650
+
1651
+ - **参数**:`sessionCode: string` - 会话代码
1652
+ - **返回值**:`Promise<void>`
1653
+ - **说明**:删除会话,如果删除的是当前会话,会自动切换到第一个会话
1654
+
1655
+ ### Message 模块 API
1656
+
1657
+ #### message.list
1658
+
1659
+ - **类型**:`Ref<IMessage[]>`
1660
+ - **说明**:消息列表
1661
+
1662
+ #### message.isListLoading / isDeleteLoading / isBatchDeleteLoading
1663
+
1664
+ - **类型**:`Ref<boolean>`
1665
+ - **说明**:各操作的加载状态
1666
+
1667
+ #### message.getMessages(sessionCode)
1668
+
1669
+ - **参数**:`sessionCode: string` - 会话代码
1670
+ - **返回值**:`Promise<void>`
1671
+ - **说明**:获取指定会话的消息列表
1672
+
1673
+ #### message.plusMessage(message)
1674
+
1675
+ - **参数**:`message: IMessage` - 消息数据
1676
+ - **返回值**:`Promise<void>`
1677
+ - **说明**:添加新消息,会先调用接口创建,然后添加到列表最前面
1678
+
1679
+ #### message.modifyMessage(message)
1680
+
1681
+ - **参数**:`message: IMessage` - 消息数据
1682
+ - **返回值**:`void`
1683
+ - **说明**:修改消息(仅本地更新,不调用接口)
1684
+
1685
+ #### message.deleteMessage(id)
1686
+
1687
+ - **参数**:`id: string` - 消息 ID
1688
+ - **返回值**:`Promise<void>`
1689
+ - **说明**:删除单条消息
1690
+
1691
+ #### message.batchDeleteMessages(ids)
1692
+
1693
+ - **参数**:`ids: string[]` - 消息 ID 数组
1694
+ - **返回值**:`Promise<void>`
1695
+ - **说明**:批量删除多条消息
1696
+
1697
+ #### message.getCurrentLoadingMessage()
1698
+
1699
+ - **返回值**:`IMessage | undefined`
1700
+ - **说明**:获取当前正在加载或流式传输中的 AI 消息(状态为 Pending 或 Streaming)
1701
+
1702
+ #### message.getMessageByMessageId(id)
1703
+
1704
+ - **参数**:`id: string` - 消息 ID
1705
+ - **返回值**:`IMessage | undefined`
1706
+ - **说明**:根据消息 ID 从列表中查找消息
1707
+
1708
+ ## 最佳实践
1709
+
1710
+ ### 1. 错误处理
1711
+
1712
+ 建议在使用 API 时添加适当的错误处理:
1713
+
1714
+ ```typescript
1715
+ const { session } = useChatHelper({
1716
+ /* ... */
1717
+ });
1718
+
1719
+ const loadSessions = async () => {
1720
+ try {
1721
+ await session.getSessions();
1722
+ } catch (error) {
1723
+ console.error('加载会话列表失败:', error);
1724
+ // 显示错误提示给用户
1725
+ }
1726
+ };
1727
+ ```
1728
+
1729
+ ### 2. 加载状态管理
1730
+
1731
+ 利用响应式的加载状态提供更好的用户体验:
1732
+
1733
+ ```vue
1734
+ <template>
1735
+ <div>
1736
+ <div v-if="session.isListLoading">
1737
+ <Spinner />
1738
+ 加载中...
1739
+ </div>
1740
+ <ul v-else>
1741
+ <li
1742
+ v-for="item in session.list"
1743
+ :key="item.sessionCode"
1744
+ >
1745
+ {{ item.sessionName }}
1746
+ </li>
1747
+ </ul>
1748
+ </div>
1749
+ </template>
1750
+ ```
1751
+
1752
+ ### 3. 会话切换
1753
+
1754
+ 切换会话时,`chooseSession` 会自动停止当前聊天并加载新会话的消息:
1755
+
1756
+ ```typescript
1757
+ // ✅ 推荐:使用 chooseSession
1758
+ await session.chooseSession(sessionCode);
1759
+
1760
+ // ❌ 不推荐:手动操作
1761
+ agent.stopChat();
1762
+ session.current.value = session.list.value.find(s => s.sessionCode === sessionCode);
1763
+ message.getMessages(sessionCode);
1764
+ ```
1765
+
1766
+ ### 4. 消息 ID 使用
1767
+
1768
+ 代码中使用 `messageId` 字段作为消息的唯一标识:
1769
+
1770
+ ```typescript
1771
+ // ✅ 正确
1772
+ message.deleteMessage(msg.messageId);
1773
+
1774
+ // ❌ 错误
1775
+ message.deleteMessage(msg.id);
1776
+ ```
1777
+
1778
+ ### 5. 动态请求配置
1779
+
1780
+ 对于需要动态获取的配置(如 token),使用函数形式:
1781
+
1782
+ ```typescript
1783
+ useChatHelper({
1784
+ requestData: {
1785
+ urlPrefix: 'https://api.example.com/',
1786
+ // ✅ 使用函数动态获取
1787
+ headers: () => ({
1788
+ Authorization: `Bearer ${localStorage.getItem('token')}`,
1789
+ 'X-Request-ID': generateRequestId(),
1790
+ }),
1791
+ },
1792
+ });
1793
+ ```
1794
+
1795
+ ### 6. Protocol 钩子函数
1796
+
1797
+ 使用 Protocol 钩子函数时,注意不要阻塞事件处理:
1798
+
1799
+ ```typescript
1800
+ new AGUIProtocol({
1801
+ onMessage: async event => {
1802
+ // ❌ 不推荐:阻塞事件处理
1803
+ await someAsyncOperation();
1804
+ console.log(event);
1805
+ },
1806
+
1807
+ onMessage: event => {
1808
+ // ✅ 推荐:快速处理,异步操作不阻塞
1809
+ console.log(event);
1810
+ someAsyncOperation(); // 不 await
1811
+ },
1812
+ });
1813
+ ```
1814
+
1815
+ ### 7. 内存管理
1816
+
1817
+ 在组件卸载时,记得停止正在进行的聊天:
1818
+
1819
+ ```typescript
1820
+ import { onUnmounted } from 'vue';
1821
+
1822
+ const { agent } = useChatHelper({
1823
+ /* ... */
1824
+ });
1825
+
1826
+ onUnmounted(() => {
1827
+ agent.stopChat();
1828
+ });
1829
+ ```
1830
+
1831
+ ### 8. 消息状态判断
1832
+
1833
+ 使用枚举类型进行状态判断,避免硬编码字符串:
1834
+
1835
+ ```typescript
1836
+ import { MessageStatus, MessageRole } from '@blueking/chat-helper';
1837
+
1838
+ // ✅ 推荐:使用枚举
1839
+ if (msg.status === MessageStatus.Streaming && msg.role === MessageRole.Assistant) {
1840
+ // 显示流式动画
1841
+ }
1842
+
1843
+ // ❌ 不推荐:硬编码字符串
1844
+ if (msg.status === 'streaming' && msg.role === 'assistant') {
1845
+ // 显示流式动画
1846
+ }
1847
+ ```
1848
+
1849
+ ## 常见问题
1850
+
1851
+ ### Q: 如何处理流式响应中的错误?
1852
+
1853
+ A: 使用 Protocol 的 `onError` 钩子函数:
1854
+
1855
+ ```typescript
1856
+ useChatHelper({
1857
+ requestData: {
1858
+ /* ... */
1859
+ },
1860
+ protocol: new AGUIProtocol({
1861
+ onError: error => {
1862
+ console.error('流式响应错误:', error);
1863
+ // 显示错误提示
1864
+ showErrorMessage('AI 响应出错,请重试');
1865
+ },
1866
+ }),
1867
+ });
1868
+ ```
1869
+
1870
+ ### Q: 如何自定义不同端点的聊天行为?
1871
+
1872
+ A: 使用 `agent.chat` 的 `url` 和 `config` 参数:
1873
+
1874
+ ```typescript
1875
+ // 使用不同的端点
1876
+ agent.chat(userInput, sessionCode, 'custom_chat/');
1877
+
1878
+ // 添加自定义参数
1879
+ agent.chat(userInput, sessionCode, undefined, {
1880
+ data: {
1881
+ temperature: 0.8,
1882
+ model: 'gpt-4',
1883
+ },
1884
+ });
1885
+ ```
1886
+
1887
+ ### Q: 消息列表如何实现自动滚动到底部?
1888
+
1889
+ A: 使用 Vue 的 `watch` 监听消息列表变化:
1890
+
1891
+ ```typescript
1892
+ import { watch, nextTick, ref } from 'vue';
1893
+
1894
+ const messageContainer = ref<HTMLElement>();
1895
+ const { message } = useChatHelper({
1896
+ /* ... */
1897
+ });
1898
+
1899
+ watch(
1900
+ () => message.list.value.length,
1901
+ async () => {
1902
+ await nextTick();
1903
+ messageContainer.value?.scrollTo({
1904
+ top: messageContainer.value.scrollHeight,
1905
+ behavior: 'smooth',
1906
+ });
1907
+ },
1908
+ );
1909
+ ```
1910
+
1911
+ ### Q: 如何判断 AI 是否正在回复?
1912
+
1913
+ A: 使用 `message.getCurrentLoadingMessage()` 方法:
1914
+
1915
+ ```typescript
1916
+ const { message } = useChatHelper({
1917
+ /* ... */
1918
+ });
1919
+
1920
+ const isAIResponding = computed(() => {
1921
+ return message.getCurrentLoadingMessage() !== undefined;
1922
+ });
1923
+ ```
1924
+
1925
+ ### Q: 如何实现多租户或多应用场景?
1926
+
1927
+ A: 使用动态的 `data` 配置:
1928
+
1929
+ ```typescript
1930
+ const currentAppId = ref('app-1');
1931
+
1932
+ useChatHelper({
1933
+ requestData: {
1934
+ urlPrefix: 'https://api.example.com/',
1935
+ data: () => ({
1936
+ app_id: currentAppId.value,
1937
+ tenant_id: getTenantId(),
1938
+ }),
1939
+ },
1940
+ });
1941
+ ```
1942
+
1943
+ ### Q: 如何自定义流式事件的处理逻辑?
1944
+
1945
+ A: 继承 `AGUIProtocol` 并重写对应的事件处理方法:
1946
+
1947
+ ```typescript
1948
+ class MyProtocol extends AGUIProtocol {
1949
+ handleTextMessageChunkEvent(event: ITextMessageChunkEvent) {
1950
+ // 自定义逻辑:例如敏感词过滤
1951
+ const filteredText = filterSensitiveWords(event.delta);
1952
+
1953
+ // 调用父类方法继续处理
1954
+ super.handleTextMessageChunkEvent({
1955
+ ...event,
1956
+ delta: filteredText,
1957
+ });
1958
+ }
1959
+ }
1960
+
1961
+ useChatHelper({
1962
+ requestData: {
1963
+ /* ... */
1964
+ },
1965
+ protocol: new MyProtocol(),
1966
+ });
1967
+ ```
1968
+
1969
+ ### Q: 如何实现消息的本地缓存?
1970
+
1971
+ A: 使用 `watch` 监听消息变化并保存到本地存储:
1972
+
1973
+ ```typescript
1974
+ import { watch } from 'vue';
1975
+
1976
+ const { message, session } = useChatHelper({
1977
+ /* ... */
1978
+ });
1979
+
1980
+ // 监听消息变化并缓存
1981
+ watch(
1982
+ () => message.list.value,
1983
+ newList => {
1984
+ if (session.current?.sessionCode) {
1985
+ localStorage.setItem(`messages_${session.current.sessionCode}`, JSON.stringify(newList));
1986
+ }
1987
+ },
1988
+ { deep: true },
1989
+ );
1990
+
1991
+ // 加载缓存
1992
+ const loadCachedMessages = (sessionCode: string) => {
1993
+ const cached = localStorage.getItem(`messages_${sessionCode}`);
1994
+ if (cached) {
1995
+ message.list.value = JSON.parse(cached);
1996
+ }
1997
+ };
1998
+ ```
1999
+
2000
+ ## 贡献指南
2001
+
2002
+ 欢迎提交 Issue 和 Pull Request!
2003
+
2004
+ 在提交 PR 之前,请确保:
2005
+
2006
+ 1. 代码通过 ESLint 检查
2007
+ 2. 添加了必要的类型定义
2008
+ 3. 更新了相关文档
2009
+
2010
+ ## 更新日志
2011
+
2012
+ 查看 [CHANGELOG.md](./CHANGELOG.md) 了解版本更新历史。
2013
+
1480
2014
  ## License
1481
2015
 
1482
2016
  MIT License