af-mobile-client-vue3 1.4.53 → 1.4.55

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 (58) hide show
  1. package/build/vite/optimize.ts +36 -36
  2. package/package.json +1 -1
  3. package/public/favicon.svg +4 -4
  4. package/scripts/verifyCommit.js +19 -19
  5. package/src/App.vue +14 -2
  6. package/src/components/common/MateChat/apiService.ts +285 -0
  7. package/src/components/common/MateChat/assets/035-avatar-13.svg +1 -0
  8. package/src/components/common/MateChat/components/MateChatContent.vue +281 -0
  9. package/src/components/common/MateChat/components/MateChatHeader.vue +298 -0
  10. package/src/components/common/MateChat/components/PasswordDialog.vue +97 -0
  11. package/src/components/common/MateChat/components/PromptList/PromptList.vue +189 -0
  12. package/src/components/common/MateChat/components/PromptList/index.ts +1 -0
  13. package/src/components/common/MateChat/composables/useChatHistoryCache.ts +117 -0
  14. package/src/components/common/MateChat/composables/useChatMessagesCache.ts +72 -0
  15. package/src/components/common/MateChat/composables/useMateChat.ts +372 -0
  16. package/src/components/common/MateChat/composables/usePasswordManager.ts +38 -0
  17. package/src/components/common/MateChat/index.vue +429 -0
  18. package/src/components/common/MateChat/types.ts +236 -0
  19. package/src/components/common/otherCharge/ChargePrintSelectorAndRemarks.vue +137 -137
  20. package/src/components/common/otherCharge/CodePayment.vue +357 -357
  21. package/src/components/common/otherCharge/FileUploader.vue +602 -602
  22. package/src/components/common/otherCharge/GridFileUploader.vue +846 -846
  23. package/src/components/common/otherCharge/PaymentMethodSelector.vue +202 -202
  24. package/src/components/common/otherCharge/PaymentMethodSelectorCard.vue +45 -45
  25. package/src/components/common/otherCharge/ReceiptModal.vue +273 -273
  26. package/src/components/common/otherCharge/index.ts +43 -43
  27. package/src/components/data/OtherCharge/OtherChargeItemModal.vue +547 -547
  28. package/src/components/data/UserDetail/types.ts +1 -1
  29. package/src/components/data/XReportGrid/XAddReport/index.ts +1 -1
  30. package/src/components/data/XReportGrid/XReportDrawer/index.ts +1 -1
  31. package/src/components/data/XTag/index.vue +10 -10
  32. package/src/components/layout/TabBarLayout/index.vue +40 -40
  33. package/src/hooks/useCommon.ts +9 -9
  34. package/src/plugins/AppData.ts +38 -38
  35. package/src/router/invoiceRoutes.ts +33 -33
  36. package/src/services/api/common.ts +109 -109
  37. package/src/services/api/manage.ts +8 -8
  38. package/src/services/api/search.ts +16 -16
  39. package/src/services/restTools.ts +56 -56
  40. package/src/utils/authority-utils.ts +84 -84
  41. package/src/utils/crypto.ts +39 -39
  42. package/src/utils/queryFormDefaultRangePicker.ts +57 -57
  43. package/src/utils/runEvalFunction.ts +13 -13
  44. package/src/views/component/EvaluateRecordView/index.vue +40 -40
  45. package/src/views/component/MateChat/MateChatView.vue +10 -254
  46. package/src/views/component/XCellDetailView/index.vue +217 -217
  47. package/src/views/component/XCellListView/index.vue +107 -138
  48. package/src/views/component/XFormGroupView/index.vue +78 -82
  49. package/src/views/component/XFormView/index.vue +41 -46
  50. package/src/views/component/XReportFormIframeView/index.vue +47 -47
  51. package/src/views/component/XReportFormView/index.vue +13 -13
  52. package/src/views/component/XSignatureView/index.vue +50 -50
  53. package/src/views/component/notice.vue +46 -46
  54. package/src/views/component/topNav.vue +36 -36
  55. package/src/views/invoiceShow/index.vue +61 -61
  56. package/src/views/user/login/index.vue +22 -22
  57. package/vite.config.ts +2 -1
  58. package/src/views/component/MateChat/apiService.ts +0 -104
@@ -0,0 +1,429 @@
1
+ <script setup lang="ts">
2
+ import type { MateChatConfig, MateChatConfigs } from '@af-mobile-client-vue3/components/common/MateChat/types'
3
+ import MateChatContent from '@af-mobile-client-vue3/components/common/MateChat/components/MateChatContent.vue'
4
+ import PasswordDialog from '@af-mobile-client-vue3/components/common/MateChat/components/PasswordDialog.vue'
5
+ import { usePasswordManager } from '@af-mobile-client-vue3/components/common/MateChat/composables/usePasswordManager'
6
+ import { getConfigByNameAsync } from '@af-mobile-client-vue3/services/api/common'
7
+ // MateChat ai 对话相关
8
+ import { showFailToast, Button as VanButton, Icon as VanIcon, Loading as VanLoading } from 'vant'
9
+ import { computed, defineProps, onMounted, ref } from 'vue'
10
+
11
+ interface MateChatProps {
12
+ /**
13
+ * 配置名称,用于从配置中心获取配置
14
+ */
15
+ configName: string
16
+ /**
17
+ * 服务名
18
+ */
19
+ serviceName?: string
20
+ }
21
+
22
+ const props = defineProps<MateChatProps>()
23
+
24
+ // 使用密码管理 composable
25
+ const { getStoredPassword, savePassword, clearPassword } = usePasswordManager()
26
+
27
+ // 配置加载状态
28
+ const isLoading = ref(true)
29
+ const configError = ref<string | null>(null)
30
+ const showPasswordDialog = ref(false)
31
+ const passwordDialogRef = ref<InstanceType<typeof PasswordDialog> | null>(null)
32
+
33
+ // 临时变量:强制保持 loading 状态(用于调整 loading 样式)
34
+ // 设置为 true 时,组件将始终保持 loading 状态
35
+ const forceLoading = ref(false)
36
+
37
+ // 计算最终的 loading 状态
38
+ // 当有错误时也应该保持 loading 状态(显示错误信息),而不是显示对话页面
39
+ const finalLoading = computed(() => forceLoading.value || isLoading.value || !!configError.value)
40
+
41
+ // 当前使用的配置
42
+ const currentConfig = ref<MateChatConfig | null>(null)
43
+
44
+ // 存储原始配置数据
45
+ const rawConfigs = ref<MateChatConfigs | MateChatConfig | null>(null)
46
+
47
+ /**
48
+ * 检查配置对象中是否有密码保护的配置
49
+ */
50
+ function hasPasswordProtectedConfig(configs: MateChatConfigs | MateChatConfig | null): boolean {
51
+ if (!configs) {
52
+ return false
53
+ }
54
+ if (Array.isArray(configs)) {
55
+ return configs.some(config => !!config.password)
56
+ }
57
+ if (typeof configs === 'object' && configs !== null) {
58
+ // 如果是配置对象集合
59
+ return Object.values(configs).some((config) => {
60
+ if (typeof config === 'object' && config !== null && 'password' in config) {
61
+ return !!config.password
62
+ }
63
+ return false
64
+ })
65
+ }
66
+ // 单个配置对象
67
+ return !!(configs as MateChatConfig).password
68
+ }
69
+
70
+ /**
71
+ * 根据密码匹配配置
72
+ */
73
+ function findConfigByPassword(
74
+ configs: MateChatConfigs | MateChatConfig | null,
75
+ password: string,
76
+ ): MateChatConfig | null {
77
+ if (!configs) {
78
+ return null
79
+ }
80
+ if (typeof configs === 'object' && configs !== null) {
81
+ // 如果是配置对象集合(MateChatConfigs)
82
+ if (props.configName in configs) {
83
+ const targetConfig = (configs as MateChatConfigs)[props.configName]
84
+ if (targetConfig && targetConfig.password === password) {
85
+ return targetConfig
86
+ }
87
+ }
88
+ // 遍历所有配置查找匹配的密码
89
+ for (const config of Object.values(configs)) {
90
+ if (typeof config === 'object' && config !== null && 'password' in config) {
91
+ if (config.password === password) {
92
+ return config as MateChatConfig
93
+ }
94
+ }
95
+ }
96
+ }
97
+ // 单个配置对象
98
+ const singleConfig = configs as MateChatConfig
99
+ if (singleConfig && singleConfig.password === password) {
100
+ return singleConfig
101
+ }
102
+ return null
103
+ }
104
+
105
+ /**
106
+ * 获取配置(无密码保护时)
107
+ */
108
+ function getConfigWithoutPassword(
109
+ configs: MateChatConfigs | MateChatConfig | null,
110
+ ): MateChatConfig | null {
111
+ if (!configs) {
112
+ return null
113
+ }
114
+ if (typeof configs === 'object' && configs !== null) {
115
+ // 如果是配置对象集合,根据 configName 获取
116
+ if (props.configName in configs) {
117
+ return (configs as MateChatConfigs)[props.configName]
118
+ }
119
+ // 如果没有找到,返回第一个配置
120
+ const firstConfig = Object.values(configs)[0]
121
+ if (firstConfig && typeof firstConfig === 'object' && 'appId' in firstConfig) {
122
+ return firstConfig as MateChatConfig
123
+ }
124
+ }
125
+ // 单个配置对象,直接返回
126
+ return configs as MateChatConfig
127
+ }
128
+
129
+ /**
130
+ * 处理密码确认
131
+ */
132
+ function handlePasswordConfirm(password: string) {
133
+ if (!rawConfigs.value) {
134
+ showFailToast('配置数据异常')
135
+ return
136
+ }
137
+
138
+ const matchedConfig = findConfigByPassword(rawConfigs.value, password)
139
+ if (matchedConfig) {
140
+ // 验证配置的有效性
141
+ if (!validateConfig(matchedConfig)) {
142
+ configError.value = '配置不完整,请联系管理员检查配置'
143
+ showPasswordDialog.value = false
144
+ isLoading.value = false
145
+ return
146
+ }
147
+ // 验证成功,保存密码
148
+ savePassword(password)
149
+ currentConfig.value = matchedConfig
150
+ showPasswordDialog.value = false
151
+ isLoading.value = false
152
+ }
153
+ else {
154
+ // 验证失败,清除保存的密码
155
+ clearPassword()
156
+ // 清空输入框并显示错误,保持对话框打开
157
+ passwordDialogRef.value?.clearPassword()
158
+ passwordDialogRef.value?.setError('密码错误,请重新输入')
159
+ }
160
+ }
161
+
162
+ /**
163
+ * 处理密码取消
164
+ */
165
+ function handlePasswordCancel() {
166
+ // 取消时保持 loading 状态,不加载配置
167
+ showPasswordDialog.value = false
168
+ }
169
+
170
+ /**
171
+ * 验证配置的有效性
172
+ * @param config 配置对象
173
+ * @returns 如果配置有效返回 true,否则返回 false
174
+ */
175
+ function validateConfig(config: MateChatConfig | null): boolean {
176
+ if (!config) {
177
+ return false
178
+ }
179
+
180
+ // 检查必需的字段
181
+ if (!config.appId || typeof config.appId !== 'string' || config.appId.trim() === '') {
182
+ return false
183
+ }
184
+
185
+ if (!config.appKey || typeof config.appKey !== 'string' || config.appKey.trim() === '') {
186
+ return false
187
+ }
188
+
189
+ return true
190
+ }
191
+
192
+ /**
193
+ * 获取友好的错误提示信息
194
+ */
195
+ function getFriendlyErrorMessage(error: string | null): string {
196
+ if (!error) {
197
+ return '加载配置失败,请稍后再试'
198
+ }
199
+
200
+ // 根据错误信息返回更友好的提示
201
+ if (error.includes('未找到') || error.includes('资源')) {
202
+ return '未找到相关配置,请联系管理员'
203
+ }
204
+
205
+ if (error.includes('网络') || error.includes('请求')) {
206
+ return '网络连接异常,请检查网络后重试'
207
+ }
208
+
209
+ if (error.includes('超时')) {
210
+ return '请求超时,请稍后重试'
211
+ }
212
+
213
+ // 默认返回原始错误信息,但可以稍作优化
214
+ return error.length > 50 ? `${error.substring(0, 50)}...` : error
215
+ }
216
+
217
+ /**
218
+ * 处理重试
219
+ */
220
+ function handleRetry() {
221
+ loadConfig()
222
+ }
223
+
224
+ /**
225
+ * 加载配置
226
+ */
227
+ async function loadConfig() {
228
+ try {
229
+ isLoading.value = true
230
+ configError.value = null
231
+
232
+ const configs = await getConfigByNameAsync(props.configName, props.serviceName)
233
+
234
+ // 检查配置是否为空或无效
235
+ if (!configs) {
236
+ configError.value = '未找到有效配置'
237
+ isLoading.value = false
238
+ return
239
+ }
240
+
241
+ rawConfigs.value = configs
242
+
243
+ // 检查是否有密码保护的配置
244
+ if (hasPasswordProtectedConfig(configs)) {
245
+ // 尝试从 localStorage 读取保存的密码
246
+ const storedPassword = getStoredPassword()
247
+ if (storedPassword) {
248
+ // 自动验证保存的密码
249
+ const matchedConfig = findConfigByPassword(configs, storedPassword)
250
+ if (matchedConfig) {
251
+ // 验证配置的有效性
252
+ if (validateConfig(matchedConfig)) {
253
+ // 验证成功,直接加载配置
254
+ currentConfig.value = matchedConfig
255
+ isLoading.value = false
256
+ return
257
+ }
258
+ else {
259
+ // 配置无效,清除密码并显示对话框
260
+ clearPassword()
261
+ showPasswordDialog.value = true
262
+ return
263
+ }
264
+ }
265
+ else {
266
+ // 密码不匹配,清除保存的密码并显示对话框
267
+ clearPassword()
268
+ showPasswordDialog.value = true
269
+ return
270
+ }
271
+ }
272
+ // 没有保存的密码,显示密码输入对话框
273
+ showPasswordDialog.value = true
274
+ // 保持 loading 状态,等待密码验证
275
+ return
276
+ }
277
+
278
+ // 没有密码保护,直接加载配置
279
+ const config = getConfigWithoutPassword(configs)
280
+ if (config) {
281
+ // 验证配置的有效性
282
+ if (!validateConfig(config)) {
283
+ configError.value = '配置不完整,请联系管理员检查配置'
284
+ isLoading.value = false
285
+ return
286
+ }
287
+ currentConfig.value = config
288
+ isLoading.value = false
289
+ }
290
+ else {
291
+ configError.value = '未找到有效配置'
292
+ isLoading.value = false
293
+ }
294
+ }
295
+ catch (error: any) {
296
+ console.error('加载配置失败:', error)
297
+ configError.value = error?.message || '加载配置失败,请稍后再试'
298
+ isLoading.value = false
299
+ }
300
+ }
301
+
302
+ onMounted(() => {
303
+ loadConfig()
304
+ })
305
+ </script>
306
+
307
+ <template>
308
+ <div id="mate-chat-view">
309
+ <!-- 密码输入对话框 -->
310
+ <PasswordDialog
311
+ ref="passwordDialogRef"
312
+ :visible="showPasswordDialog"
313
+ @confirm="handlePasswordConfirm"
314
+ @cancel="handlePasswordCancel"
315
+ />
316
+ <!-- Loading 状态 -->
317
+ <div v-if="finalLoading" class="loading-container">
318
+ <div v-if="configError" class="error-container">
319
+ <VanIcon name="warning-o" size="48" class="error-icon" />
320
+ <div class="error-title">
321
+ 抱歉,加载失败
322
+ </div>
323
+ <div class="error-message">
324
+ {{ getFriendlyErrorMessage(configError) }}
325
+ </div>
326
+ <VanButton
327
+ type="primary"
328
+ size="small"
329
+ class="retry-button"
330
+ @click="handleRetry"
331
+ >
332
+ 重试
333
+ </VanButton>
334
+ </div>
335
+ <VanLoading v-else vertical>
336
+ <template #icon>
337
+ <VanIcon name="star-o" size="30" />
338
+ </template>
339
+ 加载中...
340
+ </VanLoading>
341
+ </div>
342
+ <!-- 对话内容 -->
343
+ <MateChatContent
344
+ v-else-if="currentConfig"
345
+ :config="currentConfig"
346
+ />
347
+ </div>
348
+ </template>
349
+
350
+ <style scoped lang="less">
351
+ #mate-chat-view {
352
+ /* 外层渐变背景容器 */
353
+ width: 100%;
354
+ min-height: 100vh;
355
+ background: linear-gradient(to bottom, #a8f0ed 0%, #a8e6cf 8%, #c5b3ff 12%, #e0b3ff 40%, #fff4b8 60%, #ffb8d1 90%);
356
+ /* background: linear-gradient(to bottom, #d0c9ff 0%, #e6d6f0 8%, #f1dbea 12%, #c8dcfb 40%, #abc6f6 60%, #87aefe 90%); */
357
+ padding: 20px;
358
+ display: flex;
359
+ justify-content: center;
360
+ align-items: flex-start;
361
+ box-sizing: border-box;
362
+
363
+ /* 移动端适配 */
364
+ @media (max-width: 768px) {
365
+ padding: 8px;
366
+ }
367
+
368
+ .loading-container {
369
+ width: 100%;
370
+ max-width: 1200px;
371
+ height: calc(100vh - 40px);
372
+ background: #ffffff;
373
+ border-radius: 24px;
374
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
375
+ display: flex;
376
+ align-items: center;
377
+ justify-content: center;
378
+ min-height: 400px;
379
+ padding: 20px;
380
+ box-sizing: border-box;
381
+
382
+ /* 移动端适配 */
383
+ @media (max-width: 768px) {
384
+ height: calc(100vh - 16px);
385
+ border-radius: 16px;
386
+ padding: 8px;
387
+ }
388
+
389
+ .loading-message {
390
+ font-size: 14px;
391
+ color: #71757f;
392
+ text-align: center;
393
+ }
394
+
395
+ .error-container {
396
+ display: flex;
397
+ flex-direction: column;
398
+ align-items: center;
399
+ justify-content: center;
400
+ padding: 24px;
401
+ text-align: center;
402
+
403
+ .error-icon {
404
+ color: #ff9800;
405
+ margin-bottom: 16px;
406
+ }
407
+
408
+ .error-title {
409
+ font-size: 16px;
410
+ font-weight: 500;
411
+ color: #323233;
412
+ margin-bottom: 8px;
413
+ }
414
+
415
+ .error-message {
416
+ font-size: 14px;
417
+ color: #969799;
418
+ line-height: 1.5;
419
+ margin-bottom: 24px;
420
+ max-width: 280px;
421
+ }
422
+
423
+ .retry-button {
424
+ min-width: 100px;
425
+ }
426
+ }
427
+ }
428
+ }
429
+ </style>
@@ -0,0 +1,236 @@
1
+ /**
2
+ * MateChat 提示项接口
3
+ */
4
+ export interface MateChatPromptItem {
5
+ value: string
6
+ label: string
7
+ iconConfig?: {
8
+ name: string
9
+ color?: string
10
+ }
11
+ desc?: string
12
+ }
13
+
14
+ /**
15
+ * MateChat 配置接口
16
+ */
17
+ export interface MateChatConfig {
18
+ /**
19
+ * 可选密码,如果存在则需要密码验证
20
+ */
21
+ password?: string
22
+ /**
23
+ * FastGPT 应用 ID
24
+ */
25
+ appId: string
26
+ /**
27
+ * FastGPT API Key
28
+ */
29
+ appKey: string
30
+ /**
31
+ * 介绍文案
32
+ */
33
+ description: string[]
34
+ /**
35
+ * 首屏推荐问题列表
36
+ */
37
+ introPrompt: MateChatPromptItem[]
38
+ /**
39
+ * 底部快捷问题列表
40
+ */
41
+ simplePrompt: MateChatPromptItem[]
42
+ /**
43
+ * 客服名称
44
+ */
45
+ serviceName: string
46
+ /**
47
+ * 是否使用流式对话
48
+ */
49
+ useStream: boolean
50
+ }
51
+
52
+ /**
53
+ * 配置中心返回的多个配置对象
54
+ * key 为配置名称,value 为 MateChatConfig
55
+ */
56
+ export type MateChatConfigs = Record<string, MateChatConfig>
57
+
58
+ // ==================== API 相关类型定义 ====================
59
+
60
+ /**
61
+ * 聊天消息接口
62
+ */
63
+ export interface ChatMessage {
64
+ role: 'user' | 'assistant' | 'system'
65
+ content: string
66
+ }
67
+
68
+ /**
69
+ * 聊天请求参数接口
70
+ */
71
+ export interface ChatCompletionsRequest {
72
+ chatId: string
73
+ stream: boolean
74
+ detail: boolean
75
+ messages: ChatMessage[]
76
+ customUid?: string
77
+ }
78
+
79
+ /**
80
+ * 聊天响应使用情况接口
81
+ */
82
+ export interface ChatUsage {
83
+ prompt_tokens: number
84
+ completion_tokens: number
85
+ total_tokens: number
86
+ }
87
+
88
+ /**
89
+ * 聊天响应选择项接口
90
+ */
91
+ export interface ChatChoice {
92
+ message: {
93
+ role: 'assistant'
94
+ content: string
95
+ }
96
+ finish_reason: string
97
+ index: number
98
+ }
99
+
100
+ /**
101
+ * 聊天响应接口
102
+ */
103
+ export interface ChatCompletionsResponse {
104
+ id: string
105
+ model: string
106
+ usage: ChatUsage
107
+ choices: ChatChoice[]
108
+ }
109
+
110
+ /**
111
+ * 业务层聊天结果
112
+ */
113
+ export interface ChatBizResult {
114
+ /**
115
+ * normal: 普通回复
116
+ * transfer: 转人工
117
+ */
118
+ type: 'normal' | 'transfer'
119
+ /**
120
+ * 大模型原始返回内容
121
+ */
122
+ content: string
123
+ }
124
+
125
+ /**
126
+ * 流式对话回调
127
+ */
128
+ export interface ChatStreamCallbacks {
129
+ /**
130
+ * 每次收到 FastGPT SSE 的增量内容时触发
131
+ */
132
+ onMessage?: (chunk: string) => void
133
+ /**
134
+ * 流结束时触发(包括收到 [DONE] 或正常读取结束)
135
+ */
136
+ onComplete?: () => void
137
+ /**
138
+ * 请求或解析发生异常时触发
139
+ */
140
+ onError?: (error: unknown) => void
141
+ }
142
+
143
+ /**
144
+ * 历史会话项接口
145
+ */
146
+ export interface ChatHistoryItem {
147
+ chatId: string
148
+ updateTime: string
149
+ appId: string
150
+ customTitle: string
151
+ title: string
152
+ top: boolean
153
+ [key: string]: any
154
+ }
155
+
156
+ /**
157
+ * 历史会话查询请求参数接口
158
+ */
159
+ export interface GetHistoriesRequest {
160
+ appId: string
161
+ outLinkUid: string
162
+ offset: number
163
+ pageSize: number
164
+ source: string
165
+ }
166
+
167
+ /**
168
+ * 历史会话查询响应接口
169
+ */
170
+ export interface GetHistoriesResponse {
171
+ code: number
172
+ statusText: string
173
+ message: string
174
+ data: {
175
+ list: ChatHistoryItem[]
176
+ total: number
177
+ }
178
+ }
179
+
180
+ /**
181
+ * 历史会话记录项接口
182
+ */
183
+ export interface ChatRecordItem {
184
+ _id: string
185
+ dataId: string
186
+ hideInUI: boolean
187
+ obj: 'Human' | 'AI'
188
+ value: Array<{
189
+ type: string
190
+ text: {
191
+ content: string
192
+ }
193
+ }>
194
+ customFeedbacks: any[]
195
+ time: string
196
+ durationSeconds?: number
197
+ llmModuleAccount?: number
198
+ totalQuoteList?: any[]
199
+ historyPreviewLength?: number
200
+ [key: string]: any
201
+ }
202
+
203
+ /**
204
+ * 获取历史会话记录请求参数接口
205
+ */
206
+ export interface GetPaginationRecordsRequest {
207
+ appId: string
208
+ chatId: string
209
+ offset: number
210
+ pageSize: number
211
+ loadCustomFeedbacks: boolean
212
+ }
213
+
214
+ /**
215
+ * 获取历史会话记录响应接口
216
+ */
217
+ export interface GetPaginationRecordsResponse {
218
+ code: number
219
+ statusText: string
220
+ message: string
221
+ data: {
222
+ list: ChatRecordItem[]
223
+ total: number
224
+ }
225
+ }
226
+
227
+ // ==================== MateChat 组件内部类型 ====================
228
+
229
+ /**
230
+ * MateChat 组件内部使用的消息结构
231
+ */
232
+ export interface MateChatMessage {
233
+ from: 'user' | 'model' | 'service'
234
+ content: string
235
+ loading?: boolean
236
+ }