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,372 @@
1
+ import type {
2
+ ChatBizResult,
3
+ ChatStreamCallbacks,
4
+ MateChatConfig,
5
+ MateChatMessage,
6
+ } from '@af-mobile-client-vue3/components/common/MateChat/types'
7
+ import { useChatHistoryCache } from '@af-mobile-client-vue3/components/common/MateChat/composables/useChatHistoryCache'
8
+ import { useChatMessagesCache } from '@af-mobile-client-vue3/components/common/MateChat/composables/useChatMessagesCache'
9
+ import { showToast } from 'vant'
10
+ import { ref } from 'vue'
11
+ import { chatBiz, chatCompletionsStream, getPaginationRecords } from '../apiService'
12
+
13
+ /**
14
+ * 生成随机 chatId
15
+ */
16
+ function generateChatId(): string {
17
+ return `chat_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
18
+ }
19
+
20
+ /**
21
+ * 封装 MateChat 核心对话逻辑的组合式函数
22
+ * - 负责 startPage / inputValue / messages 等状态
23
+ * - 根据配置中的 useStream 决定使用非流式(chatBiz)还是流式(chatCompletionsStream)
24
+ * @param config MateChat 配置对象
25
+ */
26
+ export function useMateChat(config: MateChatConfig) {
27
+ const startPage = ref(true)
28
+ const inputValue = ref('')
29
+ const messages = ref<MateChatMessage[]>([])
30
+ const chatId = ref(generateChatId())
31
+ const useStream = config.useStream
32
+
33
+ // 使用历史会话缓存
34
+ const { clearCache } = useChatHistoryCache()
35
+ // 使用会话消息缓存
36
+ const { getCachedMessages, setCachedMessages, appendMessages } = useChatMessagesCache()
37
+
38
+ /**
39
+ * 新建会话:回到起始页并清空历史消息,生成新的 chatId
40
+ * 同时清除历史会话列表缓存,以便下次打开时重新获取
41
+ */
42
+ function newConversation() {
43
+ startPage.value = true
44
+ messages.value = []
45
+ chatId.value = generateChatId()
46
+ // 清除历史会话列表缓存,下次打开历史会话时会重新获取
47
+ clearCache(config.appId, config.appKey)
48
+ }
49
+
50
+ /**
51
+ * 发送一条消息
52
+ * - 推入用户消息
53
+ * - 添加一条 loading 的模型消息
54
+ * - 根据 useStream 调用对应的接口
55
+ */
56
+ async function onSubmit(evt: string) {
57
+ if (!evt.trim()) {
58
+ return
59
+ }
60
+
61
+ // 如果是从空状态发起的新会话,清除历史会话列表缓存
62
+ const isNewConversation = startPage.value
63
+ if (isNewConversation) {
64
+ clearCache(config.appId, config.appKey)
65
+ }
66
+
67
+ inputValue.value = ''
68
+ startPage.value = false
69
+
70
+ // 用户发送消息
71
+ messages.value.push({
72
+ from: 'user',
73
+ content: evt,
74
+ })
75
+
76
+ // 添加 loading 状态的 model 消息
77
+ const loadingMessageIndex = messages.value.length
78
+ messages.value.push({
79
+ from: 'model',
80
+ content: '',
81
+ loading: true,
82
+ })
83
+
84
+ if (!useStream) {
85
+ // 非流式:一次性拿到完整结果
86
+ try {
87
+ const result: ChatBizResult = await chatBiz(
88
+ evt,
89
+ config.appId,
90
+ config.appKey,
91
+ chatId.value,
92
+ )
93
+
94
+ if (result.type === 'transfer') {
95
+ // 移除 loading 消息
96
+ messages.value.splice(loadingMessageIndex, 1)
97
+ // 添加人工客服消息
98
+ messages.value.push({
99
+ from: 'service',
100
+ content: '您好,客服xxx工号xxx为你服务。功能开发中,敬请期待。',
101
+ })
102
+ // 更新缓存:追加用户消息和客服消息
103
+ appendMessages(chatId.value, [
104
+ { from: 'user', content: evt },
105
+ { from: 'service', content: '您好,客服xxx工号xxx为你服务。功能开发中,敬请期待。' },
106
+ ])
107
+ }
108
+ else {
109
+ // 正常消息:替换 loading 为模型回复
110
+ messages.value[loadingMessageIndex] = {
111
+ from: 'model',
112
+ content: result.content,
113
+ loading: false,
114
+ }
115
+ // 更新缓存:追加用户消息和模型回复
116
+ appendMessages(chatId.value, [
117
+ { from: 'user', content: evt },
118
+ { from: 'model', content: result.content, loading: false },
119
+ ])
120
+ }
121
+ }
122
+ catch (error: any) {
123
+ // 处理错误
124
+ console.error('聊天请求失败:', error)
125
+ messages.value[loadingMessageIndex] = {
126
+ from: 'model',
127
+ content: '抱歉,服务暂时不可用,请稍后再试。',
128
+ loading: false,
129
+ }
130
+ // 更新缓存:追加用户消息和错误消息
131
+ appendMessages(chatId.value, [
132
+ { from: 'user', content: evt },
133
+ { from: 'model', content: '抱歉,服务暂时不可用,请稍后再试。', loading: false },
134
+ ])
135
+ showToast(error?.message || '请求失败,请稍后再试')
136
+ }
137
+
138
+ return
139
+ }
140
+
141
+ // 流式:使用 FastGPT SSE,增量更新最后一条模型消息内容,并在前缀为 {"msgType":"transfer"} 时转人工
142
+ const transferPrefix = '{"msgType":"transfer"'
143
+ let checkedTransfer = false
144
+ let transferHandled = false
145
+ let prefixBuffer = ''
146
+
147
+ const callbacks: ChatStreamCallbacks = {
148
+ onMessage(chunk) {
149
+ if (transferHandled) {
150
+ return
151
+ }
152
+
153
+ // 尚未判断是否为转人工前缀
154
+ if (!checkedTransfer) {
155
+ const trimmed = chunk.trimStart()
156
+ if (!trimmed) {
157
+ // 纯空白,等待下一帧
158
+ return
159
+ }
160
+
161
+ // 第一个有效字符不是 { ,本次不会是转人工 JSON,后续直接按普通文本处理
162
+ if (!prefixBuffer && trimmed[0] !== '{') {
163
+ checkedTransfer = true
164
+ const msg = messages.value[loadingMessageIndex]
165
+ if (!msg) {
166
+ return
167
+ }
168
+ msg.content += chunk
169
+ msg.loading = true
170
+ return
171
+ }
172
+
173
+ // 有可能是 JSON,累积前缀做精准匹配
174
+ prefixBuffer += trimmed
175
+
176
+ // 如果当前前缀还是 transferPrefix 的前缀,继续等后续 chunk
177
+ if (transferPrefix.startsWith(prefixBuffer)) {
178
+ // 还没完整匹配上整个标识,继续等待
179
+ if (prefixBuffer.length < transferPrefix.length) {
180
+ return
181
+ }
182
+ }
183
+
184
+ if (prefixBuffer.startsWith(transferPrefix)) {
185
+ // 确认是转人工:移除 loading 模型气泡,插入客服气泡
186
+ messages.value.splice(loadingMessageIndex, 1)
187
+ messages.value.push({
188
+ from: 'service',
189
+ content: '您好,客服xxx工号xxx为你服务。功能开发中,敬请期待。',
190
+ })
191
+ transferHandled = true
192
+ checkedTransfer = true
193
+ // 更新缓存:追加用户消息和客服消息(在 onComplete 中统一处理)
194
+ return
195
+ }
196
+
197
+ // 前缀与约定不匹配,当作普通内容处理,并不再尝试转人工识别
198
+ checkedTransfer = true
199
+ const msg = messages.value[loadingMessageIndex]
200
+ if (!msg) {
201
+ return
202
+ }
203
+ msg.content += prefixBuffer
204
+ msg.loading = true
205
+ prefixBuffer = ''
206
+ return
207
+ }
208
+
209
+ // 已经判断过不会转人工,正常流式追加内容
210
+ const msg = messages.value[loadingMessageIndex]
211
+ if (!msg) {
212
+ return
213
+ }
214
+ msg.content += chunk
215
+ msg.loading = true
216
+ },
217
+ onComplete() {
218
+ if (transferHandled) {
219
+ // 转人工情况:更新缓存
220
+ appendMessages(chatId.value, [
221
+ { from: 'user', content: evt },
222
+ { from: 'service', content: '您好,客服xxx工号xxx为你服务。功能开发中,敬请期待。' },
223
+ ])
224
+ return
225
+ }
226
+ const msg = messages.value[loadingMessageIndex]
227
+ if (!msg) {
228
+ return
229
+ }
230
+ msg.loading = false
231
+ // 流式完成后更新缓存:追加用户消息和完整的模型回复
232
+ appendMessages(chatId.value, [
233
+ { from: 'user', content: evt },
234
+ { from: 'model', content: msg.content, loading: false },
235
+ ])
236
+ },
237
+ onError(error) {
238
+ console.error('聊天请求失败:', error)
239
+ const msg = messages.value[loadingMessageIndex]
240
+ if (!msg) {
241
+ return
242
+ }
243
+ msg.content = '抱歉,服务暂时不可用,请稍后再试。'
244
+ msg.loading = false
245
+ // 更新缓存:追加用户消息和错误消息
246
+ appendMessages(chatId.value, [
247
+ { from: 'user', content: evt },
248
+ { from: 'model', content: '抱歉,服务暂时不可用,请稍后再试。', loading: false },
249
+ ])
250
+ showToast((error as any)?.message || '请求失败,请稍后再试')
251
+ },
252
+ }
253
+
254
+ try {
255
+ await chatCompletionsStream(
256
+ evt,
257
+ config.appId,
258
+ config.appKey,
259
+ chatId.value,
260
+ callbacks,
261
+ )
262
+ }
263
+ catch (error: any) {
264
+ // 兜底错误处理(理论上 callbacks.onError 已经处理)
265
+ console.error('聊天流式请求异常:', error)
266
+ }
267
+ }
268
+
269
+ /**
270
+ * 加载历史会话消息
271
+ * @param targetChatId 会话 ID
272
+ */
273
+ async function loadHistoryMessages(targetChatId: string) {
274
+ // 先检查缓存
275
+ const cachedMessages = getCachedMessages(targetChatId)
276
+ if (cachedMessages) {
277
+ // 使用缓存数据
278
+ messages.value = cachedMessages
279
+ chatId.value = targetChatId
280
+ startPage.value = false
281
+ return
282
+ }
283
+
284
+ // 缓存不存在,从服务器获取
285
+ try {
286
+ const response = await getPaginationRecords(
287
+ config.appId,
288
+ config.appKey,
289
+ targetChatId,
290
+ 0,
291
+ 10,
292
+ true,
293
+ )
294
+
295
+ console.log('getPaginationRecords 响应:', response)
296
+
297
+ // 检查响应格式
298
+ if (!response) {
299
+ throw new Error('响应数据为空')
300
+ }
301
+
302
+ if (response.code !== 200) {
303
+ throw new Error(response.message || '获取历史消息失败')
304
+ }
305
+
306
+ if (!response.data) {
307
+ throw new Error('响应数据格式错误:缺少 data 字段')
308
+ }
309
+
310
+ if (!Array.isArray(response.data.list)) {
311
+ throw new TypeError('响应数据格式错误:list 不是数组')
312
+ }
313
+
314
+ // 清空当前消息
315
+ messages.value = []
316
+ // 设置 chatId
317
+ chatId.value = targetChatId
318
+
319
+ // 转换历史消息格式
320
+ const historyMessages: MateChatMessage[] = []
321
+ for (const record of response.data.list) {
322
+ if (record.hideInUI) {
323
+ continue
324
+ }
325
+
326
+ // 提取消息内容
327
+ const content = record.value?.[0]?.text?.content || ''
328
+
329
+ if (!content) {
330
+ // 跳过空内容的消息
331
+ continue
332
+ }
333
+
334
+ if (record.obj === 'Human') {
335
+ historyMessages.push({
336
+ from: 'user',
337
+ content,
338
+ })
339
+ }
340
+ else if (record.obj === 'AI') {
341
+ historyMessages.push({
342
+ from: 'model',
343
+ content,
344
+ loading: false,
345
+ })
346
+ }
347
+ }
348
+
349
+ messages.value = historyMessages
350
+ startPage.value = false
351
+
352
+ // 缓存历史消息
353
+ setCachedMessages(targetChatId, historyMessages)
354
+
355
+ console.log('历史消息加载成功,共', historyMessages.length, '条消息')
356
+ }
357
+ catch (error: any) {
358
+ console.error('加载历史消息失败:', error)
359
+ showToast(error?.message || '加载历史消息失败,请稍后再试')
360
+ }
361
+ }
362
+
363
+ return {
364
+ startPage,
365
+ inputValue,
366
+ messages,
367
+ chatId,
368
+ newConversation,
369
+ onSubmit,
370
+ loadHistoryMessages,
371
+ }
372
+ }
@@ -0,0 +1,38 @@
1
+ import { createStorage } from '@af-mobile-client-vue3/utils/Storage'
2
+
3
+ // 创建 localStorage 实例用于存储密码
4
+ const passwordStorage = createStorage({ storage: localStorage })
5
+ const PASSWORD_STORAGE_KEY = 'mateChat_password'
6
+
7
+ /**
8
+ * 密码管理组合式函数
9
+ * 提供密码的存储、读取和清除功能
10
+ */
11
+ export function usePasswordManager() {
12
+ /**
13
+ * 从 localStorage 读取保存的密码
14
+ */
15
+ function getStoredPassword(): string | null {
16
+ return passwordStorage.get(PASSWORD_STORAGE_KEY, null)
17
+ }
18
+
19
+ /**
20
+ * 将密码保存到 localStorage
21
+ */
22
+ function savePassword(password: string) {
23
+ passwordStorage.set(PASSWORD_STORAGE_KEY, password)
24
+ }
25
+
26
+ /**
27
+ * 清除保存的密码
28
+ */
29
+ function clearPassword() {
30
+ passwordStorage.remove(PASSWORD_STORAGE_KEY)
31
+ }
32
+
33
+ return {
34
+ getStoredPassword,
35
+ savePassword,
36
+ clearPassword,
37
+ }
38
+ }