@tencentcloud/ai-desk-customer-vue 1.5.4 → 1.5.6

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 (24) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/components/CustomerServiceChat/index-web.vue +2 -7
  3. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +1 -0
  4. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +6 -6
  5. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-mobile.vue +9 -9
  6. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-pc.vue +4 -7
  7. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/index.vue +6 -12
  8. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-rating/message-rating-star.vue +25 -9
  9. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-robot-welcome.vue +1 -0
  10. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-transfer-with-desc.vue +2 -7
  11. package/interface.ts +2 -2
  12. package/locales/en/aidesk.ts +3 -0
  13. package/locales/fil/aidesk.ts +3 -0
  14. package/locales/id/aidesk.ts +3 -0
  15. package/locales/ja/aidesk.ts +3 -0
  16. package/locales/ms/aidesk.ts +3 -0
  17. package/locales/ru/aidesk.ts +3 -0
  18. package/locales/th/aidesk.ts +3 -0
  19. package/locales/vi/aidesk.ts +3 -0
  20. package/locales/zh_cn/aidesk.ts +3 -0
  21. package/locales/zh_tw/aidesk.ts +3 -0
  22. package/package.json +1 -1
  23. package/server.ts +47 -15
  24. package/utils/utils.ts +16 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 1.5.6 @2025.8.5
2
+
3
+ ### Features
4
+ - 保障反复切换账号、切换客服号场景下的会话状态的准确性。
5
+ - 强校验 `userID`。
6
+
7
+ ## 1.5.5 @2024.7.30
8
+
9
+ ### Fixed
10
+ - 表单消息状态更新问题。
11
+
1
12
  ## 1.5.4 @2025.7.24
2
13
 
3
14
  ### Features
@@ -256,7 +256,7 @@ const initLanguage = () => {
256
256
  let navigatorLang = convertLanguageToLowercase(navigator.language);
257
257
  language = getValidLanguage(navigatorLang);
258
258
  }
259
- }
259
+ }
260
260
  currentLanguage.value = language;
261
261
  state.set('currentLanguage', language);
262
262
  TUITranslateService.changeLanguage(language);
@@ -392,13 +392,7 @@ function onCurrentConversationIDUpdate(conversationID: string) {
392
392
  return;
393
393
  }
394
394
 
395
- if (currentConversationID.value === conversationID) {
396
- return;
397
- }
398
-
399
395
  currentConversationID.value = conversationID;
400
-
401
- // The TUICustomerServicePlugin plugin determines if the current conversation is a customer service conversation, then sets chatType and activates the conversation.
402
396
  activeConversation();
403
397
  }
404
398
 
@@ -451,6 +445,7 @@ function changeLanguage(languageCode: string) {
451
445
  activeConversation();
452
446
  });
453
447
  }
448
+
454
449
  function activeConversation() {
455
450
  TUICore.callService({
456
451
  serviceName: TUIConstants.TUICustomerServicePlugin.SERVICE.NAME,
@@ -382,6 +382,7 @@ function setStatus(status: ReadState) {
382
382
  }
383
383
 
384
384
  .message-body-nick-name {
385
+ font-family: PingFangSC-Regular;
385
386
  display: block;
386
387
  margin-bottom: 4px;
387
388
  font-size: 12px;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="message-form">
3
3
  <BranchPc
4
- :payload ="payload"
4
+ :payload ="forwardPayload"
5
5
  @input-click="handleContentListItemClick"
6
6
  />
7
7
  </div>
@@ -11,7 +11,7 @@
11
11
  import vue from '../../../../../../../adapter-vue';
12
12
  import BranchPc from './branch-pc.vue';
13
13
  import { customerServicePayloadType } from '../../../../../../../interface';
14
- const { computed} = vue;
14
+ const { computed } = vue;
15
15
 
16
16
  interface branchItem {
17
17
  content: string;
@@ -35,9 +35,6 @@ export default {
35
35
  },
36
36
  emits: ['sendMessage', 'heightChanged'],
37
37
  setup(props: Props, { emit }) {
38
- const payload = computed<customerServicePayloadType>(() => {
39
- return props.payload;
40
- });
41
38
  const handleContentListItemClick = (branch: branchItem, cloudCustomData?: string) => {
42
39
  emit('sendMessage', { text: branch.content }, cloudCustomData || '');
43
40
  };
@@ -45,8 +42,11 @@ export default {
45
42
  const handleFormSaveInputSubmit = (text: string) => {
46
43
  emit('sendMessage', { text });
47
44
  };
45
+ const forwardPayload = computed(() => {
46
+ return props.payload;
47
+ });
48
48
  return {
49
- payload,
49
+ forwardPayload,
50
50
  handleContentListItemClick,
51
51
  handleFormSaveInputSubmit,
52
52
  };
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="form-mobile-container">
3
- <div v-if="!finishSubmit && props.payload.nodeStatus !== 2" class="before-form">
3
+ <div v-if="props.payload.nodeStatus !== 2" class="before-form">
4
4
  <Icon :src="iconForm" width="60px" height="60px" style="margin:5px 4px"/>
5
5
  <div :class="props.payload.nodeStatus === 1 ? 'form-button-disable': 'form-button'" @click="clickShowDialog">
6
6
  {{ TUITranslateService.t("AIDesk.立即填写") }}
@@ -28,10 +28,10 @@
28
28
  v-for="(item, index) in props.payload.content.inputVariables"
29
29
  :key="index"
30
30
  >
31
- <div v-if="!finishSubmit && item.formType == 0 && props.payload.nodeStatus == 0">
31
+ <div v-if="item.formType == 0 && props.payload.nodeStatus == 0">
32
32
  <InputMobile :placeholder="item.placeholder" :variableValue="item.variableValue" :name="item.name" :isRequired="item.isRequired" @input-change="handleInputChange" :validator="item.isRequired == 1 && isValid(item.name)"/>
33
33
  </div>
34
- <div v-else-if="!finishSubmit && item.formType == 1 && props.payload.nodeStatus == 0">
34
+ <div v-else-if="item.formType == 1 && props.payload.nodeStatus == 0">
35
35
  <RadioMobile :chooseItemList="item.chooseItemList" :name="item.name" :isRequired="item.isRequired" @input-change="handleInputChange" :validator="item.isRequired == 1 && isValid(item.name)"/>
36
36
  </div>
37
37
  <div v-else class="variable-value-container-mobile">
@@ -43,7 +43,7 @@
43
43
  </div>
44
44
  </div>
45
45
  </div>
46
- <div class="button-container"v-if="!finishSubmit && props.payload.nodeStatus === 0">
46
+ <div class="button-container" v-if="props.payload.nodeStatus === 0">
47
47
  <div class="button" @click="handleSendForm">
48
48
  {{ TUITranslateService.t("AIDesk.提交") }}
49
49
  </div>
@@ -52,10 +52,10 @@
52
52
  </FormPopup>
53
53
  </div>
54
54
  </div>
55
- <div v-if="finishSubmit || props.payload.nodeStatus == 2" class="before-form">
55
+ <div v-if="props.payload.nodeStatus == 2" class="before-form">
56
56
  <div class="icon-container">
57
57
  <Icon class="form-icon" :src="iconForm" width="60px" height="60px" style="margin:5px 4px"/>
58
- <Icon v-if="finishSubmit || props.payload.nodeStatus == 2" class="form-icon-check" width="26px" height="26px" :src="iconFormFilled"/>
58
+ <Icon v-if="props.payload.nodeStatus == 2" class="form-icon-check" width="26px" height="26px" :src="iconFormFilled"/>
59
59
  </div>
60
60
  <div class="form-button" @click="clickShowDialog">
61
61
  {{ TUITranslateService.t("AIDesk.查看内容") }}
@@ -102,7 +102,6 @@ export default {
102
102
  const showDialog = ref<boolean>(false);
103
103
  const mapValue=ref({});
104
104
  const isSubmit = ref<boolean>(false);
105
- const finishSubmit = ref<boolean>(false);
106
105
  const hasNullValue = ref<boolean>(true);
107
106
  onMounted(() => {
108
107
  let inputVariables = props.payload.content.inputVariables ?? [];
@@ -163,7 +162,6 @@ export default {
163
162
  }),
164
163
  };
165
164
  emit('sendMessage', submitData);
166
- finishSubmit.value = true;
167
165
  isSubmit.value = false;
168
166
  };
169
167
  const handleInputChange = ({name,value}) => {
@@ -184,7 +182,6 @@ export default {
184
182
  clickShowDialog,
185
183
  iconClose,
186
184
  checkValidator,
187
- finishSubmit,
188
185
  showValue,
189
186
  mapValue,
190
187
  isSubmit,
@@ -314,6 +311,7 @@ export default {
314
311
  display:flex;
315
312
  flex-direction: column;
316
313
  justify-content: center;
314
+ font-family: PingFangSC-Regular;
317
315
  .form-button {
318
316
  display:flex;
319
317
  justify-content: center;
@@ -321,6 +319,7 @@ export default {
321
319
  background-color: #1c66e5;
322
320
  color:white;
323
321
  border-radius:20px;
322
+ min-width: 100px;
324
323
  }
325
324
  .form-button-disable {
326
325
  background-color: #dbdbdb;
@@ -329,6 +328,7 @@ export default {
329
328
  padding: 6px 10px;
330
329
  color: white;
331
330
  border-radius: 20px;
331
+ min-width: 100px;
332
332
  }
333
333
  .icon-container {
334
334
  position: relative;
@@ -4,7 +4,7 @@
4
4
  <div class="form-title" v-if="checkTip()">
5
5
  {{ props.payload.content.tip }}
6
6
  </div>
7
- <div class="form-finish-title-right" v-if="finishSubmit || props.payload.nodeStatus == 2" >
7
+ <div class="form-finish-title-right" v-if="props.payload.nodeStatus == 2" >
8
8
  <Icon :src="iconSucess" style="margin:0px 4px"/>
9
9
  <div class="form-finish-title">
10
10
  {{ TUITranslateService.t("AIDesk.已提交") }}
@@ -17,17 +17,17 @@
17
17
  :key="index"
18
18
  >
19
19
  <LabelPC :name="item.name" :is-required="item.isRequired" />
20
- <div v-if="!finishSubmit && item.formType == 0 && props.payload.nodeStatus !== 2">
20
+ <div v-if="item.formType == 0 && props.payload.nodeStatus !== 2">
21
21
  <InputPC :placeholder="item.placeholder" :variableValue="item.variableValue" :name="item.name" :isRequired="item.isRequired" @input-change="handleInputChange" :validator="item.isRequired == 1 && isValid(item.name)" :isDisabled="props.payload.nodeStatus === 1"/>
22
22
  </div>
23
- <div v-else-if="!finishSubmit && item.formType == 1 && props.payload.nodeStatus !== 2">
23
+ <div v-else-if="item.formType == 1 && props.payload.nodeStatus !== 2">
24
24
  <RadioPC :chooseItemList="item.chooseItemList" :name="item.name" :isRequired="item.isRequired" @input-change="handleInputChange" :validator="item.isRequired == 1 && isValid(item.name)" :isDisabled="props.payload.nodeStatus === 1"/>
25
25
  </div>
26
26
  <div v-else class="variable-value-container">
27
27
  {{ item.variableValue == '' || item.variableValue == null ? mapValue[item.name] : item.variableValue}}
28
28
  </div>
29
29
  </div>
30
- <div class="button-container"v-if="!finishSubmit && props.payload.nodeStatus !== 2">
30
+ <div class="button-container" v-if="props.payload.nodeStatus !== 2">
31
31
  <div :class="props.payload.nodeStatus === 1 ? 'button-disable' : 'button'" @click="handleSendForm">
32
32
  {{ TUITranslateService.t("AIDesk.提交") }}
33
33
  </div>
@@ -66,7 +66,6 @@ export default {
66
66
  setup(props: Props, { emit }) {
67
67
  const mapValue=ref({});
68
68
  const isSubmit = ref<boolean>(false);
69
- const finishSubmit = ref<boolean>(false);
70
69
  const hasNullValue = ref<boolean>(true);
71
70
  onMounted(()=>{
72
71
  let inputVariables = props.payload.content.inputVariables ?? [];
@@ -118,7 +117,6 @@ export default {
118
117
  }),
119
118
  };
120
119
  emit('sendMessage', submitData);
121
- finishSubmit.value = true;
122
120
  isSubmit.value = false;
123
121
  };
124
122
 
@@ -141,7 +139,6 @@ export default {
141
139
  handleInputChange,
142
140
  iconSucess,
143
141
  checkValidator,
144
- finishSubmit,
145
142
  showValue,
146
143
  mapValue,
147
144
  isSubmit,
@@ -1,9 +1,9 @@
1
1
  <template>
2
2
  <div v-if="isPC" class="message-input">
3
- <FormPC :payload="payloads" @sendMessage="handleSendForm" />
3
+ <FormPC :payload="forwardPayload" @sendMessage="handleSendForm" />
4
4
  </div>
5
5
  <div v-else>
6
- <FormMobile :payload="payloads" @sendMessage="handleSendForm" @showFormPopup="handleShowFormPopup"/>
6
+ <FormMobile :payload="forwardPayload" @sendMessage="handleSendForm" @showFormPopup="handleShowFormPopup"/>
7
7
  </div>
8
8
  </template>
9
9
 
@@ -15,11 +15,6 @@ import { customerServicePayloadType } from '../../../../../../../interface';
15
15
  import { isPC } from '../../../../../../../utils/env';
16
16
  const { computed } = vue;
17
17
 
18
- interface branchItem {
19
- content: string;
20
- desc: string;
21
- }
22
-
23
18
  interface Props {
24
19
  payload: customerServicePayloadType;
25
20
  }
@@ -37,18 +32,17 @@ export default {
37
32
  },
38
33
  emits: ['sendMessage','showFormPopup'],
39
34
  setup(props: Props, { emit }) {
40
- const payloads = computed<customerServicePayloadType>(() => {
41
- return props.payload;
42
- });
43
35
  const handleSendForm = (data: any) => {
44
36
  emit('sendMessage', data);
45
37
  };
46
38
  const handleShowFormPopup = (data: boolean) => {
47
39
  emit('showFormPopup', data);
48
40
  }
49
-
41
+ const forwardPayload = computed(() => {
42
+ return props.payload;
43
+ });
50
44
  return {
51
- payloads,
45
+ forwardPayload,
52
46
  handleSendForm,
53
47
  isPC,
54
48
  handleShowFormPopup
@@ -30,15 +30,21 @@
30
30
  </div>
31
31
  </div>
32
32
  </div>
33
- <div :style="{ marginTop: 10 + 'px', marginBottom: 10 + 'px', wordBreak: 'normal' }">
34
- {{
35
- hoverValue === -1
36
- ? value === -1
37
- ? TUITranslateService.t("AIDesk.如果满意请给好评哦~")
38
- : desc[value]
39
- : desc[hoverValue]
40
- }}
33
+ <div>
34
+ <div :style="{ marginTop: 10 + 'px', marginBottom: 10 + 'px', wordBreak: 'normal' }">
35
+ {{
36
+ hoverValue === -1
37
+ ? value === -1
38
+ ? TUITranslateService.t("AIDesk.如果满意请给好评哦~")
39
+ : desc[value]
40
+ : desc[hoverValue]
41
+ }}
42
+ </div>
43
+ <div class="hidden-desc">
44
+ {{ TUITranslateService.t("AIDesk.如果满意请给好评哦~") }}
45
+ </div>
41
46
  </div>
47
+
42
48
  <button
43
49
  class="submit-button"
44
50
  :disabled="hasReply || hasExpire"
@@ -201,11 +207,11 @@ export default {
201
207
  }
202
208
 
203
209
  .message-rating-star {
210
+ font-family: PingFangSC-Regular;
204
211
  text-align: center;
205
212
  display: flex;
206
213
  flex-flow: column wrap;
207
214
  justify-content: center;
208
-
209
215
  align-items: center;
210
216
  }
211
217
 
@@ -228,4 +234,14 @@ export default {
228
234
  width: 62%;
229
235
  }
230
236
 
237
+ .hidden-desc {
238
+ width: 100%;
239
+ overflow-wrap: break-word;
240
+ word-break: normal;
241
+ visibility: hidden;
242
+ padding: 0;
243
+ margin: 0;
244
+ height: 0;
245
+ }
246
+
231
247
  </style>
@@ -134,6 +134,7 @@ export default {
134
134
  justify-content: space-between;
135
135
  align-items: center;
136
136
  width: 100%;
137
+ gap: 10px;
137
138
  }
138
139
 
139
140
  .welcome-title-left-container {
@@ -1,14 +1,12 @@
1
1
  <template>
2
2
  <div :class="['text', isPC && 'text-select']">
3
- {{ props.payload.description }}
3
+ {{ description }}
4
4
  </div>
5
5
  </template>
6
6
 
7
7
  <script lang="ts">
8
- import vue from '../../../../../../adapter-vue';
9
8
  import { customerServicePayloadType } from '../../../../../../interface';
10
9
  import { isPC } from '../../../../../../utils/env';
11
- const { computed } = vue;
12
10
 
13
11
  interface Props {
14
12
  payload: customerServicePayloadType;
@@ -22,11 +20,8 @@ export default {
22
20
  },
23
21
  },
24
22
  setup(props: Props) {
25
- const payload = computed<customerServicePayloadType>(() => {
26
- return props.payload;
27
- });
28
23
  return {
29
- props,
24
+ description: props.payload.description,
30
25
  isPC,
31
26
  };
32
27
  },
package/interface.ts CHANGED
@@ -16,7 +16,7 @@ export interface customerServicePayloadType {
16
16
  nodeID?: string;
17
17
  env?: string;
18
18
  }
19
- description?: string,
19
+ description?: string;
20
20
  }
21
21
 
22
22
  interface IMenuItem {
@@ -235,7 +235,7 @@ export interface ITransferToTaskFlowModel {
235
235
  }
236
236
 
237
237
  export interface ITransferToHumanModel {
238
- groupID?: string;
238
+ groupID?: number;
239
239
  specificMemberList?: Array<string>;
240
240
  description?: string;
241
241
  }
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"Unable to send the file as it exceeds 100 MB",
31
31
  "image 大小超过 20MB,无法发送":"Unable to send the image as it exceeds 20 MB",
32
32
  "video 大小超过 100MB,无法发送":"Unable to send the video as it exceeds 100 MB",
33
+ "userID 只能包含可打印 ASCII 字符": "The userID can only contain printable ASCII characters",
34
+ "userID 不能包含'administrator'": "The userID cannot contain the string 'administrator'",
35
+ "userID 长度不能超过45字节": "The length of the userID cannot exceed 45 bytes",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"Lampas sa 100MB ang file, hindi maipadala",
31
31
  "image 大小超过 20MB,无法发送":"Hindi maipadala ang larawan dahil lampas ito sa 20 MB",
32
32
  "video 大小超过 100MB,无法发送":"Hindi maipadala ang video dahil lampas ito sa 100 MB",
33
+ "userID 只能包含可打印 ASCII 字符": "Ang UserID ay dapat naglalaman lamang ng printable ASCII characters",
34
+ "userID 不能包含'administrator'": "Ang UserID ay hindi dapat maglaman ng 'administrator'",
35
+ "userID 长度不能超过45字节": "Ang haba ng UserID ay hindi dapat lumampas sa 45 bytes",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"Ukuran file melebihi 100MB, tidak dapat dikirim",
31
31
  "image 大小超过 20MB,无法发送":"Tidak dapat mengirim gambar karena melebihi 20 MB",
32
32
  "video 大小超过 100MB,无法发送":"Tidak dapat mengirim video karena melebihi 100 MB",
33
+ "userID 只能包含可打印 ASCII 字符": "UserID hanya boleh berisi karakter ASCII yang dapat dicetak",
34
+ "userID 不能包含'administrator'": "UserID tidak boleh mengandung 'administrator'",
35
+ "userID 长度不能超过45字节": "Panjang UserID tidak boleh melebihi 45 byte",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"ファイルサイズが100MBを超えているため送信できません",
31
31
  "image 大小超过 20MB,无法发送":"画像のサイズが20MBを超えているため送信できません",
32
32
  "video 大小超过 100MB,无法发送":"動画のサイズが100MBを超えているため送信できません",
33
+ "userID 只能包含可打印 ASCII 字符": "UserIDは印字可能なASCII文字のみを含める必要があります",
34
+ "userID 不能包含'administrator'": "UserIDに'administrator'を含めることはできません",
35
+ "userID 长度不能超过45字节": "UserIDの長さは45バイトを超えてはいけません",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"Saiz fail melebihi 100MB, tidak boleh dihantar",
31
31
  "image 大小超过 20MB,无法发送":"Tidak dapat menghantar gambar kerana melebihi 20 MB",
32
32
  "video 大小超过 100MB,无法发送":"Tidak dapat menghantar video kerana melebihi 100 MB",
33
+ "userID 只能包含可打印 ASCII 字符": "UserID hanya boleh mengandung aksara ASCII yang boleh dicetak",
34
+ "userID 不能包含'administrator'": "UserID tidak boleh mengandung 'administrator'",
35
+ "userID 长度不能超过45字节": "Panjang UserID tidak boleh melebihi 45 bait",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"Размер файла превышает 100MB, отправка невозможна",
31
31
  "image 大小超过 20MB,无法发送":"Невозможно отправить изображение, так как оно превышает 20 MB",
32
32
  "video 大小超过 100MB,无法发送":"Невозможно отправить видео, так как оно превышает 100 MB",
33
+ "userID 只能包含可打印 ASCII 字符": "UserID должен содержать только печатные символы ASCII",
34
+ "userID 不能包含'administrator'": "UserID не должен содержать 'administrator'",
35
+ "userID 长度不能超过45字节": "Длина UserID не должна превышать 45 байт",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"ขนาดไฟล์เกิน 100MB ไม่สามารถส่งได้",
31
31
  "image 大小超过 20MB,无法发送":"ไม่สามารถส่งรูปภาพได้เนื่องจากขนาดเกิน 20 MB",
32
32
  "video 大小超过 100MB,无法发送":"ไม่สามารถส่งวิดีโอได้เนื่องจากขนาดเกิน 100 MB",
33
+ "userID 只能包含可打印 ASCII 字符": "UserID ต้องมีเฉพาะตัวอักษร ASCII ที่พิมพ์ได้เท่านั้น",
34
+ "userID 不能包含'administrator'": "UserID ห้ามมี 'administrator'",
35
+ "userID 长度不能超过45字节": "ความยาว UserID ต้องไม่เกิน 45 ไบต์",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"Kích thước tệp vượt quá 100MB, không thể gửi",
31
31
  "image 大小超过 20MB,无法发送":"Không thể gửi hình ảnh do vượt quá 20 MB",
32
32
  "video 大小超过 100MB,无法发送":"Không thể gửi video do vượt quá 100 MB",
33
+ "userID 只能包含可打印 ASCII 字符": "UserID chỉ được chứa ký tự ASCII có thể in được",
34
+ "userID 不能包含'administrator'": "UserID không được chứa 'administrator'",
35
+ "userID 长度不能超过45字节": "Độ dài UserID không được vượt quá 45 byte",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送": "file 大小超过 100MB,无法发送",
31
31
  "image 大小超过 20MB,无法发送":"image 大小超过 20MB,无法发送",
32
32
  "video 大小超过 100MB,无法发送":"video 大小超过 100MB,无法发送",
33
+ "userID 只能包含可打印 ASCII 字符": "userID 只能包含可打印 ASCII 字符",
34
+ "userID 不能包含'administrator'": "userID 不能包含'administrator'",
35
+ "userID 长度不能超过45字节": "userID 长度不能超过45字节",
33
36
  }
34
37
  export default AIDesk;
@@ -30,5 +30,8 @@ const AIDesk = {
30
30
  "file 大小超过 100MB,无法发送":"檔案大小超過100MB,無法傳送",
31
31
  "image 大小超过 20MB,无法发送":"圖片大小超過20MB,無法傳送",
32
32
  "video 大小超过 100MB,无法发送":"影片大小超過100MB,無法傳送",
33
+ "userID 只能包含可打印 ASCII 字符": "userID 只能包含可打印 ASCII 字元",
34
+ "userID 不能包含'administrator'": "userID 不能包含'administrator'",
35
+ "userID 长度不能超过45字节": "userID 長度不能超過45位元組",
33
36
  }
34
37
  export default AIDesk;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencentcloud/ai-desk-customer-vue",
3
- "version": "1.5.4",
3
+ "version": "1.5.6",
4
4
  "description": "Vue2/Vue3 UIKit for AI Desk",
5
5
  "main": "index",
6
6
  "keywords": [
package/server.ts CHANGED
@@ -19,9 +19,9 @@ import TUIChatEngine, {
19
19
  import Log from './utils/logger';
20
20
  import { version } from './package.json'
21
21
  import { Toast, TOAST_TYPE } from "./components/common/Toast/index-web";
22
- import { switchReadStatus, transferToHuman, transferToTaskFlow } from "./utils/utils";
22
+ import { switchReadStatus, transferToHuman, transferToTaskFlow, validateUserID } from "./utils/utils";
23
23
  import state from "./utils/state";
24
- import { CUSTOM_MESSAGE_SRC, USER_DEFAULT_AVATAR } from "./constant";
24
+ import { USER_DEFAULT_AVATAR } from "./constant";
25
25
  import { vueVersion } from "./adapter-vue-web";
26
26
  import { ITransferToHumanModel, ITransferToTaskFlowModel } from './interface';
27
27
 
@@ -46,6 +46,7 @@ export default class TUICustomerServer {
46
46
  private currentCustomerServiceID: string;
47
47
  private loggedInUserID: string;
48
48
  private myProfile: IProfile;
49
+ private paramsForActiveAgain: any;
49
50
  constructor() {
50
51
  TUICore.registerService(TUIConstants.TUICustomerServicePlugin.SERVICE.NAME, this);
51
52
  TUICore.registerExtension(TUIConstants.TUIContact.EXTENSION.CONTACT_LIST.EXT_ID, this);
@@ -63,9 +64,9 @@ export default class TUICustomerServer {
63
64
  return TUICustomerServer.instance;
64
65
  }
65
66
 
66
- private loginCustomerUIKit(SDKAppID:number, userID:string, userSig:string) {
67
+ private async loginCustomerUIKit(SDKAppID:number, userID:string, userSig:string) {
67
68
  clearChatStorage(SDKAppID, userID);
68
- TUIChatEngine.login({
69
+ return TUIChatEngine.login({
69
70
  SDKAppID,
70
71
  userID,
71
72
  userSig,
@@ -74,7 +75,7 @@ export default class TUICustomerServer {
74
75
  Log.i(`login success. userID:${userID}`);
75
76
  this.isLoggedIn = true;
76
77
  this.loggedInUserID = userID;
77
- TUIConversationService.switchConversation(`C2C${this.currentCustomerServiceID}`);
78
+ this.switchConversation(`C2C${this.currentCustomerServiceID}`);
78
79
  switchReadStatus(state.get('showReadStatus'));
79
80
  TUIChatEngine.chat.callExperimentalAPI('isFeatureEnabledForStat', Math.pow(2, 42));
80
81
  })
@@ -88,24 +89,45 @@ export default class TUICustomerServer {
88
89
  })
89
90
  }
90
91
 
91
- public init(SDKAppID:number, userID:string, userSig:string) {
92
+ public async init(SDKAppID:number, userID:string, userSig:string) {
92
93
  Log.l(`TUICustomerServer.init vueVersion:${vueVersion} version:${version} SDKAppID:${SDKAppID} userID:${userID}` +
93
94
  ` isLoggedIn:${this.isLoggedIn} loggedInUserID:${this.loggedInUserID} currentCustomerServiceID:${this.currentCustomerServiceID}`);
95
+ if (userID) {
96
+ let ret = validateUserID(userID);
97
+ if (ret > 0) {
98
+ setTimeout(() => {
99
+ let reason = '';
100
+ if (ret === 1) {
101
+ reason = TUITranslateService.t("AIDesk.userID 只能包含可打印 ASCII 字符");
102
+ } else if (ret === 2) {
103
+ reason = TUITranslateService.t("AIDesk.userID 不能包含'administrator'");
104
+ } else if (ret === 3) {
105
+ reason = TUITranslateService.t("AIDesk.userID 长度不能超过45字节");
106
+ }
107
+ Toast({
108
+ message: reason,
109
+ type: TOAST_TYPE.ERROR,
110
+ duration: 30000,
111
+ });
112
+ }, 500);
113
+ return;
114
+ }
115
+ }
94
116
  if (this.isLoggedIn) {
95
117
  if (this.loggedInUserID === userID) {
96
- TUIConversationService.switchConversation(`C2C${this.currentCustomerServiceID}`);
118
+ await this.switchConversation(`C2C${this.currentCustomerServiceID}`);
97
119
  return;
98
120
  }
99
- this.unInit().finally(() => {
121
+ return this.unInit().finally(() => {
100
122
  this.isLoggedIn = false;
101
123
  this.loginCustomerUIKit(SDKAppID, userID, userSig);
102
124
  });
103
125
  } else {
104
- this.loginCustomerUIKit(SDKAppID, userID, userSig);
126
+ return this.loginCustomerUIKit(SDKAppID, userID, userSig);
105
127
  }
106
128
  }
107
129
 
108
- public initWithProfile(options: IInitWithProfile) {
130
+ public async initWithProfile(options: IInitWithProfile) {
109
131
  const { SDKAppID, userID, userSig, nickName, avatar, customerServiceID } = options;
110
132
  Log.l(`TUICustomerServer.initWithProfile version:${version}`);
111
133
  if (nickName) {
@@ -118,10 +140,10 @@ export default class TUICustomerServer {
118
140
  if (customerServiceID) {
119
141
  this.currentCustomerServiceID = customerServiceID;
120
142
  }
121
- this.init(SDKAppID, userID, userSig);
143
+ return this.init(SDKAppID, userID, userSig);
122
144
  }
123
145
 
124
- public unInit() {
146
+ public async unInit() {
125
147
  this.isLoggedIn = false;
126
148
  return TUIChatEngine.logout();
127
149
  }
@@ -228,9 +250,18 @@ export default class TUICustomerServer {
228
250
  }
229
251
  }
230
252
 
253
+ private async switchConversation(conversationID: string) {
254
+ const ret = await TUIConversationService.switchConversation(conversationID);
255
+ // -100002 即要切换的会话 ID 跟当前已打开的会话 ID 相同,这个时候我们重发 src 7,确保服务流状态正确
256
+ if (ret.code === -100002) {
257
+ this.activeServiceFlow(this.paramsForActiveAgain);
258
+ }
259
+ }
260
+
231
261
  // 激活会话服务流
232
262
  private activeServiceFlow(params: any) {
233
- Log.i(`activeServiceFlow params: language:${params.robotLang} country:${params.country} timezone:${params.timezone}`)
263
+ Log.i(`TUICustomerServer.activeServiceFlow params: language:${params.robotLang} country:${params.country} timezone:${params.timezone}`);
264
+ this.paramsForActiveAgain = { ...params };
234
265
  TUIChatService.sendCustomMessage({
235
266
  to: params.conversationID.slice(3),
236
267
  conversationType: TUIChatEngine.TYPES.CONV_C2C,
@@ -238,8 +269,8 @@ export default class TUICustomerServer {
238
269
  data: JSON.stringify({
239
270
  src: '7',
240
271
  customerServicePlugin: 0,
241
- triggeredContent: {
242
- language: params.robotLang,
272
+ triggeredContent: {
273
+ language: params.robotLang,
243
274
  country: params.country,
244
275
  timezone: params.timezone,
245
276
  }
@@ -247,5 +278,6 @@ export default class TUICustomerServer {
247
278
  },
248
279
  }, { onlineUserOnly: true });
249
280
  TUIStore.update(StoreName.CUSTOM, "isInSession", true);
281
+ Log.w(`TUICustomerServer.activeServiceFlow src 7 sent`);
250
282
  }
251
283
  }
package/utils/utils.ts CHANGED
@@ -288,7 +288,7 @@ export function transferToTaskFlow(conversationID: string, taskFlowID: number, d
288
288
  },{ onlineUserOnly: description ? false : true });
289
289
  }
290
290
 
291
- export function transferToHuman(conversationID: string, groupID?: string, specificMemberList?: Array<string>, description?: string) {
291
+ export function transferToHuman(conversationID: string, groupID?: number, specificMemberList?: Array<string>, description?: string) {
292
292
  if (!groupID && (!specificMemberList || specificMemberList.length === 0)) {
293
293
  Log.w(`groupID or specificMemberList is required`);
294
294
  return;
@@ -306,4 +306,19 @@ export function transferToHuman(conversationID: string, groupID?: string, specif
306
306
  })
307
307
  }
308
308
  },{ onlineUserOnly: description ? false : true });
309
+ }
310
+
311
+ export function validateUserID(userID: string) {
312
+ let ret = 0;
313
+ if (!/^[\x20-\x7E]+$/.test(userID)) {
314
+ ret = 1;
315
+ }
316
+ if (userID.toLowerCase().includes('administrator')) {
317
+ ret = 2;
318
+ }
319
+ const byteSize = new TextEncoder().encode(userID).length;
320
+ if (byteSize > 45) {
321
+ ret = 3;
322
+ }
323
+ return ret;
309
324
  }