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

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/CHANGELOG.md +9 -0
  2. package/assets/feedback_dislike_after.svg +1 -1
  3. package/assets/feedback_dislike_before.svg +2 -9
  4. package/assets/feedback_dislike_hover.svg +2 -9
  5. package/assets/feedback_like_after.svg +2 -13
  6. package/assets/feedback_like_before.svg +2 -9
  7. package/assets/feedback_like_hover.svg +2 -9
  8. package/components/CustomerServiceChat/chat-header/index-web.vue +10 -10
  9. package/components/CustomerServiceChat/feedback-modal/index.vue +391 -0
  10. package/components/CustomerServiceChat/index-web.vue +26 -0
  11. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +0 -1
  12. package/components/CustomerServiceChat/message-input/message-input-quote/index.vue +0 -3
  13. package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +4 -2
  14. package/components/CustomerServiceChat/message-list/bottom-quick-order/index.vue +0 -1
  15. package/components/CustomerServiceChat/message-list/index-web.vue +90 -61
  16. package/components/CustomerServiceChat/message-list/message-elements/feedback-button.vue +96 -251
  17. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +51 -16
  18. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-concurrency-limit.vue +0 -2
  19. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-ivr-form/form-branch.vue +0 -1
  20. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +0 -2
  21. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-mobile.vue +5 -9
  22. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-pc.vue +0 -1
  23. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-order.vue +0 -1
  24. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-product-card.vue +0 -1
  25. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-rating/message-rating-star.vue +0 -1
  26. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-stream.vue +0 -1
  27. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-transfer-with-desc.vue +0 -1
  28. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/styles/common.scss +0 -2
  29. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-plugin-layout-web.vue +16 -0
  30. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-plugin-web.vue +18 -0
  31. package/components/CustomerServiceChat/message-list/message-elements/message-quote/index-web.vue +0 -3
  32. package/components/CustomerServiceChat/message-list/message-elements/message-text.vue +0 -1
  33. package/components/CustomerServiceChat/message-list/message-elements/message-timestamp.vue +0 -1
  34. package/components/CustomerServiceChat/message-list/message-elements/read-status/index.vue +4 -2
  35. package/components/CustomerServiceChat/message-list/scroll-button/index.vue +8 -4
  36. package/components/CustomerServiceChat/message-list/style/web.scss +0 -2
  37. package/components/CustomerServiceChat/message-toolbar-button/index.vue +8 -5
  38. package/components/CustomerServiceChat/style/common.scss +4 -0
  39. package/components/CustomerServiceChat/style/web.scss +2 -1
  40. package/components/common/BottomPopup/style/h5.scss +0 -1
  41. package/components/common/Dialog/style/color.scss +0 -1
  42. package/components/common/Toast/index-web.vue +1 -1
  43. package/constant.ts +2 -0
  44. package/locales/en/aidesk.ts +18 -15
  45. package/locales/fil/aidesk.ts +17 -15
  46. package/locales/id/aidesk.ts +17 -15
  47. package/locales/ja/aidesk.ts +17 -15
  48. package/locales/ms/aidesk.ts +17 -15
  49. package/locales/ru/aidesk.ts +17 -15
  50. package/locales/th/aidesk.ts +17 -15
  51. package/locales/vi/aidesk.ts +17 -15
  52. package/locales/zh_cn/aidesk.ts +17 -14
  53. package/locales/zh_tw/aidesk.ts +17 -15
  54. package/package.json +1 -1
  55. package/server.ts +5 -2
  56. package/utils/index.ts +6 -0
  57. package/utils/utils.ts +42 -0
  58. package/assets/customer_avatar.png +0 -0
@@ -148,8 +148,10 @@ const onCurrentConversationUpdate = (conversation: IConversationModel) => {
148
148
  = currentConversation?.value?.type === TUIChatEngine.TYPES.CONV_GROUP;
149
149
  };
150
150
 
151
- const onInHumanServiceUpdate = (value: boolean) => {
152
- isInHumanService.value = value;
151
+ const onInHumanServiceUpdate = (data: {conversationID: string, value: boolean}) => {
152
+ if (data && data.conversationID === currentConversation.value.conversationID) {
153
+ isInHumanService.value = data.value;
154
+ }
153
155
  };
154
156
 
155
157
  const insertEmoji = (emojiObj: object) => {
@@ -100,7 +100,6 @@ const closeOrder = () => {
100
100
  border-radius: 8px;
101
101
  margin: 0 10px 0 10px;
102
102
  position: relative;
103
- font-family: PingFangSC-Regular;
104
103
  }
105
104
  .bottom-quick-order-container-pc {
106
105
  width: 300px;
@@ -49,6 +49,10 @@
49
49
  @handleH5LongPress="handleH5LongPress"
50
50
  @heightChanged="onHeightChanged"
51
51
  @messageSent="onMessageSent"
52
+ :enableFeedback="props.enableFeedback"
53
+ :enableAINote="props.enableAINote"
54
+ @like="onLike"
55
+ @dislike="onDislike"
52
56
  />
53
57
  <div
54
58
  v-else
@@ -66,8 +70,12 @@
66
70
  :isAudioPlayed="Boolean(audioPlayedMapping[item.ID])"
67
71
  :blinkMessageIDList="blinkMessageIDList"
68
72
  :messageItem="JSON.parse(JSON.stringify(item))"
73
+ :enableFeedback="props.enableFeedback"
74
+ :enableAINote="props.enableAINote"
69
75
  @blinkMessage="blinkMessage"
70
76
  @resendMessage="resendMessage(item)"
77
+ @like="onLike"
78
+ @dislike="onDislike"
71
79
  >
72
80
  <template #messageElement>
73
81
  <MessageThinking v-if="isThinkingMessage(item)"/>
@@ -151,6 +159,7 @@
151
159
  <ScrollButton
152
160
  ref="scrollButtonInstanceRef"
153
161
  @scrollToLatestMessage="scrollToLatestMessage"
162
+ @scrollNearToBottom="scrollNearToBottom"
154
163
  />
155
164
  <Dialog
156
165
  v-if="reSendDialogShow"
@@ -226,11 +235,13 @@ import {
226
235
  isEnabledMessageReadReceiptGlobal,
227
236
  deepCopy,
228
237
  isNonEmptyObject,
238
+ updateCustomStore,
229
239
  } from '../../../utils/utils';
230
240
  import { isMessageInvisible, isThinkingMessage, isThinkingMessageOverTime, JSONToObject, isTransferMessageWithoutDesc } from '../../../utils/index';
231
241
  import { isCustomerConversation } from '../../../index';
232
242
  import { CUSTOM_MESSAGE_SRC } from '../../../constant';
233
243
  import { QuickOrderModel } from '../../../interface';
244
+ import Log from '../../../utils/logger';
234
245
 
235
246
  interface ScrollConfig {
236
247
  scrollToMessage?: IMessageModel;
@@ -244,15 +255,21 @@ interface ScrollConfig {
244
255
  interface IProps {
245
256
  bottomQuickOrder?: QuickOrderModel;
246
257
  showBottomQuickOrder: boolean;
258
+ enableFeedback: number;
259
+ enableAINote: number;
247
260
  }
248
261
  const props = withDefaults(defineProps<IProps>(), {
249
262
  showBottomQuickOrder: false,
263
+ enableFeedback: 0,
264
+ enableAINote: 1,
250
265
  });
251
266
 
252
267
  interface IEmits {
253
268
  (key: 'closeInputToolBar'): void;
254
269
  (key: 'handleEditor', message: IMessageModel, type: string): void;
255
270
  (key: 'closeBottomQuickOrder'): void;
271
+ (key: 'like', messageInfo: Object): void;
272
+ (key: 'dislike', messageInfo: Object): void;
256
273
  }
257
274
 
258
275
  const emits = defineEmits<IEmits>();
@@ -354,29 +371,32 @@ function onNewMessageList(list: IMessageModel[]) {
354
371
  list.forEach((message:IMessageModel) => {
355
372
  if (message.type === TUIChatEngine.TYPES.MSG_CUSTOM) {
356
373
  const data = JSONToObject(message.payload.data);
374
+ const conversationID = message.conversationID;
357
375
  if (data) {
358
376
  if (data.src === CUSTOM_MESSAGE_SRC.BOT_STATUS) {
359
377
  if (data.content.content === 'inBot') {
360
- TUIStore.update(StoreName.CUSTOM, "isInHumanService", false);
378
+ updateCustomStore("isInHumanService", { conversationID, value: false });
361
379
  }
362
380
  } else if (data.src === CUSTOM_MESSAGE_SRC.SEAT_STATUS) {
363
381
  if (data.content.command === "updateSeatStatus") {
364
382
  if (data.content.content === 'inSeat') {
365
- TUIStore.update(StoreName.CUSTOM, "isInHumanService", true);
383
+ updateCustomStore("isInHumanService", { conversationID, value: true });
366
384
  } else if (data.content.content === 'outSeat') {
367
- TUIStore.update(StoreName.CUSTOM, "isInHumanService", false);
385
+ updateCustomStore("isInHumanService", { conversationID, value: false });
368
386
  }
369
387
  }
370
388
  } else if (data.src === CUSTOM_MESSAGE_SRC.TYPING_STATE) {
371
389
  if (data.typingStatus === 1) {
372
- TUIStore.update(StoreName.CUSTOM, 'isTyping', true);
390
+ updateCustomStore("isTyping", { conversationID, value: true });
373
391
  } else {
374
- TUIStore.update(StoreName.CUSTOM, 'isTyping', false);
392
+ updateCustomStore("isTyping", { conversationID, value: false });
375
393
  }
376
394
  } else if (data.src === CUSTOM_MESSAGE_SRC.NO_SEAT_ONLINE || data.src === CUSTOM_MESSAGE_SRC.TIMEOUT || data.src === CUSTOM_MESSAGE_SRC.END) {
377
- TUIStore.update(StoreName.CUSTOM, "isInSession", false);
395
+ updateCustomStore("isInSession", { conversationID, value: false });
378
396
  } else if (data.src === CUSTOM_MESSAGE_SRC.SESSION_RESTARTED) {
379
- TUIStore.update(StoreName.CUSTOM, "isInSession", true);
397
+ updateCustomStore("isInSession", { conversationID, value: true });
398
+ } else if (data.src === CUSTOM_MESSAGE_SRC.GET_FEEDBACK_MENU) {
399
+ TUIStore.update(StoreName.CUSTOM, "feedbackTags", data.content.menu);
380
400
  }
381
401
  }
382
402
  }
@@ -385,6 +405,7 @@ function onNewMessageList(list: IMessageModel[]) {
385
405
 
386
406
  async function onMessageListUpdated(list: IMessageModel[]) {
387
407
  if (!isCustomerConversation(currentConversationID.value)) {
408
+ Log.w(`Messages filtered as they are not customer service messages. currentConversationID: ${currentConversationID.value}`);
388
409
  return;
389
410
  }
390
411
  observer?.disconnect();
@@ -441,9 +462,7 @@ async function onMessageListUpdated(list: IMessageModel[]) {
441
462
  await scrollToPosition({ scrollToBottom: true });
442
463
  }
443
464
  currentLastMessage.value = Object.assign({}, newLastMessage);
444
- if (isEnabledMessageReadReceiptGlobal()) {
445
- nextTick(() => bindIntersectionObserver());
446
- }
465
+ nextTick(() => bindIntersectionObserver());
447
466
  }
448
467
 
449
468
  function isCurrentListInBottomPosition() {
@@ -682,6 +701,13 @@ async function scrollToLatestMessage() {
682
701
  }
683
702
  scrollButtonInstanceRef.value?.hideScrollButton();
684
703
  beforeHistoryGetScrollHeight.value = 0;
704
+ // 滚动到底部的时候,及时检查是否要上报已读回执
705
+ bindIntersectionObserver();
706
+ }
707
+
708
+ function scrollNearToBottom() {
709
+ // 滚动到底部的时候,及时检查是否要上报已读回执
710
+ bindIntersectionObserver();
685
711
  }
686
712
 
687
713
  const handelScrollListScroll = throttle(
@@ -693,67 +719,62 @@ const handelScrollListScroll = throttle(
693
719
  );
694
720
 
695
721
  async function bindIntersectionObserver() {
696
- if (
697
- !messageList.value
698
- || !messageListRef.value
699
- || messageList.value.length === 0
700
- ) {
722
+ if (!messageList.value || !messageListRef.value || messageList.value.length === 0) {
701
723
  return;
702
724
  }
703
725
 
704
- const mappingFromIDToMessage: Record<
705
- string,
706
- {
707
- msgDom: HTMLElement;
708
- msgModel: IMessageModel | undefined;
709
- }
710
- > = {};
726
+ if (!isEnabledMessageReadReceiptGlobal()) {
727
+ return;
728
+ }
711
729
 
712
- observer?.disconnect();
713
- observer = new IntersectionObserver(
714
- (entries) => {
715
- entries.forEach((entry) => {
716
- const { isIntersecting, target } = entry;
717
- if (isIntersecting) {
718
- const { msgDom, msgModel } = mappingFromIDToMessage[target.id];
719
- if (
720
- msgModel
721
- && !msgModel.readReceiptInfo?.isPeerRead
722
- && !sentReceiptMessageIDSet.has(msgModel.ID)
723
- ) {
730
+ const mappingFromIDToMessage: Record<string, { msgDom: HTMLElement; msgModel: IMessageModel | undefined; }> = {};
731
+
732
+ if (observer) {
733
+ observer.disconnect();
734
+ }
735
+ observer = new IntersectionObserver((entries) => {
736
+ entries.forEach((entry) => {
737
+ const { isIntersecting, target } = entry;
738
+ if (isIntersecting) {
739
+ const { msgDom, msgModel } = mappingFromIDToMessage[target.id];
740
+ if (msgModel) {
741
+ const { readReceiptInfo, ID, payload } = msgModel;
742
+ if (!readReceiptInfo.isPeerRead && !sentReceiptMessageIDSet.has(ID)) {
724
743
  TUIChatService.sendMessageReadReceipt([msgModel]);
725
- sentReceiptMessageIDSet.add(msgModel.ID);
726
- observer?.unobserve(msgDom);
744
+ sentReceiptMessageIDSet.add(ID);
745
+ Log.l(`Receipt sent. ID:${ID} payload:${JSON.stringify(payload)}`);
746
+ if (observer) {
747
+ observer.unobserve(msgDom);
748
+ }
727
749
  }
728
750
  }
729
- });
730
- },
731
- {
732
- root: messageListRef.value,
733
- threshold: 0.7,
734
- },
735
- );
751
+ }
752
+ });
753
+ }, {
754
+ root: messageListRef.value,
755
+ threshold: 0.3, // 超过1/4可见时触发,提高灵敏度
756
+ });
736
757
 
737
- const arrayOfMessageLi
738
- = messageListRef.value?.querySelectorAll('.message-li');
758
+ const arrayOfMessageLi = messageListRef.value?.querySelectorAll('.message-li');
739
759
  if (arrayOfMessageLi) {
740
- for (let i = 0; i < arrayOfMessageLi?.length; ++i) {
760
+ for (let i = 0, length = arrayOfMessageLi.length; i < length; i++) {
741
761
  const messageElement = arrayOfMessageLi[i] as HTMLElement;
742
- const matchingMessage = messageList.value.find(
743
- (message: IMessageModel) => {
744
- return messageElement.id.slice(4) === message.ID;
745
- },
746
- );
747
- if (
748
- matchingMessage
749
- && matchingMessage.needReadReceipt
750
- && matchingMessage.flow === 'in'
751
- ) {
752
- mappingFromIDToMessage[messageElement.id] = {
753
- msgDom: messageElement,
754
- msgModel: matchingMessage,
755
- };
756
- observer?.observe(messageElement);
762
+ const matchingMessage = messageList.value.find((message: IMessageModel) => {
763
+ // replace tui-
764
+ return messageElement.id.slice(4) === message.ID;
765
+ });
766
+ if (matchingMessage) {
767
+ const { needReadReceipt, readReceiptInfo, flow } = matchingMessage;
768
+ // 收到的消息需要已读回执,但我尚未发送
769
+ if (needReadReceipt && flow === 'in' && !readReceiptInfo.isPeerRead) {
770
+ mappingFromIDToMessage[messageElement.id] = {
771
+ msgDom: messageElement,
772
+ msgModel: matchingMessage,
773
+ };
774
+ if (observer) {
775
+ observer.observe(messageElement);
776
+ }
777
+ }
757
778
  }
758
779
  }
759
780
  }
@@ -794,6 +815,14 @@ function sendBottomQuickOrder() {
794
815
  closeBottomQuickOrder();
795
816
  }
796
817
 
818
+ function onLike(messageInfo: Object) {
819
+ emits('like', messageInfo);
820
+ };
821
+
822
+ function onDislike(messageInfo: Object) {
823
+ emits('dislike', messageInfo);
824
+ };
825
+
797
826
  defineExpose({
798
827
  scrollToLatestMessage,
799
828
  });