af-mobile-client-vue3 1.6.5 → 1.6.7

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.6.5",
4
+ "version": "1.6.7",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -10,6 +10,42 @@ import { showToast } from 'vant'
10
10
  import { ref } from 'vue'
11
11
  import { chatBiz, chatCompletionsStream, getPaginationRecords } from '../apiService'
12
12
 
13
+ /** 空回复兜底文案:归因服务端繁忙 */
14
+ const EMPTY_RESPONSE_MESSAGE = '服务器繁忙,请稍后再试。'
15
+
16
+ /** 网络/请求异常兜底文案 */
17
+ const CHAT_ERROR_MESSAGE = '抱歉,服务暂时不可用,请稍后再试。'
18
+
19
+ /**
20
+ * 判定模型返回内容是否「有效为空」
21
+ */
22
+ function isEmptyModelResponse(content: string | null | undefined): boolean {
23
+ const text = (content ?? '').trim()
24
+ if (!text) {
25
+ return true
26
+ }
27
+
28
+ try {
29
+ const parsed = JSON.parse(text) as Record<string, unknown>
30
+ if (!parsed || typeof parsed !== 'object') {
31
+ return false
32
+ }
33
+
34
+ const msgType = parsed.msgType
35
+ if (msgType === 'card' || msgType === 'transfer') {
36
+ return false
37
+ }
38
+ if (msgType === 'options' && Array.isArray(parsed.options) && parsed.options.length > 0) {
39
+ return false
40
+ }
41
+ }
42
+ catch {
43
+ // 非 JSON 普通文本,trim 已判定非空
44
+ }
45
+
46
+ return false
47
+ }
48
+
13
49
  /**
14
50
  * 生成随机 chatId
15
51
  */
@@ -35,6 +71,24 @@ export function useMateChat(config: MateChatConfig) {
35
71
  // 使用会话消息缓存
36
72
  const { getCachedMessages, setCachedMessages, appendMessages } = useChatMessagesCache()
37
73
 
74
+ /** 请求成功但 content 为空时,统一替换为服务端繁忙提示 */
75
+ function applyEmptyResponseFallback(
76
+ loadingIndex: number,
77
+ userText: string,
78
+ currentChatId: string,
79
+ ) {
80
+ messages.value[loadingIndex] = {
81
+ from: 'model',
82
+ content: EMPTY_RESPONSE_MESSAGE,
83
+ loading: false,
84
+ typing: false,
85
+ }
86
+ appendMessages(currentChatId, [
87
+ { from: 'user', content: userText },
88
+ { from: 'model', content: EMPTY_RESPONSE_MESSAGE, loading: false, typing: false },
89
+ ])
90
+ }
91
+
38
92
  /**
39
93
  * 新建会话:回到起始页并清空历史消息,生成新的 chatId
40
94
  * 同时清除历史会话列表缓存,以便下次打开时重新获取
@@ -105,6 +159,9 @@ export function useMateChat(config: MateChatConfig) {
105
159
  { from: 'service', content: '您好,客服xxx工号xxx为你服务。功能开发中,敬请期待。' },
106
160
  ])
107
161
  }
162
+ else if (isEmptyModelResponse(result.content)) {
163
+ applyEmptyResponseFallback(loadingMessageIndex, evt, chatId.value)
164
+ }
108
165
  else {
109
166
  let typing = true
110
167
  try {
@@ -135,13 +192,13 @@ export function useMateChat(config: MateChatConfig) {
135
192
  console.error('聊天请求失败:', error)
136
193
  messages.value[loadingMessageIndex] = {
137
194
  from: 'model',
138
- content: '抱歉,服务暂时不可用,请稍后再试。',
195
+ content: CHAT_ERROR_MESSAGE,
139
196
  loading: false,
140
197
  }
141
198
  // 更新缓存:追加用户消息和错误消息
142
199
  appendMessages(chatId.value, [
143
200
  { from: 'user', content: evt },
144
- { from: 'model', content: '抱歉,服务暂时不可用,请稍后再试。', loading: false },
201
+ { from: 'model', content: CHAT_ERROR_MESSAGE, loading: false },
145
202
  ])
146
203
  showToast(error?.message || '请求失败,请稍后再试')
147
204
  }
@@ -229,6 +286,11 @@ export function useMateChat(config: MateChatConfig) {
229
286
  msg.typing = true
230
287
  }
231
288
 
289
+ if (isEmptyModelResponse(msg.content)) {
290
+ applyEmptyResponseFallback(loadingMessageIndex, evt, chatId.value)
291
+ return
292
+ }
293
+
232
294
  // 统一更新缓存
233
295
  appendMessages(chatId.value, [
234
296
  { from: 'user', content: evt },
@@ -241,12 +303,12 @@ export function useMateChat(config: MateChatConfig) {
241
303
  if (!msg) {
242
304
  return
243
305
  }
244
- msg.content = '抱歉,服务暂时不可用,请稍后再试。'
306
+ msg.content = CHAT_ERROR_MESSAGE
245
307
  msg.loading = false
246
308
  // 更新缓存:追加用户消息和错误消息
247
309
  appendMessages(chatId.value, [
248
310
  { from: 'user', content: evt },
249
- { from: 'model', content: '抱歉,服务暂时不可用,请稍后再试。', loading: false },
311
+ { from: 'model', content: CHAT_ERROR_MESSAGE, loading: false },
250
312
  ])
251
313
  showToast((error as any)?.message || '请求失败,请稍后再试')
252
314
  },
@@ -0,0 +1,122 @@
1
+ <script setup lang="ts">
2
+ import SignatureComponent from '@af-mobile-client-vue3/components/core/Signature/SignatureComponent.vue'
3
+ import FilePreview from '@af-mobile-client-vue3/components/data/FilePreview/index.vue'
4
+ import { runLogic } from '@af-mobile-client-vue3/services/api/common'
5
+ import { showToast, Button as VanButton, Dialog as VanDialog } from 'vant'
6
+ import { computed, onMounted, ref } from 'vue'
7
+ import { useRoute } from 'vue-router'
8
+
9
+ /** 生成合同接口返回的 data 结构 */
10
+ interface ContractDetail {
11
+ f_signature_url?: string
12
+ f_signature_path?: string
13
+ f_contract_signature_id?: string
14
+ f_contract_type?: string
15
+ f_signature_location?: string
16
+ [key: string]: unknown
17
+ }
18
+
19
+ // pdf的路径
20
+ const previewPath = ref('')
21
+ // pdf查看组件引用
22
+ const filePreviewRef = ref()
23
+ // 显示签字弹窗
24
+ const previewVisible = ref(false)
25
+ // 签字组件引用
26
+ const signatureRef = ref<any>()
27
+ // 生成的合同信息
28
+ const contractdetails = ref<ContractDetail>({})
29
+ const id = computed(() => useRoute().query.id?.toString() || '')
30
+
31
+ function openSign() {
32
+ previewVisible.value = true
33
+ }
34
+ function onDialogCancel() {
35
+ previewVisible.value = false
36
+ }
37
+ async function onSignSubmit() {
38
+ const signData = await Promise.resolve(signatureRef.value?.getSignatureData?.())
39
+ console.log('signData', signData)
40
+ const resData = {
41
+ f_signature_path: contractdetails.value.f_signature_path,
42
+ f_contract_url: contractdetails.value.f_signature_url,
43
+ f_contract_signature_id: contractdetails.value.f_contract_signature_id,
44
+ f_signature_location: contractdetails.value.f_signature_location,
45
+ f_contract_type: contractdetails.value.f_contract_type,
46
+ image: signData,
47
+ }
48
+ runLogic('contractSignature', resData, 'af-revenue').then(() => {
49
+ showToast('合同签字成功!')
50
+ history.back()
51
+ })
52
+ }
53
+
54
+ onMounted(() => {
55
+ runLogic('getNoSignContract', { id: id.value }, 'af-revenue').then((res) => {
56
+ contractdetails.value = res[0]
57
+ const path = res[0].f_signature_url
58
+ filePreviewRef.value.init({ path })
59
+ })
60
+ })
61
+ </script>
62
+
63
+ <template>
64
+ <div class="contract-container">
65
+ <div>
66
+ <FilePreview
67
+ ref="filePreviewRef"
68
+ :key="`pdf-${previewPath}`"
69
+ style="height: 95vh"
70
+ />
71
+ <div class="bottom-bar">
72
+ <VanButton type="primary" block @click="openSign">
73
+ 签字提交
74
+ </VanButton>
75
+ </div>
76
+ </div>
77
+ <VanDialog
78
+ v-model:show="previewVisible"
79
+ title="合同信息"
80
+ show-cancel-button
81
+ show-confirm-button
82
+ style="max-width: 90vw; max-height: 40vh;"
83
+ @confirm="onSignSubmit"
84
+ @cancel="onDialogCancel"
85
+ >
86
+ <SignatureComponent ref="signatureRef" style="padding: 20px;" label="用户签字" :form-readonly="false" />
87
+ </VanDialog>
88
+ </div>
89
+ </template>
90
+
91
+ <style scoped lang="less">
92
+ .contract-container {
93
+ height: 95vh;
94
+ display: flex;
95
+ flex-direction: column;
96
+ }
97
+
98
+ :deep(.contract-form-group) {
99
+ flex: 1;
100
+ display: flex;
101
+ flex-direction: column;
102
+ overflow: hidden;
103
+ }
104
+
105
+ :deep(.contract-form) {
106
+ flex: 1;
107
+ overflow: auto;
108
+ }
109
+
110
+ .bottom-bar {
111
+ position: fixed;
112
+ left: 0;
113
+ right: 0;
114
+ bottom: 0;
115
+ background: #fff;
116
+ padding: 8px 12px calc(8px + env(safe-area-inset-bottom));
117
+ display: grid;
118
+ gap: 8px;
119
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
120
+ z-index: 100;
121
+ }
122
+ </style>
@@ -1,39 +1,48 @@
1
- <script setup lang="ts">
2
- import XCellList from '@af-mobile-client-vue3/components/data/XCellList/index.vue'
3
- import { computed } from 'vue'
4
- import { useRoute, useRouter } from 'vue-router'
5
-
6
- const router = useRouter()
7
- const configName = 'userinfoContractCRUD'
8
- const serviceName = 'af-system'
9
- const userId = computed(() => useRoute().query.userId?.toString() || '')
10
- function addContract() {
11
- router.push({
12
- name: 'addcontract',
13
- query: {
14
- userId: userId.value,
15
- },
16
- })
17
- }
18
- function previewContract(res) {
19
- router.push({
20
- name: 'viewContract',
21
- query: {
22
- previewPath: res.c_f_signature_url,
23
- },
24
- })
25
- }
26
- </script>
27
-
28
- <template>
29
- <XCellList
30
- :config-name="configName"
31
- :service-name="serviceName"
32
- :custom-add="true"
33
- :fix-query-form="{
34
- c_f_userinfo_id: userId,
35
- }"
36
- @add="addContract"
37
- @preview-contract="previewContract"
38
- />
39
- </template>
1
+ <script setup lang="ts">
2
+ import XCellList from '@af-mobile-client-vue3/components/data/XCellList/index.vue'
3
+ import { computed } from 'vue'
4
+ import { useRoute, useRouter } from 'vue-router'
5
+
6
+ const router = useRouter()
7
+ const configName = 'userinfoContractCRUD'
8
+ const serviceName = 'af-system'
9
+ const userId = computed(() => useRoute().query.userId?.toString() || '')
10
+ function addContract() {
11
+ router.push({
12
+ name: 'addcontract',
13
+ query: {
14
+ userId: userId.value,
15
+ },
16
+ })
17
+ }
18
+ function previewContract(res) {
19
+ router.push({
20
+ name: 'viewContract',
21
+ query: {
22
+ previewPath: res.c_f_signature_url,
23
+ },
24
+ })
25
+ }
26
+ function sign(res) {
27
+ router.push({
28
+ name: 'signcontract',
29
+ query: {
30
+ id: res.c_f_contract_signature_id,
31
+ },
32
+ })
33
+ }
34
+ </script>
35
+
36
+ <template>
37
+ <XCellList
38
+ :config-name="configName"
39
+ :service-name="serviceName"
40
+ :custom-add="true"
41
+ :fix-query-form="{
42
+ c_f_userinfo_id: userId,
43
+ }"
44
+ @add="addContract"
45
+ @preview-contract="previewContract"
46
+ @sign-contract="sign"
47
+ />
48
+ </template>