@tencentcloud/ai-desk-customer-vue 1.5.10 → 1.6.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 (41) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +103 -77
  3. package/README_EN.md +873 -0
  4. package/assets/audio-blue.svg +4 -0
  5. package/assets/audio_icon_1.svg +3 -0
  6. package/assets/audio_icon_2.svg +3 -0
  7. package/assets/audio_icon_3.svg +3 -0
  8. package/assets/keyboard-icon.svg +9 -0
  9. package/components/CustomerServiceChat/chat-header/index-web.vue +41 -10
  10. package/components/CustomerServiceChat/feedback-modal/index.vue +1 -1
  11. package/components/CustomerServiceChat/index-web.vue +13 -3
  12. package/components/CustomerServiceChat/message-input/index-web.vue +57 -10
  13. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +342 -11
  14. package/components/CustomerServiceChat/message-list/index-web.vue +13 -0
  15. package/components/CustomerServiceChat/message-list/message-elements/message-audio-web.vue +50 -78
  16. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +1 -1
  17. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/marked.ts +4 -3
  18. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-robot-welcome.vue +13 -6
  19. package/components/CustomerServiceChat/message-list/message-elements/message-text.vue +3 -2
  20. package/components/CustomerServiceChat/message-toolbar-button/index.vue +11 -6
  21. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-end-human-service.vue +16 -5
  22. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-human-service.vue +8 -5
  23. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-service-rating.vue +8 -5
  24. package/components/common/Toast/index-web.ts +16 -3
  25. package/components/common/Toast/index-web.vue +16 -6
  26. package/constant.ts +1 -0
  27. package/interface.ts +8 -0
  28. package/locales/en/TUIChat.ts +6 -2
  29. package/locales/fil/TUIChat.ts +6 -2
  30. package/locales/id/TUIChat.ts +76 -72
  31. package/locales/ja/TUIChat.ts +76 -72
  32. package/locales/ms/TUIChat.ts +76 -72
  33. package/locales/ru/TUIChat.ts +6 -2
  34. package/locales/th/TUIChat.ts +76 -72
  35. package/locales/vi/TUIChat.ts +76 -72
  36. package/locales/zh_cn/TUIChat.ts +6 -2
  37. package/locales/zh_tw/TUIChat.ts +6 -2
  38. package/package.json +2 -1
  39. package/server.ts +22 -3
  40. package/utils/utils.ts +13 -0
  41. package/assets/keyboard_icon.png +0 -0
@@ -0,0 +1,4 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M7 6C7 3.23858 9.23858 1 12 1C14.7614 1 17 3.23858 17 6V12.5C17 15.2614 14.7614 17.5 12 17.5C9.23858 17.5 7 15.2614 7 12.5V6ZM15 6V12.5C15 14.1569 13.6569 15.5 12 15.5C10.3431 15.5 9 14.1569 9 12.5V6C9 4.34315 10.3431 3 12 3C13.6569 3 15 4.34315 15 6Z" fill="#1C66E5"/>
3
+ <path d="M3 11V12.5C3 17.1326 6.50005 20.9476 11 21.4451V24H13V21.4451C17.5 20.9476 21 17.1326 21 12.5V11H19V12.5C19 16.366 15.866 19.5 12 19.5C8.13401 19.5 5 16.366 5 12.5V11H3Z" fill="#1C66E5"/>
4
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M11.4131 6.32129C11.0189 6.76465 10.7773 7.34741 10.7773 7.9873C10.7775 8.62651 11.0195 9.20745 11.4131 9.65039L13.2852 7.98633L11.4131 6.32129Z" fill="white" style="fill:white;fill-opacity:1;"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M8.59863 3.82031C7.6133 4.92851 7.0137 6.38793 7.01367 7.9873L7.02148 8.30957C7.09619 9.782 7.68002 11.1194 8.59863 12.1523L9.71973 11.1553C8.97051 10.3125 8.51389 9.20362 8.51367 7.9873C8.5137 6.77028 8.9699 5.65959 9.71973 4.81641L8.59863 3.82031ZM11.4131 6.32129C11.0189 6.76465 10.7773 7.34741 10.7773 7.9873C10.7775 8.62651 11.0195 9.20745 11.4131 9.65039L13.2852 7.98633L11.4131 6.32129Z" fill="white" style="fill:white;fill-opacity:1;"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M5.78613 1.32031C4.20943 3.09333 3.25002 5.42813 3.25 7.9873L3.2627 8.50391C3.38231 10.8598 4.31628 12.9998 5.78613 14.6523L6.90723 13.6562C5.56634 12.1486 4.75019 10.1637 4.75 7.9873C4.75002 5.81048 5.56603 3.82441 6.90723 2.31641L5.78613 1.32031ZM8.59863 3.82031C7.6133 4.92851 7.0137 6.38793 7.01367 7.9873L7.02148 8.30957C7.09619 9.782 7.68002 11.1194 8.59863 12.1523L9.71973 11.1553C8.97051 10.3125 8.51389 9.20362 8.51367 7.9873C8.5137 6.77028 8.9699 5.65959 9.71973 4.81641L8.59863 3.82031ZM11.4131 6.32129C11.0189 6.76465 10.7773 7.34741 10.7773 7.9873C10.7775 8.62651 11.0195 9.20745 11.4131 9.65039L13.2852 7.98633L11.4131 6.32129Z" fill="white" style="fill:white;fill-opacity:1;"/>
3
+ </svg>
@@ -0,0 +1,9 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="12" cy="12" r="10" stroke="#1C66E5" stroke-width="2"/>
3
+ <circle cx="12" cy="9.5" r="1.5" fill="#1C66E5"/>
4
+ <circle cx="12" cy="14.5" r="1.5" fill="#1C66E5"/>
5
+ <circle cx="7.5" cy="9.5" r="1.5" fill="#1C66E5"/>
6
+ <circle cx="7.5" cy="14.5" r="1.5" fill="#1C66E5"/>
7
+ <circle cx="16.5" cy="9.5" r="1.5" fill="#1C66E5"/>
8
+ <circle cx="16.5" cy="14.5" r="1.5" fill="#1C66E5"/>
9
+ </svg>
@@ -1,16 +1,16 @@
1
1
  <template>
2
- <div :class="['chat-header', !isPC && 'chat-header-h5']">
2
+ <div :class="['chat-header', isH5 && 'chat-header-h5']">
3
3
  <div class="chat-header-name">
4
4
  <div
5
- v-if="!isPC && props.canCloseChat"
6
- :class="['chat-header-back', !isPC && 'chat-header-h5-back']"
5
+ v-if="isH5 && props.canCloseChat"
6
+ :class="['chat-header-back', isH5 && 'chat-header-h5-back']"
7
7
  @click="closeChat(currentConversation.conversationID)"
8
8
  >
9
9
  <Icon :file="backSVG" />
10
10
  </div>
11
11
  <div class="chat-header-container">
12
- <Icon v-if="!isPC" width="32px" height="23px" :file="customerAvatarMobile" />
13
- <div :class="['chat-header-content', !isPC && 'chat-header-h5-content']">
12
+ <Icon v-if="showHeaderIcon" :width="headerIconWidth" :height="headerIconHeight" :file="headerIcon" />
13
+ <div :class="['chat-header-content', isH5 && 'chat-header-h5-content']">
14
14
  {{ currentConversationName }}
15
15
  </div>
16
16
  </div>
@@ -60,16 +60,18 @@ import backSVG from '../../../assets/back.svg';
60
60
  import arrowDown from '../../../assets/language_arrow_down.svg';
61
61
  import customerAvatarMobile from '../../../assets/customer_avatar_mobile.png';
62
62
  import languageSelectedIcon from '../../../assets/language_check.svg';
63
- import { isPC } from '../../../utils/env';
63
+ import { isH5 } from '../../../utils/env';
64
64
  import state from '../../../utils/state';
65
65
  import { SUPPORTED_LANGUAGES } from '../../../constant';
66
- const { ref, onMounted, onUnmounted } = vue;
66
+ import { IHeaderConfig } from '../../../interface';
67
+ const { ref, onMounted, onUnmounted, computed } = vue;
67
68
 
68
69
  interface IProps {
69
70
  canCloseChat?: boolean;
70
71
  currentLang?: string;
71
72
  enableMultilingual: number;
72
73
  langList?: Array<string>;
74
+ headerConfig?: IHeaderConfig;
73
75
  }
74
76
  interface ILanguage {
75
77
  name: string;
@@ -92,6 +94,31 @@ const props = withDefaults(defineProps<IProps>(), {
92
94
  langList: () => [],
93
95
  });
94
96
 
97
+ const showHeaderIcon = computed(() => {
98
+ if (isH5) {
99
+ if (props.headerConfig && props.headerConfig.showLogo === false) {
100
+ return false;
101
+ }
102
+ return true;
103
+ }
104
+ return false;
105
+ });
106
+
107
+ const headerIcon = computed(() => {
108
+ return (props.headerConfig && props.headerConfig.logo) || customerAvatarMobile;
109
+ });
110
+
111
+ const headerIconWidth = computed(() => {
112
+ return (props.headerConfig && props.headerConfig.logoWidth) ?? '32px';
113
+ });
114
+
115
+ const headerIconHeight = computed(() => {
116
+ if (props.headerConfig) {
117
+ return props.headerConfig.logoHeight || '32px';
118
+ }
119
+ return '23px';
120
+ });
121
+
95
122
  if (props.enableMultilingual === 1) {
96
123
  languageOptionList.value = props.langList
97
124
  .map(key => ({
@@ -130,10 +157,14 @@ function setCurrentConversationName() {
130
157
  if (!currentConversation.value) {
131
158
  return;
132
159
  }
133
- if (isPC) {
134
- currentConversationName.value = currentConversation.value?.getShowName() || '';
160
+ if (props.headerConfig && props.headerConfig.title) {
161
+ currentConversationName.value = props.headerConfig.title;
135
162
  } else {
136
- currentConversationName.value = TUITranslateService.t('AIDesk.Hi,我是') + currentConversation.value.getShowName();
163
+ if (isH5) {
164
+ currentConversationName.value = TUITranslateService.t('AIDesk.Hi,我是') + currentConversation.value.getShowName();
165
+ } else {
166
+ currentConversationName.value = currentConversation.value.getShowName() || '';
167
+ }
137
168
  }
138
169
  }
139
170
 
@@ -174,7 +174,7 @@ export default {
174
174
  messageInfo,
175
175
  }),
176
176
  },
177
- });
177
+ },{ onlineUserOnly: true});
178
178
  }
179
179
 
180
180
  const dialogButtonClick = (item) => {
@@ -13,6 +13,7 @@
13
13
  :enableMultilingual="props.enableMultilingual"
14
14
  :langList="languageForShowList"
15
15
  :currentLang="currentLanguage"
16
+ :headerConfig="props.headerConfig"
16
17
  @closeChat="closeChat"
17
18
  @changeLanguage="changeLanguage"
18
19
  />
@@ -56,6 +57,7 @@
56
57
  :placeholder="TUITranslateService.t('TUIChat.请输入消息')"
57
58
  :inputToolbarDisplayType="inputToolbarDisplayType"
58
59
  :inputToolbarList="props.inputToolbarList"
60
+ :enableSendingAudio="props.enableSendingAudio"
59
61
  @changeToolbarDisplayType="changeToolbarDisplayType"
60
62
  @emojiShow="emojiShow"
61
63
  @toolShow="toolShow"
@@ -111,7 +113,7 @@ import MessageInput from './message-input/index-web.vue';
111
113
  import MessageInputToolbar from './message-input-toolbar/index-web.vue';
112
114
  import EmojiDialog from './message-input-toolbar/emoji-dialog-mobile/emoji-dialog-mobile.vue';
113
115
  import { isH5, isPC } from '../../utils/env';
114
- import { ToolbarButtonModel, ToolbarDisplayType, InputToolbarModel, QuickOrderModel } from '../../interface';
116
+ import { ToolbarButtonModel, ToolbarDisplayType, InputToolbarModel, QuickOrderModel, IHeaderConfig} from '../../interface';
115
117
  import Log from '../../utils/logger';
116
118
  import MessageToolbarButton from './message-toolbar-button/index.vue';
117
119
  import TUILocales from '../../locales';
@@ -147,6 +149,8 @@ interface IProps {
147
149
  enableFeedback?: number;
148
150
  enableAINote?: number;
149
151
  enableURLDetection?: number;
152
+ headerConfig?: IHeaderConfig;
153
+ enableSendingAudio?: number;
150
154
  }
151
155
 
152
156
  const emits = defineEmits(['closeChat']);
@@ -190,6 +194,7 @@ const props = withDefaults(defineProps<IProps>(), {
190
194
  enableAINote: 1,
191
195
  langList: () => [],
192
196
  enableURLDetection: 0,
197
+ enableSendingAudio: 0,
193
198
  });
194
199
 
195
200
  const loginCustomerUIKit = () => {
@@ -327,6 +332,11 @@ try {
327
332
  }
328
333
 
329
334
  onMounted(() => {
335
+ // 如果组件挂载在 sdk login 之后,需再次设置 displayMessageReadReceipt,确保状态准确
336
+ if (TUIChatEngine.isReady()) {
337
+ switchReadStatus(state.get('showReadStatus'));
338
+ }
339
+
330
340
  TUIStore.watch(StoreName.CONV, {
331
341
  currentConversationID: onCurrentConversationIDUpdate,
332
342
  });
@@ -434,8 +444,8 @@ function toolShow(){
434
444
  toolShowH5.value = !toolShowH5.value;
435
445
  }
436
446
 
437
- function blurToolAndEmojiH5(){
438
- if(emojiOpen.value === true){
447
+ function blurToolAndEmojiH5() {
448
+ if (emojiOpen.value === true) {
439
449
  emojiOpen.value = false;
440
450
  }
441
451
  toolShowH5.value = false;
@@ -9,16 +9,21 @@
9
9
  !isPC && 'message-input-container-h5',
10
10
  ]"
11
11
  >
12
+ <div v-if="shouldShowAudio" class="audio-icon" @click="audioShow">
13
+ <Icon :file="isInAudioMode ? keyboardIcon : audioIcon" width="24px" height="24px"/>
14
+ </div>
12
15
  <MessageInputEditor
13
16
  ref="editor"
14
- :placeholder="props.placeholder"
17
+ :placeholder="placeholder"
15
18
  :isMuted="props.isMuted"
16
19
  :muteText="props.muteText"
17
20
  :enableInput="props.enableInput"
18
21
  :enableTyping="props.enableTyping"
19
22
  :enableDragUpload="props.enableDragUpload"
20
- :hasInputTool="shouldShowToolbar"
21
- :hasEmojiTool="shouldShowEmoji"
23
+ :isInAudioMode="isInAudioMode"
24
+ :shouldShowToolbar="shouldShowToolbar"
25
+ :shouldShowEmoji="shouldShowEmoji"
26
+ :shouldShowAudio="shouldShowAudio"
22
27
  @sendMessage="sendMessage"
23
28
  @onTyping="onTyping"
24
29
  @blurToolAndEmojiH5="blurToolAndEmojiH5"
@@ -47,6 +52,7 @@ import {
47
52
  TUIStore,
48
53
  StoreName,
49
54
  IConversationModel,
55
+ TUITranslateService
50
56
  } from '@tencentcloud/chat-uikit-engine';
51
57
  import vue from '../../../adapter-vue';
52
58
  import MessageInputEditor from './message-input-editor-web.vue';
@@ -59,14 +65,13 @@ import Icon from '../../common/Icon.vue';
59
65
  import emojiIcon from '../../../assets/emoji.png';
60
66
  import toolIcon from '../../../assets/more_tools.png';
61
67
  import sendButtonIcon from '../../../assets/send_button_h5.svg';
68
+ import audioIcon from '../../../assets/audio-blue.svg';
69
+ import keyboardIcon from '../../../assets/keyboard-icon.svg';
62
70
  import { INPUT_TOOLBAR_TYPE } from '../../../constant';
71
+ import { onUnmounted } from 'vue';
63
72
  const { ref, onMounted, onBeforeUnmount, computed } = vue;
64
73
 
65
74
  const props = defineProps({
66
- placeholder: {
67
- type: String,
68
- default: 'this is placeholder',
69
- },
70
75
  isMuted: {
71
76
  type: Boolean,
72
77
  default: true,
@@ -91,18 +96,40 @@ const props = defineProps({
91
96
  type: Array,
92
97
  default: undefined,
93
98
  },
99
+ enableSendingAudio: {
100
+ type: Number,
101
+ default: 0,
102
+ },
94
103
  });
95
104
 
96
105
  const emit = defineEmits(['messageSent', 'resetReplyOrReference', 'onTyping','scrollToLatestMessage','changeToolbarDisplayType','insertEmoji','emojiShow','toolShow','blurToolAndEmojiH5']);
106
+ const placeholder = ref(TUITranslateService.t('TUIChat.请输入消息'));
97
107
  const editor = ref<InstanceType<typeof MessageInputEditor>>();
98
108
  const currentConversation = ref<IConversationModel>();
99
109
  const h5Dialog = ref<HTMLElement>();
100
110
  const showSendButton = ref(false);
111
+ const isInHumanService = ref(false);
101
112
  let quoteMessageCloudCustomData:string = '';
113
+ const isInAudioMode = ref(false);
102
114
 
103
115
  onMounted(() => {
104
116
  // document.addEventListener('click', handleClick);
105
- })
117
+ TUIStore.watch(StoreName.CUSTOM, {
118
+ isInHumanService: onInHumanServiceUpdate,
119
+ });
120
+ });
121
+
122
+ onUnmounted(() => {
123
+ TUIStore.unwatch(StoreName.CUSTOM, {
124
+ isInHumanService: onInHumanServiceUpdate,
125
+ });
126
+ });
127
+
128
+ const onInHumanServiceUpdate = (data: {conversationID: string, value: boolean}) => {
129
+ if (data && data.conversationID === currentConversation.value.conversationID) {
130
+ isInHumanService.value = data.value;
131
+ }
132
+ };
106
133
 
107
134
  onBeforeUnmount(() => {
108
135
  // document.removeEventListener('click', handleClick);
@@ -177,18 +204,28 @@ const blurToolAndEmojiH5 = () =>{
177
204
  emit('blurToolAndEmojiH5');
178
205
  }
179
206
 
207
+ const audioShow = () => {
208
+ isInAudioMode.value = !isInAudioMode.value;
209
+ placeholder.value = isInAudioMode.value ? TUITranslateService.t('TUIChat.按住说话') : TUITranslateService.t('TUIChat.请输入消息');
210
+ emit('blurToolAndEmojiH5');
211
+ }
212
+
180
213
  const shouldShowToolbar = computed(() => {
181
214
  if (props.inputToolbarList !== undefined && props.inputToolbarList.length === 0) {
182
215
  return false;
183
216
  }
184
- if (props.inputToolbarList !== undefined && props.inputToolbarList.length === 1 && props.inputToolbarList[0]?.presetId === INPUT_TOOLBAR_TYPE.EMOJI) {
217
+ if (props.inputToolbarList !== undefined && props.inputToolbarList.length === 1 && (props.inputToolbarList[0]?.presetId === INPUT_TOOLBAR_TYPE.EMOJI || props.inputToolbarList[0]?.presetId === INPUT_TOOLBAR_TYPE.AUDIO)) {
185
218
  return false;
186
219
  }
187
220
  return true;
188
221
  });
189
222
 
190
223
  const shouldShowEmoji = computed(() => {
191
- return props.inputToolbarList === undefined || props.inputToolbarList.some(item => item.presetId === INPUT_TOOLBAR_TYPE.EMOJI && item.isEnabled === 1);
224
+ return (props.inputToolbarList === undefined || props.inputToolbarList.some(item => item.presetId === INPUT_TOOLBAR_TYPE.EMOJI && item.isEnabled === 1)) && !isInAudioMode.value;
225
+ });
226
+
227
+ const shouldShowAudio = computed(() => {
228
+ return isH5 && isInHumanService.value && (props.enableSendingAudio === 1 || (props.inputToolbarList !== undefined && props.inputToolbarList.some(item => item.presetId === INPUT_TOOLBAR_TYPE.AUDIO && item.isEnabled === 1)));
192
229
  });
193
230
 
194
231
  defineExpose({
@@ -246,6 +283,16 @@ defineExpose({
246
283
  align-items: center;
247
284
  }
248
285
 
286
+ .audio-icon {
287
+ background: #fff;
288
+ margin-left: 10px;
289
+ border-radius: 10px;
290
+ padding: 5px;
291
+ display: flex;
292
+ justify-content: center;
293
+ align-items: center;
294
+ }
295
+
249
296
  .input-tool-dialog{
250
297
  height: fit-content;
251
298
  background: #fff;