af-mobile-client-vue3 1.4.54 → 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 (47) 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/components/common/MateChat/apiService.ts +134 -103
  6. package/src/components/common/MateChat/components/MateChatContent.vue +281 -0
  7. package/src/components/common/MateChat/components/MateChatHeader.vue +298 -253
  8. package/src/components/common/MateChat/components/PasswordDialog.vue +97 -0
  9. package/src/components/common/MateChat/composables/useChatHistoryCache.ts +117 -0
  10. package/src/components/common/MateChat/composables/useChatMessagesCache.ts +72 -0
  11. package/src/components/common/MateChat/composables/useMateChat.ts +372 -0
  12. package/src/components/common/MateChat/composables/usePasswordManager.ts +38 -0
  13. package/src/components/common/MateChat/index.vue +429 -0
  14. package/src/components/common/MateChat/types.ts +236 -0
  15. package/src/components/data/UserDetail/types.ts +1 -1
  16. package/src/components/data/XFormGroup/doc/DeviceForm.vue +1 -1
  17. package/src/components/data/XFormGroup/doc/UserForm.vue +1 -1
  18. package/src/components/data/XReportGrid/XAddReport/index.ts +1 -1
  19. package/src/components/data/XReportGrid/XReportDemo.vue +33 -33
  20. package/src/components/data/XReportGrid/XReportDrawer/index.ts +1 -1
  21. package/src/components/data/XReportGrid/print.js +184 -184
  22. package/src/components/data/XTag/index.vue +10 -10
  23. package/src/components/layout/TabBarLayout/index.vue +40 -40
  24. package/src/hooks/useCommon.ts +9 -9
  25. package/src/plugins/AppData.ts +38 -38
  26. package/src/router/invoiceRoutes.ts +33 -33
  27. package/src/services/api/common.ts +109 -109
  28. package/src/services/api/manage.ts +8 -8
  29. package/src/services/api/search.ts +16 -16
  30. package/src/services/restTools.ts +56 -56
  31. package/src/utils/authority-utils.ts +84 -84
  32. package/src/utils/crypto.ts +39 -39
  33. package/src/utils/runEvalFunction.ts +13 -13
  34. package/src/utils/timeUtil.ts +27 -27
  35. package/src/views/component/EvaluateRecordView/index.vue +40 -40
  36. package/src/views/component/MateChat/MateChatView.vue +10 -51
  37. package/src/views/component/XCellDetailView/index.vue +217 -217
  38. package/src/views/component/XReportFormIframeView/index.vue +47 -47
  39. package/src/views/component/XReportFormView/index.vue +13 -13
  40. package/src/views/component/XSignatureView/index.vue +50 -50
  41. package/src/views/component/notice.vue +46 -46
  42. package/src/views/component/topNav.vue +36 -36
  43. package/src/views/invoiceShow/index.vue +61 -61
  44. package/src/views/user/login/index.vue +22 -22
  45. package/vite.config.ts +2 -1
  46. package/src/components/common/MateChat/MateChat.vue +0 -248
  47. package/src/components/common/MateChat/useMateChat.ts +0 -212
@@ -0,0 +1,281 @@
1
+ <script setup lang="ts">
2
+ import type { MateChatConfig } from '@af-mobile-client-vue3/components/common/MateChat/types'
3
+ import liuliLogo from '@af-mobile-client-vue3/assets/img/component/liuli.png'
4
+ import userAvatarImg from '@af-mobile-client-vue3/components/common/MateChat/assets/035-avatar-13.svg'
5
+ import MateChatHeader from '@af-mobile-client-vue3/components/common/MateChat/components/MateChatHeader.vue'
6
+ import { PromptList } from '@af-mobile-client-vue3/components/common/MateChat/components/PromptList'
7
+ import { useMateChat } from '@af-mobile-client-vue3/components/common/MateChat/composables/useMateChat'
8
+ import { useDebounceFn } from '@vueuse/core'
9
+ import { Button as VanButton } from 'vant'
10
+ import { computed, nextTick, ref, watch } from 'vue'
11
+ import 'vant/es/image-preview/style'
12
+
13
+ interface MateChatContentProps {
14
+ /**
15
+ * 完整的配置对象
16
+ */
17
+ config: MateChatConfig
18
+ }
19
+
20
+ const props = defineProps<MateChatContentProps>()
21
+
22
+ // 从配置中获取的值
23
+ const description = computed(() => props.config.description || [])
24
+ const introPrompt = computed(() => props.config.introPrompt || [])
25
+ const simplePrompt = computed(() => props.config.simplePrompt || [])
26
+ const serviceName = computed(() => props.config.serviceName || '智能客服')
27
+ const useStream = computed(() => props.config.useStream || false)
28
+
29
+ // 初始化 useMateChat
30
+ const mateChatInstance = useMateChat(props.config)
31
+
32
+ const startPage = computed(() => mateChatInstance.startPage.value)
33
+ const inputValue = computed({
34
+ get: () => mateChatInstance.inputValue.value,
35
+ set: (val: string) => {
36
+ mateChatInstance.inputValue.value = val
37
+ },
38
+ })
39
+ const messages = computed(() => mateChatInstance.messages.value)
40
+ const newConversation = () => mateChatInstance.newConversation()
41
+ const onSubmit = (evt: string) => mateChatInstance.onSubmit(evt)
42
+
43
+ // 消息容器的 ref
44
+ const messageContainerRef = ref<HTMLElement | null>(null)
45
+ // 滚动锚点的 ref
46
+ const scrollAnchorRef = ref<HTMLElement | null>(null)
47
+
48
+ /**
49
+ * 滚动到底部
50
+ */
51
+ function scrollToBottom() {
52
+ nextTick(() => {
53
+ // 优先使用滚动锚点元素滚动到底部(最可靠)
54
+ if (scrollAnchorRef.value) {
55
+ scrollAnchorRef.value.scrollIntoView({ behavior: 'smooth', block: 'end' })
56
+ return
57
+ }
58
+
59
+ // 使用 ref 获取容器并滚动
60
+ const container = messageContainerRef.value
61
+ if (container instanceof HTMLElement) {
62
+ container.scrollTo({
63
+ top: container.scrollHeight,
64
+ behavior: 'smooth',
65
+ })
66
+ }
67
+ })
68
+ }
69
+
70
+ // 使用防抖处理滚动函数,延迟 100ms
71
+ const debouncedScrollToBottom = useDebounceFn(scrollToBottom, 100)
72
+
73
+ // 计算最后一条消息的长度
74
+ const lastMessageLength = computed(() => {
75
+ const msgList = messages.value
76
+ if (msgList.length === 0) {
77
+ return 0
78
+ }
79
+ const lastMsg = msgList[msgList.length - 1]
80
+ return lastMsg?.content?.length || 0
81
+ })
82
+
83
+ // 监听消息数量和最后一条消息的长度变化,自动滚动到底部
84
+ // 使用 immediate: false 避免初始化时滚动
85
+ watch(
86
+ [() => messages.value.length, lastMessageLength],
87
+ () => {
88
+ debouncedScrollToBottom()
89
+ },
90
+ { flush: 'post' },
91
+ )
92
+
93
+ /**
94
+ * 处理历史会话选择
95
+ */
96
+ function handleSelectSession(session: { chatId: string, title: string, lastTime: string }) {
97
+ mateChatInstance.loadHistoryMessages(session.chatId)
98
+ // 加载历史消息后也需要滚动到底部
99
+ // 使用防抖版本,避免频繁滚动
100
+ debouncedScrollToBottom()
101
+ }
102
+ </script>
103
+
104
+ <template>
105
+ <McLayout class="chat-card">
106
+ <MateChatHeader
107
+ :show-title="!startPage"
108
+ :logo-image="liuliLogo"
109
+ :title="serviceName"
110
+ :app-id="props.config.appId"
111
+ :app-key="props.config.appKey"
112
+ @select-session="handleSelectSession"
113
+ />
114
+ <McLayoutContent
115
+ v-if="startPage"
116
+ style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px"
117
+ >
118
+ <McIntroduction
119
+ v-if="description && description.length"
120
+ :logo-img="liuliLogo"
121
+ :title="serviceName"
122
+ :sub-title="`Hi,我是${serviceName}`"
123
+ :description="description"
124
+ class="McIntroduction"
125
+ />
126
+ <PromptList
127
+ v-if="introPrompt && introPrompt.length"
128
+ :list="introPrompt"
129
+ @item-click="onSubmit($event.label)"
130
+ />
131
+ </McLayoutContent>
132
+ <McLayoutContent v-else ref="messageContainerRef" class="content-container">
133
+ <template v-for="(msg, idx) in messages" :key="idx">
134
+ <McBubble
135
+ v-if="msg.from === 'user'"
136
+ :content="msg.content"
137
+ align="right"
138
+ :avatar-config="{ imgSrc: userAvatarImg }"
139
+ />
140
+ <McBubble
141
+ v-else-if="msg.from === 'service'"
142
+ :content="msg.content"
143
+ :avatar-config="{ imgSrc: userAvatarImg }"
144
+ />
145
+ <McBubble v-else :content="msg.content" :avatar-config="{ imgSrc: liuliLogo }" :loading="msg.loading">
146
+ <McMarkdownCard
147
+ :content="msg.content"
148
+ :typing="!useStream"
149
+ />
150
+ </McBubble>
151
+ </template>
152
+ <!-- 滚动锚点 -->
153
+ <div ref="scrollAnchorRef" class="scroll-anchor" />
154
+ </McLayoutContent>
155
+ <div class="shortcut" style="display: flex; align-items: center; gap: 8px">
156
+ <PromptList
157
+ v-if="!startPage && simplePrompt && simplePrompt.length"
158
+ :list="simplePrompt"
159
+ direction="horizontal"
160
+ class="shortcut-prompt"
161
+ style="flex: 1"
162
+ @item-click="onSubmit($event.label)"
163
+ />
164
+ <VanButton
165
+ style="margin-left: auto"
166
+ icon="add"
167
+ title="新建对话"
168
+ size="small"
169
+ round
170
+ @click="newConversation"
171
+ />
172
+ </div>
173
+ <McLayoutSender>
174
+ <McInput
175
+ :value="inputValue"
176
+ placeholder="请输入您的问题,我会为您解答"
177
+ :max-length="2000"
178
+ @change="(e) => (inputValue = e)"
179
+ @submit="onSubmit"
180
+ />
181
+ </McLayoutSender>
182
+ </McLayout>
183
+ </template>
184
+
185
+ <style scoped lang="less">
186
+ .chat-card {
187
+ width: 100%;
188
+ max-width: 1200px;
189
+ height: calc(100vh - 40px);
190
+ background: #ffffff;
191
+ border-radius: 24px;
192
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
193
+ overflow: hidden;
194
+ display: flex;
195
+ flex-direction: column;
196
+ transition: all 0.3s ease;
197
+ padding: 20px;
198
+ gap: 8px;
199
+ }
200
+
201
+ .chat-card:hover {
202
+ box-shadow: 0 25px 70px rgba(0, 0, 0, 0.2);
203
+ }
204
+
205
+ /* 移动端适配 */
206
+ @media (max-width: 768px) {
207
+ .chat-card {
208
+ height: calc(100vh - 16px);
209
+ border-radius: 16px;
210
+ padding: 8px;
211
+ }
212
+ }
213
+
214
+ .content-container {
215
+ display: flex;
216
+ flex-direction: column;
217
+ gap: 8px;
218
+ overflow: auto;
219
+ }
220
+
221
+ .scroll-anchor {
222
+ height: 1px;
223
+ width: 100%;
224
+ flex-shrink: 0;
225
+ }
226
+
227
+ .input-foot-wrapper {
228
+ display: flex;
229
+ justify-content: space-between;
230
+ align-items: center;
231
+ width: 100%;
232
+ height: 100%;
233
+ margin-right: 8px;
234
+
235
+ .input-foot-left {
236
+ display: flex;
237
+ align-items: center;
238
+ gap: 8px;
239
+
240
+ span {
241
+ font-size: 14px;
242
+ line-height: 18px;
243
+ color: #252b3a;
244
+ cursor: pointer;
245
+ }
246
+
247
+ .input-foot-dividing-line {
248
+ width: 1px;
249
+ height: 14px;
250
+ background-color: #d7d8da;
251
+ }
252
+
253
+ .input-foot-maxlength {
254
+ font-size: 14px;
255
+ color: #71757f;
256
+ }
257
+ }
258
+
259
+ .input-foot-right {
260
+ .demo-button-content {
261
+ font-size: 14px;
262
+ }
263
+
264
+ & > *:not(:first-child) {
265
+ margin-left: 8px;
266
+ }
267
+ }
268
+ }
269
+
270
+ :deep(.mc-header-logo-container img) {
271
+ width: 32px;
272
+ height: 32px;
273
+ }
274
+
275
+ .McIntroduction {
276
+ :deep(.mc-introduction-logo-container img) {
277
+ width: 64px;
278
+ height: 64px;
279
+ }
280
+ }
281
+ </style>