@tencentcloud/ai-desk-customer-vue 1.3.0 → 1.4.0

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 (56) hide show
  1. package/CHANGELOG.md +11 -4
  2. package/assets/customer_avatar.png +0 -0
  3. package/assets/face.svg +10 -0
  4. package/assets/files.svg +5 -0
  5. package/assets/image.svg +8 -0
  6. package/assets/rating_tool_icon.svg +5 -0
  7. package/assets/rating_tool_icon_h5.svg +1 -0
  8. package/assets/video.svg +8 -0
  9. package/assets/video_h5.svg +1 -0
  10. package/components/CustomerServiceChat/chat-header/index-web.vue +16 -14
  11. package/components/CustomerServiceChat/index-web.vue +69 -13
  12. package/components/CustomerServiceChat/message-input/index-web.vue +31 -5
  13. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +24 -0
  14. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/emoji-picker-dialog.vue +1 -1
  15. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/index.vue +1 -1
  16. package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +6 -8
  17. package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +11 -16
  18. package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +61 -18
  19. package/components/CustomerServiceChat/message-input-toolbar/rating-tool/index.vue +72 -0
  20. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/h5.scss +10 -1
  21. package/components/CustomerServiceChat/message-input-toolbar/user-define-input-tool.vue +80 -0
  22. package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +9 -14
  23. package/components/CustomerServiceChat/message-list/index-web.vue +33 -6
  24. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +53 -6
  25. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-branch.vue +5 -5
  26. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +8 -3
  27. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +20 -6
  28. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +2 -2
  29. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-order.vue +1 -2
  30. package/components/CustomerServiceChat/message-list/message-elements/message-file.vue +1 -1
  31. package/components/CustomerServiceChat/message-list/scroll-button/index.vue +3 -3
  32. package/components/CustomerServiceChat/message-list/style/web.scss +2 -1
  33. package/components/CustomerServiceChat/message-toolbar-button/index.vue +111 -42
  34. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-end-human-service.vue +59 -0
  35. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-human-service.vue +55 -0
  36. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-service-rating.vue +59 -0
  37. package/constant.ts +15 -0
  38. package/interface.ts +35 -5
  39. package/locales/en/aidesk.ts +4 -3
  40. package/locales/fil/aidesk.ts +2 -1
  41. package/locales/id/aidesk.ts +3 -2
  42. package/locales/ja/aidesk.ts +1 -0
  43. package/locales/ms/aidesk.ts +1 -0
  44. package/locales/ru/aidesk.ts +1 -0
  45. package/locales/th/aidesk.ts +1 -0
  46. package/locales/vi/aidesk.ts +1 -0
  47. package/locales/zh_cn/aidesk.ts +1 -0
  48. package/locales/zh_tw/aidesk.ts +1 -0
  49. package/package.json +1 -1
  50. package/server.ts +5 -1
  51. package/utils/state.js +30 -0
  52. package/utils/utils.ts +48 -1
  53. package/assets/face.png +0 -0
  54. package/assets/files.png +0 -0
  55. package/assets/image.png +0 -0
  56. package/assets/video.png +0 -0
@@ -11,20 +11,48 @@
11
11
  !isPC && 'message-input-toolbar-h5-list',
12
12
  ]"
13
13
  >
14
- <EmojiPicker
15
- ref="emojiPickerRef"
16
- @insertEmoji="insertEmoji"
17
- @dialogShowInH5="dialogShowInH5"
18
- @dialogCloseInH5="dialogCloseInH5"
19
- @changeToolbarDisplayType="
20
- (type) => emits('changeToolbarDisplayType', type)
21
- "
22
- :isH5EmojiShow="isH5EmojiShow"
23
- @sendMessage="sendMessage"
24
- />
25
- <ImageUpload imageSourceType="album" :isH5ToolShow="isH5ToolShow"/>
26
- <FileUpload :isH5ToolShow="isH5ToolShow"/>
27
- <!-- <VideoUpload videoSourceType="album" /> -->
14
+ <template v-if="props.inputToolbarList === undefined">
15
+ <EmojiPicker
16
+ v-if="isPC"
17
+ ref="emojiPickerRef"
18
+ @insertEmoji="insertEmoji"
19
+ @dialogShowInH5="dialogShowInH5"
20
+ @dialogCloseInH5="dialogCloseInH5"
21
+ @changeToolbarDisplayType="
22
+ (type) => emits('changeToolbarDisplayType', type)
23
+ "
24
+ :isH5EmojiShow="isH5EmojiShow"
25
+ @sendMessage="sendMessage"
26
+ />
27
+ <ImageUpload imageSourceType="album" :isH5ToolShow="isH5ToolShow"/>
28
+ <FileUpload :isH5ToolShow="isH5ToolShow"/>
29
+ </template>
30
+ <template v-else v-for="(item, index) in props.inputToolbarList" :key="index">
31
+ <EmojiPicker
32
+ v-if="isPC && item.presetId === INPUT_TOOLBAR_TYPE.EMOJI && item.isEnabled === 1"
33
+ ref="emojiPickerRef"
34
+ @insertEmoji="insertEmoji"
35
+ @dialogShowInH5="dialogShowInH5"
36
+ @dialogCloseInH5="dialogCloseInH5"
37
+ @changeToolbarDisplayType="
38
+ (type) => emits('changeToolbarDisplayType', type)
39
+ "
40
+ :isH5EmojiShow="isH5EmojiShow"
41
+ @sendMessage="sendMessage"
42
+ />
43
+ <ImageUpload v-else-if="item.presetId === INPUT_TOOLBAR_TYPE.IMAGE && item.isEnabled === 1" imageSourceType="album" :isH5ToolShow="isH5ToolShow"/>
44
+ <FileUpload v-else-if="item.presetId === INPUT_TOOLBAR_TYPE.FILE && item.isEnabled === 1" :isH5ToolShow="isH5ToolShow"/>
45
+ <VideoUpload v-else-if="item.presetId === INPUT_TOOLBAR_TYPE.VIDEO && item.isEnabled === 1" videoSourceType="album" :isH5ToolShow="isH5ToolShow"/>
46
+ <RatingTool v-else-if="item.presetId === INPUT_TOOLBAR_TYPE.RATING && item.isEnabled === 1 && isInHumanService" :isH5ToolShow="isH5ToolShow"/>
47
+ <UserDefineTool
48
+ v-else-if="item.isEnabled === 1 && item.isPreset === 0 && item.presetId !== INPUT_TOOLBAR_TYPE.EMOJI"
49
+ :isH5ToolShow="isH5ToolShow"
50
+ :title="item.title"
51
+ :icon="item.icon"
52
+ :type="item.type"
53
+ :content="item.content"
54
+ />
55
+ </template>
28
56
  </div>
29
57
  <div
30
58
  v-if="isH5"
@@ -44,14 +72,18 @@ import EmojiPicker from './emoji-picker/index.vue';
44
72
  import ImageUpload from './image-upload/index.vue';
45
73
  import FileUpload from './file-upload/index.vue';
46
74
  import VideoUpload from './video-upload/index.vue';
75
+ import RatingTool from './rating-tool/index.vue';
47
76
  import { isPC, isH5 } from '../../../utils/env';
48
- import { ToolbarDisplayType } from '../../../interface';
77
+ import { ToolbarDisplayType, InputToolbarModel } from '../../../interface';
78
+ import UserDefineTool from './user-define-input-tool.vue'
79
+ import { INPUT_TOOLBAR_TYPE } from '../../../constant';
49
80
  const { ref, onMounted, onUnmounted, watch } = vue;
50
81
 
51
82
  interface IProps {
52
83
  displayType: ToolbarDisplayType;
53
- isH5EmojiShow?:boolean;
54
- isH5ToolShow?:boolean;
84
+ isH5EmojiShow?: boolean;
85
+ isH5ToolShow?: boolean;
86
+ inputToolbarList?: InputToolbarModel[];
55
87
  }
56
88
  interface IEmits {
57
89
  (e: 'scrollToLatestMessage'): void;
@@ -70,17 +102,24 @@ const h5Dialog = ref<HTMLElement>();
70
102
  const currentConversation = ref<IConversationModel>();
71
103
  const isGroup = ref<boolean>(false);
72
104
  const emojiPickerRef = ref<InstanceType<typeof EmojiPicker>>();
105
+ const isInHumanService = ref(false);
73
106
 
74
107
  onMounted(() => {
75
108
  TUIStore.watch(StoreName.CONV, {
76
109
  currentConversation: onCurrentConversationUpdate,
77
110
  });
111
+ TUIStore.watch(StoreName.CUSTOM, {
112
+ isInHumanService: onInHumanServiceUpdate,
113
+ });
78
114
  });
79
115
 
80
116
  onUnmounted(() => {
81
117
  TUIStore.unwatch(StoreName.CONV, {
82
118
  currentConversation: onCurrentConversationUpdate,
83
119
  });
120
+ TUIStore.unwatch(StoreName.CUSTOM, {
121
+ isInHumanService: onInHumanServiceUpdate,
122
+ });
84
123
  });
85
124
 
86
125
  watch(
@@ -99,7 +138,7 @@ watch(
99
138
  (newValue) => {
100
139
  if (newValue === false) {
101
140
  emojiPickerRef.value?.closeEmojiPicker();
102
- }
141
+ }
103
142
  },
104
143
  );
105
144
 
@@ -109,6 +148,10 @@ const onCurrentConversationUpdate = (conversation: IConversationModel) => {
109
148
  = currentConversation?.value?.type === TUIChatEngine.TYPES.CONV_GROUP;
110
149
  };
111
150
 
151
+ const onInHumanServiceUpdate = (value: boolean) => {
152
+ isInHumanService.value = value;
153
+ };
154
+
112
155
  const insertEmoji = (emojiObj: object) => {
113
156
  emits('insertEmoji', emojiObj);
114
157
  };
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <ToolbarItemContainer
3
+ :iconFile="getIcon()"
4
+ :title="props.title"
5
+ :iconWidth="isPC ? '20px' : '25px'"
6
+ :iconHeight="isPC ? '20px' : '25px'"
7
+ :needDialog="false"
8
+ @onIconClick="onIconClick"
9
+ :isH5ToolShow="isH5ToolShow"
10
+ />
11
+ </template>
12
+ <script lang="ts" setup>
13
+ import {
14
+ TUIChatService,
15
+ TUIStore,
16
+ StoreName,
17
+ IConversationModel,
18
+ TUITranslateService,
19
+ } from '@tencentcloud/chat-uikit-engine';
20
+ import vue from '../../../../adapter-vue';
21
+ import { isPC, isH5 } from '../../../../utils/env';
22
+ import ToolbarItemContainer from '../toolbar-item-container/index.vue';
23
+ import ratingToolIcon from '../../../../assets/rating_tool_icon.svg'
24
+ import ratingToolIconH5 from '../../../../assets/rating_tool_icon_h5.svg';
25
+ import { getTo } from '../../../../utils/utils';
26
+ import { CUSTOM_MESSAGE_SRC } from '../../../../constant';
27
+ const { ref, onMounted, onUnmounted } = vue;
28
+
29
+ const props = defineProps({
30
+ isH5ToolShow:{
31
+ type: Boolean,
32
+ default: false,
33
+ },
34
+ title: {
35
+ type: String,
36
+ default: TUITranslateService.t('AIDesk.提交评价')
37
+ },
38
+ icon: {
39
+ type: String,
40
+ default: '',
41
+ }
42
+ });
43
+
44
+ const currentConversation = ref<IConversationModel>();
45
+
46
+ TUIStore.watch(StoreName.CONV, {
47
+ currentConversation: (conversation: IConversationModel) => {
48
+ currentConversation.value = conversation;
49
+ },
50
+ });
51
+
52
+ const getIcon = (): string => {
53
+ return isH5 ? ratingToolIconH5 : ratingToolIcon;
54
+ }
55
+
56
+ const onIconClick = () => {
57
+ TUIChatService.sendCustomMessage({
58
+ to: getTo(currentConversation?.value),
59
+ conversationType: currentConversation?.value?.type,
60
+ payload:{
61
+ data:JSON.stringify({
62
+ src: CUSTOM_MESSAGE_SRC.USER_SATISFACTION,
63
+ customerServicePlugin: 0,
64
+ }),
65
+ },
66
+ },{ onlineUserOnly: true })
67
+ };
68
+ </script>
69
+
70
+ <style lang="scss" scoped>
71
+ @import "../../style/common";
72
+ </style>
@@ -6,11 +6,20 @@
6
6
  margin-right:15px;
7
7
  &-title {
8
8
  margin-top:5px;
9
+ font-size: 14px;
10
+ white-space: nowrap;
11
+ max-width: 85px;
12
+ overflow: hidden;
13
+ text-overflow: ellipsis;
9
14
  }
10
15
  &-icon{
11
16
  background-color: #fff;
12
- padding:20px;
17
+ width: 40px;
13
18
  border-radius: 10px;
19
+ height: 40px;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
14
23
  }
15
24
  &-dialog {
16
25
  position: static !important;
@@ -0,0 +1,80 @@
1
+ <template>
2
+ <ToolbarItemContainer
3
+ :iconFile="getIcon()"
4
+ :title="props.title"
5
+ :iconWidth="isPC ? '20px' : '25px'"
6
+ :iconHeight="isPC ? '20px' : '25px'"
7
+ :needDialog="false"
8
+ @onIconClick="onIconClick"
9
+ :isH5ToolShow="isH5ToolShow">
10
+ <div :class="['image-upload', !isPC && 'image-upload-h5', 'image-video']">
11
+ </div>
12
+ </ToolbarItemContainer>
13
+ </template>
14
+ <script lang="ts" setup>
15
+ import {
16
+ TUIChatService,
17
+ TUIStore,
18
+ StoreName,
19
+ IConversationModel,
20
+ } from '@tencentcloud/chat-uikit-engine';
21
+ import vue from '../../../adapter-vue';
22
+ import { isPC } from '../../../utils/env';
23
+ import ToolbarItemContainer from './toolbar-item-container/index.vue';
24
+ import { isEnabledMessageReadReceiptGlobal, openSafeUrl, getTo } from '../../../utils/utils';
25
+ const { ref } = vue;
26
+
27
+ const props = defineProps({
28
+ isH5ToolShow: {
29
+ type: Boolean,
30
+ default: false,
31
+ },
32
+ title: {
33
+ type: String,
34
+ default: ''
35
+ },
36
+ icon: {
37
+ type: String,
38
+ default: '',
39
+ },
40
+ type: {
41
+ type: Number,
42
+ default: 1
43
+ },
44
+ content: {
45
+ type: String,
46
+ default: ''
47
+ }
48
+ });
49
+
50
+ const currentConversation = ref<IConversationModel>();
51
+
52
+ TUIStore.watch(StoreName.CONV, {
53
+ currentConversation: (conversation: IConversationModel) => {
54
+ currentConversation.value = conversation;
55
+ },
56
+ });
57
+
58
+ const getIcon = (): string => {
59
+ return props.icon;
60
+ }
61
+
62
+ const onIconClick = () => {
63
+ if (props.type === 1 && props.content) {
64
+ TUIChatService.sendTextMessage({
65
+ to: getTo(currentConversation?.value),
66
+ conversationType: currentConversation?.value?.type,
67
+ payload: {
68
+ text: props.content
69
+ },
70
+ needReadReceipt: isEnabledMessageReadReceiptGlobal(),
71
+ });
72
+ } else if (props.type === 2 && props.content) {
73
+ openSafeUrl(props.content);
74
+ }
75
+ };
76
+ </script>
77
+
78
+ <style lang="scss" scoped>
79
+ @import "../style/common";
80
+ </style>
@@ -3,8 +3,8 @@
3
3
  :iconFile="handleIcon()"
4
4
  :title="handleTitle()"
5
5
  :needDialog="false"
6
- iconWidth='21px'
7
- iconHeight='18px'
6
+ iconWidth='20px'
7
+ iconHeight='20px'
8
8
  @onIconClick="onIconClick"
9
9
  >
10
10
  <div :class="['video-upload', !isPC && 'video-upload-h5']">
@@ -32,8 +32,9 @@ import {
32
32
  import vue from '../../../../adapter-vue';
33
33
  import { isPC } from '../../../../utils/env';
34
34
  import ToolbarItemContainer from '../toolbar-item-container/index.vue';
35
- import videoIcon from '../../../../assets/video.png';
36
- import { isEnabledMessageReadReceiptGlobal } from '../../../../utils/utils';
35
+ import videoIcon from '../../../../assets/video.svg';
36
+ import videoIconH5 from '../../../../assets/video_h5.svg'
37
+ import { isEnabledMessageReadReceiptGlobal, getTo } from '../../../../utils/utils';
37
38
  const { ref } = vue;
38
39
 
39
40
  const props = defineProps({
@@ -56,19 +57,15 @@ TUIStore.watch(StoreName.CONV, {
56
57
  });
57
58
 
58
59
  const handleIcon = (): string => {
59
- return videoIcon;
60
-
60
+ return isPC ? videoIcon : videoIconH5;
61
61
  };
62
62
 
63
63
  const handleTitle = (): string => {
64
-
65
- return '视频';
66
-
64
+ return TUITranslateService.t('视频');
67
65
  };
68
66
 
69
67
  const onIconClick = () => {
70
- inputRef?.value?.click && inputRef?.value?.click();
71
-
68
+ inputRef?.value?.click && inputRef?.value?.click();
72
69
  };
73
70
 
74
71
  const sendVideoInWeb = (e: any) => {
@@ -84,9 +81,7 @@ const sendVideoMessage = (file: any) => {
84
81
  return;
85
82
  }
86
83
  const options = {
87
- to:
88
- currentConversation?.value?.groupProfile?.groupID
89
- || currentConversation?.value?.userProfile?.userID,
84
+ to: getTo(currentConversation?.value),
90
85
  conversationType: currentConversation?.value?.type,
91
86
  payload: {
92
87
  file,
@@ -217,8 +217,9 @@ import {
217
217
  isEnabledMessageReadReceiptGlobal,
218
218
  deepCopy,
219
219
  } from '../../../utils/utils';
220
- import { isMessageInvisible, isThinkingMessage, isThinkingMessageOverTime } from '../../../utils/index';
220
+ import { isMessageInvisible, isThinkingMessage, isThinkingMessageOverTime, JSONToObject } from '../../../utils/index';
221
221
  import { isCustomerConversation } from '../../../index';
222
+ import { CUSTOM_MESSAGE_SRC } from '../../../constant';
222
223
 
223
224
  interface ScrollConfig {
224
225
  scrollToMessage?: IMessageModel;
@@ -290,9 +291,9 @@ onMounted(() => {
290
291
  messageList: onMessageListUpdated,
291
292
  messageSource: onMessageSourceUpdated,
292
293
  isCompleted: isCompletedUpdated,
294
+ newMessageList: onNewMessageList,
293
295
  });
294
296
 
295
-
296
297
  TUIStore.watch(StoreName.CUSTOM, {
297
298
  isShowMessagePopMenu: isShowMessagePopMenuUpdated,
298
299
  });
@@ -329,6 +330,29 @@ onUnmounted(() => {
329
330
  observer = null;
330
331
  });
331
332
 
333
+ function onNewMessageList(list: IMessageModel[]) {
334
+ list.forEach((message:IMessageModel) => {
335
+ if (message?.type === TUIChatEngine.TYPES.MSG_CUSTOM) {
336
+ const data = JSONToObject(message?.payload?.data);
337
+ if (data?.src === CUSTOM_MESSAGE_SRC.SEAT_STATUS) {
338
+ if (data?.content.command === "updateSeatStatus") {
339
+ if (data.content.content === 'inSeat') {
340
+ TUIStore.update(StoreName.CUSTOM, "isInHumanService", true);
341
+ } else if (data.content.content === 'outSeat') {
342
+ TUIStore.update(StoreName.CUSTOM, "isInHumanService", false);
343
+ }
344
+ }
345
+ } else if (data?.src === CUSTOM_MESSAGE_SRC.TYPING_STATE) {
346
+ if (data?.typingStatus === 1) {
347
+ TUIStore.update(StoreName.CUSTOM, 'isTyping', true);
348
+ } else {
349
+ TUIStore.update(StoreName.CUSTOM, 'isTyping', false);
350
+ }
351
+ }
352
+ }
353
+ });
354
+ }
355
+
332
356
  async function onMessageListUpdated(list: IMessageModel[]) {
333
357
  if (!isCustomerConversation(currentConversationID.value)) {
334
358
  return;
@@ -370,7 +394,6 @@ async function onMessageListUpdated(list: IMessageModel[]) {
370
394
  await scrollToPosition({
371
395
  scrollToOffset: { bottom: beforeHistoryGetScrollHeight.value },
372
396
  });
373
- beforeHistoryGetScrollHeight.value = 0;
374
397
  } else if (
375
398
  scrollButtonInstanceRef.value?.isScrollButtonVisible
376
399
  && newLastMessage?.flow === 'in'
@@ -425,8 +448,7 @@ async function scrollToPosition(config: ScrollConfig = {}): Promise<void> {
425
448
  if (config.scrollToOffset?.top) {
426
449
  container!.scrollTop = config.scrollToOffset.top;
427
450
  } else if (config.scrollToOffset?.bottom) {
428
- container!.scrollTop
429
- = container!.scrollHeight - config.scrollToOffset.bottom;
451
+ container!.scrollTop = container!.scrollHeight - config.scrollToOffset.bottom;
430
452
  }
431
453
  }
432
454
  resolve();
@@ -574,6 +596,11 @@ const handleH5LongPress = (e: any, message: IMessageModel, type: string) => {
574
596
  };
575
597
 
576
598
  function onHeightChanged() {
599
+ // 如果 view more 导致了子组件派发了 heightChanged 事件,不滚动到最底部,保留原来的滚动位置
600
+ if (beforeHistoryGetScrollHeight.value > 0) {
601
+ return;
602
+ }
603
+
577
604
  scrollToLatestMessage();
578
605
  }
579
606
 
@@ -617,6 +644,7 @@ async function scrollToLatestMessage() {
617
644
  messageListRef.value.scrollTop = scrollHeight - height;
618
645
  }
619
646
  scrollButtonInstanceRef.value?.hideScrollButton();
647
+ beforeHistoryGetScrollHeight.value = 0;
620
648
  }
621
649
 
622
650
  const handelScrollListScroll = throttle(
@@ -709,7 +737,6 @@ function setAudioPlayed(messageID: string) {
709
737
  };
710
738
  }
711
739
 
712
-
713
740
  defineExpose({
714
741
  scrollToLatestMessage,
715
742
  });
@@ -12,21 +12,19 @@
12
12
  :class="[message.flow === 'in' ? '' : 'reverse']"
13
13
  >
14
14
  <Avatar
15
- v-if="isPC"
15
+ v-if="isPC && showAvatar === 1"
16
16
  useSkeletonAnimation
17
- :url="message.flow=== 'in'? (message.avatar || customerAvatar) : (message.avatar || '')"
17
+ :url="avatarUrl"
18
18
  />
19
19
  <main
20
20
  :class="['message-body',message.flow==='out' && 'message-body-reverse']"
21
21
  @click.stop
22
22
  >
23
23
  <div
24
- v-if="
25
- isPC
26
- "
24
+ v-if="isPC && showNickName === 1"
27
25
  class="message-body-nick-name"
28
26
  >
29
- {{ props.content.showName }}
27
+ {{ nickName }}
30
28
  </div>
31
29
  <div
32
30
  :class="[
@@ -142,6 +140,8 @@ import loadingIcon from '../../../../assets/loading.png';
142
140
  import customerAvatar from '../../../../assets/customer_avatar.png';
143
141
  import { shallowCopyMessage } from '../../../../utils/utils';
144
142
  import { isPC,isH5 } from '../../../../utils/env';
143
+ import { JSONToObject } from '../../../../utils/index';
144
+ import state from '../../../../utils/state.js';
145
145
  import { CUSTOM_MESSAGE_SRC } from '../../../../constant';
146
146
  const { computed, toRefs } = vue;
147
147
 
@@ -182,6 +182,16 @@ const needLoadingIconMessageType = [
182
182
  ];
183
183
 
184
184
  const { blinkMessageIDList, messageItem: message } = toRefs(props);
185
+ const {
186
+ showAvatar,
187
+ showNickName,
188
+ robotAvatar,
189
+ staffAvatar,
190
+ userAvatar,
191
+ robotNickName,
192
+ staffNickName,
193
+ userNickName,
194
+ } = state.get('avatarNickName');
185
195
 
186
196
  const isDisplayUnplayMark = computed<boolean>(() => {
187
197
  return (
@@ -213,6 +223,43 @@ const isMultiBranchMsg = computed(()=>{
213
223
  return false;
214
224
  });
215
225
 
226
+ function isFromRobot(cloudCustomData: string) {
227
+ try {
228
+ const jsonObj = JSONToObject(cloudCustomData);
229
+ return jsonObj.hasOwnProperty("role") && jsonObj.role === "robot";
230
+ } catch (e) {
231
+ return false;
232
+ }
233
+ }
234
+
235
+ const avatarUrl = computed(() => {
236
+ let url = '';
237
+ if (message.value?.flow === 'in') {
238
+ if (isFromRobot(message.value?.cloudCustomData)) {
239
+ url = robotAvatar || customerAvatar;
240
+ } else {
241
+ url = staffAvatar || message.value?.avatar;
242
+ }
243
+ } else {
244
+ url = userAvatar || message.value?.avatar || '';
245
+ }
246
+ return url;
247
+ });
248
+
249
+ const nickName = computed(() => {
250
+ let nick = '';
251
+ if (message.value?.flow === 'in') {
252
+ if (isFromRobot(message.value?.cloudCustomData)) {
253
+ nick = robotNickName || props.content.showName;
254
+ } else {
255
+ nick = staffNickName || props.content.showName;
256
+ }
257
+ } else {
258
+ nick = userNickName || props.content.showName;
259
+ }
260
+ return nick;
261
+ });
262
+
216
263
  const isProductCardOrOrderMessage = computed(() => {
217
264
  if (message.value?.type == "TIMCustomElem") {
218
265
  const src = JSON.parse(message.value.payload.data).src;
@@ -122,7 +122,7 @@ export default {
122
122
  margin-bottom: 8px;
123
123
  }
124
124
  .branch-item {
125
- color:#1c66e5;
125
+ color: #1c66e5;
126
126
  cursor: pointer;
127
127
  }
128
128
  .warning-item {
@@ -131,14 +131,14 @@ export default {
131
131
  .branch-card {
132
132
  min-width: 250px;
133
133
  max-width: 350px;
134
- display:flex;
135
- flex-direction:column;
134
+ display: flex;
135
+ flex-direction: column;
136
136
  align-items: flex-start;
137
137
  }
138
138
  .branch-item-selected {
139
- cursor:none;
139
+ cursor: default;
140
140
  color: #a0a7b8;
141
- opacity:0.6;
141
+ opacity: 0.6;
142
142
  }
143
143
  .branch-title-icon {
144
144
  margin-right: 5px;
@@ -79,6 +79,7 @@ import { CustomMessagePayload, TextMessagePayload, customerServicePayloadType }
79
79
  import MessageRating from './message-rating/index.vue';
80
80
  import vue from '../../../../../../adapter-vue';
81
81
  import { JSONToObject } from '../../../../../../utils/index';
82
+ import { isEnabledMessageReadReceiptGlobal } from '../../../../../../utils/utils';
82
83
  import { CUSTOM_MESSAGE_SRC } from '../../../../../../constant';
83
84
  import MessageBranch from './message-branch.vue';
84
85
  import MessageForm from './message-single-form/index.vue';
@@ -123,11 +124,15 @@ export default {
123
124
  const payload = computed<customerServicePayloadType>(() => {
124
125
  return props.message && JSONToObject(props.message?.payload?.data);
125
126
  });
126
- const sendTextMessage = (payload: TextMessagePayload) => {
127
- TUIChatService.sendTextMessage({payload});
127
+ const sendTextMessage = (payload: TextMessagePayload, cloudCustomData?: string) => {
128
+ TUIChatService.sendTextMessage({
129
+ payload,
130
+ cloudCustomData: cloudCustomData || '',
131
+ needReadReceipt: isEnabledMessageReadReceiptGlobal()
132
+ });
128
133
  };
129
134
  const sendCustomMessage = (payload: CustomMessagePayload) => {
130
- TUIChatService.sendCustomMessage({payload});
135
+ TUIChatService.sendCustomMessage({ payload });
131
136
  };
132
137
  const handleShowFormPopup = (data: boolean) => {
133
138
  emit('showFormPopup', data);
@@ -7,7 +7,7 @@
7
7
  {{ props.payload.content.header }}
8
8
  </div>
9
9
  <div
10
- v-if="props.payload.status == 0 && !isClicked"
10
+ v-if="props.payload.status === 0 && canSelect"
11
11
  v-for="(item, index) in props.payload.content.items"
12
12
  :key="index"
13
13
  :class="['form-branch-item', item.content ? '' : 'warning-item']"
@@ -19,9 +19,11 @@
19
19
  </template>
20
20
 
21
21
  <script lang="ts">
22
+ import vue from '../../../../../../../adapter-vue';
22
23
  import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
23
24
  import { customerServicePayloadType } from '../../../../../../../interface';
24
25
  import { isPC } from '../../../../../../../utils/env'
26
+ const { ref } = vue;
25
27
  interface branchItem {
26
28
  content: string;
27
29
  desc: string;
@@ -35,23 +37,35 @@ export default {
35
37
  props: {
36
38
  payload: {
37
39
  type: Object as () => customerServicePayloadType,
38
- default: () => ({content: { header: '', items: [] },status:0}),
40
+ default: () => ({optionType: 0, taskInfo: { taskID: 0, nodeID: '', env: '' }, content: { header: '', items: [] }, status: 0}),
39
41
  },
40
42
  },
41
43
  emits: ['input-click'],
42
44
  setup(props: Props, { emit }) {
43
- let isClicked = false;
45
+ let canSelect = ref(true);
44
46
  const listItemClick = (branch: branchItem): void => {
45
47
  if (!branch.content) {
46
48
  return;
47
49
  }
48
- emit('input-click', branch);
49
- isClicked = true;
50
+ let cloudCustomData;
51
+ if (props.payload.optionType === 0) {
52
+ cloudCustomData = '';
53
+ canSelect.value = false;
54
+ } else if (props.payload.optionType === 1) {
55
+ cloudCustomData = JSON.stringify({
56
+ BranchOptionInfo: {
57
+ taskID: props.payload.taskInfo?.taskID,
58
+ nodeID: props.payload.taskInfo?.nodeID,
59
+ env: props.payload.taskInfo?.env,
60
+ }
61
+ });
62
+ }
63
+ emit('input-click', branch, cloudCustomData);
50
64
  };
51
65
 
52
66
  return {
53
67
  props,
54
- isClicked,
68
+ canSelect,
55
69
  listItemClick,
56
70
  isPC,
57
71
  TUITranslateService
@@ -37,8 +37,8 @@ export default {
37
37
  const payload = computed<customerServicePayloadType>(() => {
38
38
  return props.payload;
39
39
  });
40
- const handleContentListItemClick = (branch: branchItem) => {
41
- emit('sendMessage', { text: branch.content });
40
+ const handleContentListItemClick = (branch: branchItem, cloudCustomData?: string) => {
41
+ emit('sendMessage', { text: branch.content }, cloudCustomData || '');
42
42
  };
43
43
 
44
44
  const handleFormSaveInputSubmit = (text: string) => {