@tencentcloud/ai-desk-customer-vue 1.1.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 (70) hide show
  1. package/CHANGELOG.md +16 -3
  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 +72 -16
  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 +34 -6
  24. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +65 -19
  25. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/marked.ts +1 -1
  26. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-branch.vue +30 -11
  27. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-concurrency-limit.vue +40 -0
  28. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +29 -7
  29. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +107 -73
  30. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +53 -52
  31. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-mobile/input-mobile.vue +73 -80
  32. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-mobile/label-mobile.vue +21 -24
  33. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-mobile/radios-mobile.vue +115 -116
  34. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-pc/input-pc.vue +69 -73
  35. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-pc/label-pc.vue +21 -25
  36. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-pc/radio-pc.vue +87 -77
  37. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-mobile.vue +213 -200
  38. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-pc.vue +122 -113
  39. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/index.vue +7 -7
  40. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-order.vue +141 -0
  41. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-rich-text.vue +5 -1
  42. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-stream.vue +25 -1
  43. package/components/CustomerServiceChat/message-list/message-elements/message-file.vue +1 -1
  44. package/components/CustomerServiceChat/message-list/scroll-button/index.vue +18 -6
  45. package/components/CustomerServiceChat/message-list/style/web.scss +2 -1
  46. package/components/CustomerServiceChat/message-toolbar-button/index.vue +111 -42
  47. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-end-human-service.vue +59 -0
  48. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-human-service.vue +55 -0
  49. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-service-rating.vue +59 -0
  50. package/components/common/BottomPopup/index.vue +1 -1
  51. package/constant.ts +25 -4
  52. package/interface.ts +35 -5
  53. package/locales/en/aidesk.ts +6 -3
  54. package/locales/fil/aidesk.ts +4 -1
  55. package/locales/id/aidesk.ts +7 -4
  56. package/locales/ja/aidesk.ts +5 -2
  57. package/locales/ms/aidesk.ts +5 -2
  58. package/locales/ru/aidesk.ts +6 -3
  59. package/locales/th/aidesk.ts +4 -1
  60. package/locales/vi/aidesk.ts +5 -2
  61. package/locales/zh_cn/aidesk.ts +5 -3
  62. package/locales/zh_tw/aidesk.ts +4 -1
  63. package/package.json +1 -1
  64. package/server.ts +11 -2
  65. package/utils/state.js +30 -0
  66. package/utils/utils.ts +48 -1
  67. package/assets/face.png +0 -0
  68. package/assets/files.png +0 -0
  69. package/assets/image.png +0 -0
  70. 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
 
@@ -616,6 +643,8 @@ async function scrollToLatestMessage() {
616
643
  if (messageListRef.value) {
617
644
  messageListRef.value.scrollTop = scrollHeight - height;
618
645
  }
646
+ scrollButtonInstanceRef.value?.hideScrollButton();
647
+ beforeHistoryGetScrollHeight.value = 0;
619
648
  }
620
649
 
621
650
  const handelScrollListScroll = throttle(
@@ -708,7 +737,6 @@ function setAudioPlayed(messageID: string) {
708
737
  };
709
738
  }
710
739
 
711
-
712
740
  defineExpose({
713
741
  scrollToLatestMessage,
714
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="[
@@ -44,7 +42,7 @@
44
42
  isNoPadding && isBlink ? 'blink-shadow' : '',
45
43
  !isNoPadding && isBlink ? 'blink-content' : '',
46
44
  isMultiBranchMsg?'multi-branch-message':'',
47
- (isProductCardMessage && isH5) ? 'product-message-bubble-h5' : (isProductCardMessage ? 'product-message-bubble' : ''),
45
+ (isProductCardOrOrderMessage && isH5) ? 'product-order-message-bubble-h5' : (isProductCardOrOrderMessage ? 'product-order-message-bubble' : ''),
48
46
  isPC ? '':'body-mobile',
49
47
  ]"
50
48
  >
@@ -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 (
@@ -204,20 +214,56 @@ const hasEmojiReaction = computed(() => {
204
214
  });
205
215
 
206
216
  const isMultiBranchMsg = computed(()=>{
207
- if(message.value?.type == "TIMCustomElem"){
208
- if(JSON.parse(message.value.payload.data).src == '32'){
209
- return true;
217
+ if (message.value?.type == "TIMCustomElem") {
218
+ const src = JSON.parse(message.value.payload.data).src;
219
+ if (src === CUSTOM_MESSAGE_SRC.MULTI_BRANCH || src === CUSTOM_MESSAGE_SRC.BRANCH || src === CUSTOM_MESSAGE_SRC.BRANCH_NUMBER) {
220
+ return true;
221
+ }
210
222
  }
223
+ return false;
224
+ });
211
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;
212
232
  }
213
-
214
- return false;
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;
215
247
  });
216
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
+ });
217
262
 
218
- const isProductCardMessage = computed(()=>{
263
+ const isProductCardOrOrderMessage = computed(() => {
219
264
  if (message.value?.type == "TIMCustomElem") {
220
- if (JSON.parse(message.value.payload.data).src === CUSTOM_MESSAGE_SRC.PRODUCT_CARD) {
265
+ const src = JSON.parse(message.value.payload.data).src;
266
+ if (src === CUSTOM_MESSAGE_SRC.PRODUCT_CARD || src === CUSTOM_MESSAGE_SRC.ORDER) {
221
267
  return true;
222
268
  }
223
269
  }
@@ -395,8 +441,8 @@ function scrollTo(scrollHeight: number) {
395
441
  padding-top: 5px;
396
442
  }
397
443
  }
398
-
399
-
444
+
445
+
400
446
  .content-in {
401
447
  background: #f3f4f7;
402
448
  border-radius: 0 10px 10px;
@@ -411,12 +457,12 @@ function scrollTo(scrollHeight: number) {
411
457
  border-radius: 0 10px 10px;
412
458
  padding: 0px;
413
459
  }
414
-
415
- .product-message-bubble {
460
+
461
+ .product-order-message-bubble {
416
462
  background: #f3f4f7 !important ;
417
463
  }
418
464
 
419
- .product-message-bubble-h5 {
465
+ .product-order-message-bubble-h5 {
420
466
  background: #fff !important ;
421
467
  }
422
468
 
@@ -10,7 +10,7 @@ export const marked = new Marked(
10
10
  class="image-container"
11
11
  onclick="onMarkdownImageClicked('${safeHref}')"
12
12
  style="cursor:pointer;" >
13
- <img src="${href}" alt="${text}"/>
13
+ <img src="${href}" alt="${text}" onload="onMarkdownImageLoad()"/>
14
14
  </div>
15
15
  `;
16
16
  },