af-mobile-client-vue3 1.4.54 → 1.4.56

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 +443 -0
  14. package/src/components/common/MateChat/types.ts +241 -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
@@ -1,253 +1,298 @@
1
- <script setup lang="ts">
2
- import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
3
- import { Empty, Icon, Loading, Popup } from 'vant'
4
- import { ref } from 'vue'
5
- import 'vant/es/popup/style'
6
- import 'vant/es/empty/style'
7
- import 'vant/es/loading/style'
8
- import 'vant/es/icon/style'
9
-
10
- interface Props {
11
- /**
12
- * 头部左侧 Logo 图片
13
- */
14
- logoImage: string
15
- /**
16
- * 头部标题文案
17
- */
18
- title: string
19
- /**
20
- * 历史对话弹层标题
21
- */
22
- historyTitle?: string
23
- }
24
-
25
- interface SessionItem {
26
- id: string
27
- title: string
28
- lastTime: string
29
- }
30
-
31
- interface Emits {
32
- /**
33
- * 选择某条历史会话
34
- */
35
- (e: 'selectSession', session: SessionItem): void
36
- }
37
-
38
- const props = withDefaults(defineProps<Props>(), {
39
- historyTitle: '对话历史',
40
- })
41
-
42
- const emit = defineEmits<Emits>()
43
-
44
- const userStore = useUserStore()
45
-
46
- const showHistory = ref(false)
47
- const isLoading = ref(false)
48
- const sessionList = ref<SessionItem[]>([])
49
-
50
- function handleOpenHistory() {
51
- showHistory.value = true
52
- fetchSessions()
53
- }
54
-
55
- function handleSelectSession(session: SessionItem) {
56
- emit('selectSession', session)
57
- showHistory.value = false
58
- }
59
-
60
- async function fetchSessions() {
61
- isLoading.value = true
62
- try {
63
- const userInfo = userStore.getUserInfo()
64
- const userId = userInfo?.id ?? 'guest'
65
- const userName = userInfo?.name ?? '当前用户'
66
-
67
- // 模拟接口延迟
68
- await new Promise(resolve => setTimeout(resolve, 500))
69
-
70
- // 模拟会话列表数据
71
- sessionList.value = [
72
- {
73
- id: `${userId}-1`,
74
- title: `${userName} 最近的缴费咨询`,
75
- lastTime: '2025-01-01 10:00',
76
- },
77
- {
78
- id: `${userId}-2`,
79
- title: `${userName} 燃气报修记录`,
80
- lastTime: '2025-01-02 14:30',
81
- },
82
- {
83
- id: `${userId}-3`,
84
- title: `${userName} 安全用气相关问题`,
85
- lastTime: '2025-01-03 09:15',
86
- },
87
- ]
88
- }
89
- finally {
90
- isLoading.value = false
91
- }
92
- }
93
- </script>
94
-
95
- <template>
96
- <div class="matechat-header-wrapper">
97
- <McHeader :logo-img="props.logoImage" :title="props.title" :logo-clickable="false">
98
- <template #operationArea>
99
- <div class="matechat-header-history-btn" @click="handleOpenHistory">
100
- <Icon name="clock-o" size="16" />
101
- </div>
102
- </template>
103
- </McHeader>
104
-
105
- <Popup
106
- v-model:show="showHistory"
107
- position="center"
108
- round
109
- :overlay="true"
110
- class="matechat-history-popup"
111
- >
112
- <div class="matechat-history-card">
113
- <div class="matechat-history-card__header">
114
- <span class="matechat-history-card__title">
115
- {{ props.historyTitle }}
116
- </span>
117
- </div>
118
- <div class="matechat-history-card__content">
119
- <div v-if="isLoading" class="matechat-history-card__loading">
120
- <Loading size="24px" />
121
- </div>
122
- <template v-else>
123
- <div
124
- v-if="sessionList.length"
125
- class="matechat-history-card__list"
126
- >
127
- <div
128
- v-for="session in sessionList"
129
- :key="session.id"
130
- class="matechat-history-card__item"
131
- @click="handleSelectSession(session)"
132
- >
133
- <div class="matechat-history-card__item-title">
134
- {{ session.title }}
135
- </div>
136
- <div class="matechat-history-card__item-time">
137
- {{ session.lastTime }}
138
- </div>
139
- </div>
140
- </div>
141
- <div v-else class="matechat-history-card__empty">
142
- <Empty description="无数据" />
143
- </div>
144
- </template>
145
- </div>
146
- </div>
147
- </Popup>
148
- </div>
149
- </template>
150
-
151
- <style scoped lang="less">
152
- .matechat-header-wrapper {
153
- position: relative;
154
- }
155
-
156
- .matechat-header-history-btn {
157
- display: inline-flex;
158
- align-items: center;
159
- justify-content: center;
160
- width: 32px;
161
- height: 32px;
162
- border-radius: 50%;
163
- background-color: rgba(255, 255, 255, 0.8);
164
- cursor: pointer;
165
- transition: all 0.2s ease;
166
-
167
- &:hover {
168
- background-color: rgba(255, 255, 255, 1);
169
- transform: translateY(-1px);
170
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
171
- }
172
- }
173
-
174
- .matechat-history-popup {
175
- .matechat-history-card {
176
- width: 320px;
177
- max-width: 80vw;
178
- background-color: #ffffff;
179
- border-radius: 16px;
180
- box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
181
- padding: 16px 16px 12px;
182
- box-sizing: border-box;
183
-
184
- &__header {
185
- display: flex;
186
- align-items: center;
187
- justify-content: space-between;
188
- margin-bottom: 12px;
189
- }
190
-
191
- &__title {
192
- font-size: 16px;
193
- font-weight: 600;
194
- color: #252b3a;
195
- }
196
-
197
- &__content {
198
- min-height: 120px;
199
- max-height: 260px;
200
- overflow: auto;
201
- }
202
-
203
- &__loading {
204
- display: flex;
205
- align-items: center;
206
- justify-content: center;
207
- padding: 24px 0;
208
- }
209
-
210
- &__list {
211
- display: flex;
212
- flex-direction: column;
213
- gap: 8px;
214
- }
215
-
216
- &__item {
217
- padding: 10px 8px;
218
- border-radius: 8px;
219
- cursor: pointer;
220
- transition: all 0.2s ease;
221
-
222
- &:hover {
223
- background-color: #f5f6f9;
224
- }
225
- }
226
-
227
- &__item-title {
228
- font-size: 14px;
229
- color: #252b3a;
230
- margin-bottom: 2px;
231
- }
232
-
233
- &__item-time {
234
- font-size: 12px;
235
- color: #a0a4af;
236
- }
237
-
238
- &__empty {
239
- padding: 16px 0 8px;
240
-
241
- :deep(.van-empty__image) {
242
- width: 80px;
243
- height: 80px;
244
- }
245
-
246
- :deep(.van-empty__description) {
247
- font-size: 13px;
248
- color: #a0a4af;
249
- }
250
- }
251
- }
252
- }
253
- </style>
1
+ <script setup lang="ts">
2
+ import type { ChatHistoryItem } from '@af-mobile-client-vue3/components/common/MateChat/types'
3
+ import { getHistories } from '@af-mobile-client-vue3/components/common/MateChat/apiService'
4
+ import { useChatHistoryCache } from '@af-mobile-client-vue3/components/common/MateChat/composables/useChatHistoryCache'
5
+ import { Empty, Icon, Loading, Popup } from 'vant'
6
+ import { ref } from 'vue'
7
+ import 'vant/es/popup/style'
8
+ import 'vant/es/empty/style'
9
+ import 'vant/es/loading/style'
10
+ import 'vant/es/icon/style'
11
+
12
+ interface Props {
13
+ /**
14
+ * 头部左侧 Logo 图片
15
+ */
16
+ logoImage: string
17
+ /**
18
+ * 头部标题文案
19
+ */
20
+ title: string
21
+ /**
22
+ * 历史对话弹层标题
23
+ */
24
+ historyTitle?: string
25
+ /**
26
+ * FastGPT 应用 ID
27
+ */
28
+ appId: string
29
+ /**
30
+ * FastGPT API Key
31
+ */
32
+ appKey: string
33
+ /**
34
+ * 是否显示标题
35
+ */
36
+ showTitle: boolean
37
+ }
38
+
39
+ interface SessionItem {
40
+ chatId: string
41
+ title: string
42
+ lastTime: string
43
+ }
44
+
45
+ interface Emits {
46
+ /**
47
+ * 选择某条历史会话
48
+ */
49
+ (e: 'selectSession', session: SessionItem): void
50
+ }
51
+
52
+ const props = withDefaults(defineProps<Props>(), {
53
+ historyTitle: '对话历史',
54
+ })
55
+
56
+ const emit = defineEmits<Emits>()
57
+
58
+ const showHistory = ref(false)
59
+ const isLoading = ref(false)
60
+ const sessionList = ref<SessionItem[]>([])
61
+
62
+ // 使用历史会话缓存
63
+ const { getCachedHistory, setCachedHistory } = useChatHistoryCache()
64
+
65
+ function handleOpenHistory() {
66
+ showHistory.value = true
67
+ fetchSessions()
68
+ }
69
+
70
+ function handleSelectSession(session: SessionItem) {
71
+ emit('selectSession', session)
72
+ showHistory.value = false
73
+ }
74
+
75
+ /**
76
+ * 格式化时间字符串为日期时间字符串
77
+ */
78
+ function formatDateTime(timeString: string): string {
79
+ try {
80
+ const date = new Date(timeString)
81
+ const year = date.getFullYear()
82
+ const month = String(date.getMonth() + 1).padStart(2, '0')
83
+ const day = String(date.getDate()).padStart(2, '0')
84
+ const hours = String(date.getHours()).padStart(2, '0')
85
+ const minutes = String(date.getMinutes()).padStart(2, '0')
86
+ return `${year}-${month}-${day} ${hours}:${minutes}`
87
+ }
88
+ catch {
89
+ return timeString
90
+ }
91
+ }
92
+
93
+ /**
94
+ * 将接口返回的历史会话数据转换为组件需要的格式
95
+ */
96
+ function transformHistoryItem(item: ChatHistoryItem): SessionItem {
97
+ return {
98
+ chatId: item.chatId || '',
99
+ title: item.title || item.customTitle || '未命名会话',
100
+ lastTime: item.updateTime ? formatDateTime(item.updateTime) : '',
101
+ }
102
+ }
103
+
104
+ async function fetchSessions() {
105
+ // 先检查缓存
106
+ const cachedList = getCachedHistory(props.appId, props.appKey)
107
+ if (cachedList) {
108
+ // 使用缓存数据
109
+ sessionList.value = cachedList.map(transformHistoryItem)
110
+ return
111
+ }
112
+
113
+ // 缓存不存在或已过期,从服务器获取
114
+ isLoading.value = true
115
+ try {
116
+ const response = await getHistories(props.appId, props.appKey, 0, 5)
117
+
118
+ // 转换数据格式
119
+ if (response.data?.list && Array.isArray(response.data.list)) {
120
+ // 缓存数据
121
+ setCachedHistory(props.appId, props.appKey, response.data.list)
122
+ sessionList.value = response.data.list.map(transformHistoryItem)
123
+ }
124
+ else {
125
+ sessionList.value = []
126
+ // 缓存空数组,避免重复请求
127
+ setCachedHistory(props.appId, props.appKey, [])
128
+ }
129
+ }
130
+ catch (error) {
131
+ console.error('获取历史会话失败:', error)
132
+ sessionList.value = []
133
+ }
134
+ finally {
135
+ isLoading.value = false
136
+ }
137
+ }
138
+ </script>
139
+
140
+ <template>
141
+ <div class="matechat-header-wrapper">
142
+ <McHeader :logo-img="showTitle ? props.logoImage : ''" :title="showTitle ? props.title : ''" :logo-clickable="false">
143
+ <template #operationArea>
144
+ <div class="matechat-header-history-btn" @click="handleOpenHistory">
145
+ <Icon name="clock-o" size="16" />
146
+ </div>
147
+ </template>
148
+ </McHeader>
149
+
150
+ <Popup
151
+ v-model:show="showHistory"
152
+ position="center"
153
+ round
154
+ :overlay="true"
155
+ class="matechat-history-popup"
156
+ >
157
+ <div class="matechat-history-card">
158
+ <div class="matechat-history-card__header">
159
+ <span class="matechat-history-card__title">
160
+ {{ props.historyTitle }}
161
+ </span>
162
+ </div>
163
+ <div class="matechat-history-card__content">
164
+ <div v-if="isLoading" class="matechat-history-card__loading">
165
+ <Loading size="24px" />
166
+ </div>
167
+ <template v-else>
168
+ <div
169
+ v-if="sessionList.length"
170
+ class="matechat-history-card__list"
171
+ >
172
+ <div
173
+ v-for="session in sessionList"
174
+ :key="session.chatId"
175
+ class="matechat-history-card__item"
176
+ @click="handleSelectSession(session)"
177
+ >
178
+ <div class="matechat-history-card__item-title">
179
+ {{ session.title }}
180
+ </div>
181
+ <div class="matechat-history-card__item-time">
182
+ {{ session.lastTime }}
183
+ </div>
184
+ </div>
185
+ </div>
186
+ <div v-else class="matechat-history-card__empty">
187
+ <Empty description="无数据" />
188
+ </div>
189
+ </template>
190
+ </div>
191
+ </div>
192
+ </Popup>
193
+ </div>
194
+ </template>
195
+
196
+ <style scoped lang="less">
197
+ .matechat-header-wrapper {
198
+ position: relative;
199
+ }
200
+
201
+ .matechat-header-history-btn {
202
+ display: inline-flex;
203
+ align-items: center;
204
+ justify-content: center;
205
+ width: 32px;
206
+ height: 32px;
207
+ border-radius: 50%;
208
+ background-color: rgba(255, 255, 255, 0.8);
209
+ cursor: pointer;
210
+ transition: all 0.2s ease;
211
+
212
+ &:hover {
213
+ background-color: rgba(255, 255, 255, 1);
214
+ transform: translateY(-1px);
215
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
216
+ }
217
+ }
218
+
219
+ .matechat-history-popup {
220
+ .matechat-history-card {
221
+ width: 320px;
222
+ max-width: 80vw;
223
+ background-color: #ffffff;
224
+ border-radius: 16px;
225
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
226
+ padding: 16px 16px 12px;
227
+ box-sizing: border-box;
228
+
229
+ &__header {
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: space-between;
233
+ margin-bottom: 12px;
234
+ }
235
+
236
+ &__title {
237
+ font-size: 16px;
238
+ font-weight: 600;
239
+ color: #252b3a;
240
+ }
241
+
242
+ &__content {
243
+ min-height: 120px;
244
+ max-height: 260px;
245
+ overflow: auto;
246
+ }
247
+
248
+ &__loading {
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: center;
252
+ padding: 24px 0;
253
+ }
254
+
255
+ &__list {
256
+ display: flex;
257
+ flex-direction: column;
258
+ gap: 8px;
259
+ }
260
+
261
+ &__item {
262
+ padding: 10px 8px;
263
+ border-radius: 8px;
264
+ cursor: pointer;
265
+ transition: all 0.2s ease;
266
+
267
+ &:hover {
268
+ background-color: #f5f6f9;
269
+ }
270
+ }
271
+
272
+ &__item-title {
273
+ font-size: 14px;
274
+ color: #252b3a;
275
+ margin-bottom: 2px;
276
+ }
277
+
278
+ &__item-time {
279
+ font-size: 12px;
280
+ color: #a0a4af;
281
+ }
282
+
283
+ &__empty {
284
+ padding: 16px 0 8px;
285
+
286
+ :deep(.van-empty__image) {
287
+ width: 80px;
288
+ height: 80px;
289
+ }
290
+
291
+ :deep(.van-empty__description) {
292
+ font-size: 13px;
293
+ color: #a0a4af;
294
+ }
295
+ }
296
+ }
297
+ }
298
+ </style>
@@ -0,0 +1,97 @@
1
+ <script setup lang="ts">
2
+ import { Dialog as VanDialog, Field as VanField, Icon as VanIcon } from 'vant'
3
+ import { ref, watch } from 'vue'
4
+
5
+ interface PasswordDialogProps {
6
+ visible: boolean
7
+ }
8
+
9
+ const props = defineProps<PasswordDialogProps>()
10
+
11
+ const emit = defineEmits<{
12
+ (event: 'confirm', password: string): void
13
+ (event: 'cancel'): void
14
+ }>()
15
+
16
+ const password = ref('')
17
+ const showPassword = ref(false)
18
+ const errorMessage = ref('')
19
+
20
+ watch(() => props.visible, (newVal) => {
21
+ if (newVal) {
22
+ password.value = ''
23
+ errorMessage.value = ''
24
+ showPassword.value = false
25
+ }
26
+ })
27
+
28
+ function handleConfirm() {
29
+ if (!password.value.trim()) {
30
+ errorMessage.value = '请输入密码'
31
+ return
32
+ }
33
+ emit('confirm', password.value.trim())
34
+ }
35
+
36
+ function handleCancel() {
37
+ emit('cancel')
38
+ }
39
+
40
+ function togglePasswordVisibility() {
41
+ showPassword.value = !showPassword.value
42
+ }
43
+
44
+ /**
45
+ * 清空密码输入框和错误信息
46
+ */
47
+ function clearPassword() {
48
+ password.value = ''
49
+ errorMessage.value = ''
50
+ showPassword.value = false
51
+ }
52
+
53
+ /**
54
+ * 设置错误信息
55
+ */
56
+ function setError(message: string) {
57
+ errorMessage.value = message
58
+ }
59
+
60
+ // 暴露方法供父组件调用
61
+ defineExpose({
62
+ clearPassword,
63
+ setError,
64
+ })
65
+ </script>
66
+
67
+ <template>
68
+ <VanDialog
69
+ :show="visible"
70
+ title="请输入密码"
71
+ show-cancel-button
72
+ confirm-button-text="确定"
73
+ cancel-button-text="取消"
74
+ @confirm="handleConfirm"
75
+ @cancel="handleCancel"
76
+ @update:show="(val: boolean) => emit('cancel')"
77
+ >
78
+ <div style="padding: 16px 0">
79
+ <VanField
80
+ v-model="password"
81
+ :type="showPassword ? 'text' : 'password'"
82
+ placeholder="请输入密码"
83
+ :error="!!errorMessage"
84
+ :error-message="errorMessage"
85
+ @update:model-value="errorMessage = ''"
86
+ @keyup.enter="handleConfirm"
87
+ >
88
+ <template #right-icon>
89
+ <VanIcon
90
+ :name="showPassword ? 'eye-o' : 'closed-eye'"
91
+ @click="togglePasswordVisibility"
92
+ />
93
+ </template>
94
+ </VanField>
95
+ </div>
96
+ </VanDialog>
97
+ </template>