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

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 (63) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +316 -71
  3. package/assets/language_arrow_down.svg +3 -0
  4. package/assets/language_check.svg +3 -0
  5. package/components/CustomerServiceChat/chat-header/index-web.vue +215 -13
  6. package/components/CustomerServiceChat/emoji-config/index.ts +3 -2
  7. package/components/CustomerServiceChat/index-web.vue +87 -25
  8. package/components/CustomerServiceChat/message-input/index-web.vue +9 -2
  9. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +11 -0
  10. package/components/CustomerServiceChat/message-input/message-input-quote/index.vue +31 -6
  11. package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +14 -4
  12. package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +19 -16
  13. package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +4 -4
  14. package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +10 -3
  15. package/components/CustomerServiceChat/message-list/index-web.vue +27 -14
  16. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +6 -1
  17. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +20 -13
  18. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +1 -0
  19. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-robot-welcome.vue +73 -29
  20. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-transfer-with-desc.vue +51 -0
  21. package/components/CustomerServiceChat/message-list/message-elements/message-quote/index-web.vue +18 -9
  22. package/components/CustomerServiceChat/message-toolbar-button/index.vue +13 -2
  23. package/components/CustomerServiceChat/style/web.scss +2 -0
  24. package/components/CustomerServiceChat/utils/sendMessage.ts +5 -0
  25. package/constant.ts +21 -1
  26. package/interface.ts +16 -3
  27. package/locales/en/TUIChat.ts +2 -1
  28. package/locales/en/aidesk.ts +4 -4
  29. package/locales/en/index.ts +2 -2
  30. package/locales/en/time.ts +2 -2
  31. package/locales/fil/TUIChat.ts +1 -0
  32. package/locales/fil/index.ts +2 -2
  33. package/locales/fil/time.ts +2 -2
  34. package/locales/id/TUIChat.ts +161 -160
  35. package/locales/id/index.ts +2 -2
  36. package/locales/id/time.ts +2 -2
  37. package/locales/ja/TUIChat.ts +162 -161
  38. package/locales/ja/index.ts +2 -2
  39. package/locales/ja/time.ts +2 -2
  40. package/locales/ms/TUIChat.ts +162 -161
  41. package/locales/ms/index.ts +2 -2
  42. package/locales/ms/time.ts +2 -2
  43. package/locales/ru/TUIChat.ts +155 -154
  44. package/locales/ru/index.ts +2 -2
  45. package/locales/ru/time.ts +2 -2
  46. package/locales/th/TUIChat.ts +162 -161
  47. package/locales/th/index.ts +2 -2
  48. package/locales/th/time.ts +2 -2
  49. package/locales/vi/TUIChat.ts +153 -152
  50. package/locales/vi/index.ts +2 -2
  51. package/locales/vi/time.ts +2 -2
  52. package/locales/zh_cn/TUIChat.ts +1 -0
  53. package/locales/zh_cn/aidesk.ts +1 -1
  54. package/locales/zh_cn/index.ts +2 -2
  55. package/locales/zh_cn/time.ts +2 -2
  56. package/locales/zh_tw/TUIChat.ts +1 -0
  57. package/locales/zh_tw/aidesk.ts +1 -1
  58. package/locales/zh_tw/index.ts +2 -2
  59. package/locales/zh_tw/time.ts +2 -2
  60. package/package.json +5 -6
  61. package/server.ts +63 -10
  62. package/utils/index.ts +6 -13
  63. package/utils/utils.ts +96 -2
@@ -1,17 +1,49 @@
1
1
  <template>
2
2
  <div :class="['chat-header', !isPC && 'chat-header-h5']">
3
- <div
4
- v-if="!isPC && props.canCloseChat"
5
- :class="['chat-header-back', !isPC && 'chat-header-h5-back']"
6
- @click="closeChat(currentConversation.conversationID)"
7
- >
8
- <Icon :file="backSVG" />
3
+ <div class="chat-header-name">
4
+ <div
5
+ v-if="!isPC && props.canCloseChat"
6
+ :class="['chat-header-back', !isPC && 'chat-header-h5-back']"
7
+ @click="closeChat(currentConversation.conversationID)"
8
+ >
9
+ <Icon :file="backSVG" />
10
+ </div>
11
+ <div class="chat-header-container">
12
+ <Icon v-if="!isPC" width="32px" :file="customerAvatarMobile" />
13
+ <div :class="['chat-header-content', !isPC && 'chat-header-h5-content']">
14
+ {{ currentConversationName }}
15
+ </div>
16
+ </div>
9
17
  </div>
10
- <div class="chat-header-container">
11
- <Icon v-if="!isPC" style="width:32px;" :file="customerAvatarMobile" />
12
- <div :class="['chat-header-content', !isPC && 'chat-header-h5-content']">
13
- {{ currentConversationName }}
18
+ <div v-if="props.enableMultilingual === 1" class="language-selector">
19
+ <div class="selected-language-option" @click="toggleDropdown">
20
+ {{ currentLanguageOption.name }}
21
+ <span class="arrow" :class="{ 'arrow-up': showDropdown }">
22
+ <Icon :file="arrowDown" width="12px" height="12px" />
23
+ </span>
14
24
  </div>
25
+
26
+ <transition name="fade">
27
+ <div v-if="showDropdown" class="dropdown-popup">
28
+ <div
29
+ v-for="option in languageOptionList"
30
+ :key="option.code"
31
+ :class="['dropdown-item',currentLanguageOption.code === option.code ? 'dropdown-item-selected' : '']"
32
+ @click="selectLanguage(option)"
33
+ >
34
+ <div class="language">
35
+ <div class="language-code">
36
+ {{ option.codeForShow }}
37
+ </div>
38
+ <div class="language-name">
39
+ {{ option.name }}
40
+ </div>
41
+ </div>
42
+
43
+ <Icon v-if="currentLanguageOption.code === option.code" :file="languageSelectedIcon" width="20px" height="20px" />
44
+ </div>
45
+ </div>
46
+ </transition>
15
47
  </div>
16
48
  </div>
17
49
  </template>
@@ -25,24 +57,51 @@ import {
25
57
  } from '@tencentcloud/chat-uikit-engine';
26
58
  import Icon from '../../common/Icon.vue';
27
59
  import backSVG from '../../../assets/back.svg';
60
+ import arrowDown from '../../../assets/language_arrow_down.svg';
28
61
  import customerAvatarMobile from '../../../assets/customer_avatar_mobile.png';
62
+ import languageSelectedIcon from '../../../assets/language_check.svg';
29
63
  import { isPC } from '../../../utils/env';
30
64
  import state from '../../../utils/state';
65
+ import { SUPPORTED_LANGUAGES } from '../../../constant';
31
66
  const { ref, onMounted, onUnmounted } = vue;
32
67
 
33
68
  interface IProps {
34
69
  canCloseChat?: boolean;
70
+ currentLang?: string;
71
+ enableMultilingual: number;
72
+ langList?: Array<string>;
73
+ }
74
+ interface ILanguage {
75
+ name: string;
76
+ code: string;
77
+ codeForShow: string;
35
78
  }
36
79
 
37
- const emits = defineEmits(['closeChat']);
80
+ const emits = defineEmits(['closeChat','changeLanguage']);
38
81
  const currentConversation = ref<IConversationModel>();
39
82
  const currentConversationName = ref('');
40
83
  const isTyping = ref(false);
41
-
84
+ const showDropdown = ref(false);
85
+ const languageOptionList = ref<Array<ILanguage>>([]);
86
+ const currentLanguageOption = ref<ILanguage>({name:'',code:'',codeForShow:''});
87
+
42
88
  const props = withDefaults(defineProps<IProps>(), {
43
89
  canCloseChat: true,
90
+ currentLang: '',
91
+ enableMultilingual: 0,
92
+ langList: () => [],
44
93
  });
45
94
 
95
+ if (props.enableMultilingual === 1) {
96
+ languageOptionList.value = props.langList
97
+ .map(key => ({
98
+ code: key,
99
+ codeForShow: SUPPORTED_LANGUAGES[key].codeForShow,
100
+ name: SUPPORTED_LANGUAGES[key].name,
101
+ }));
102
+ currentLanguageOption.value = languageOptionList.value.find(option => option.code === props.currentLang);
103
+ }
104
+
46
105
  onMounted(() => {
47
106
  TUIStore.watch(StoreName.CONV, {
48
107
  currentConversation: onCurrentConversationUpdated,
@@ -84,8 +143,24 @@ function onTypingStatusUpdated(status: boolean) {
84
143
  }
85
144
  }
86
145
  }
146
+
147
+ function toggleDropdown() {
148
+ showDropdown.value = !showDropdown.value
149
+ }
150
+
151
+ function selectLanguage(languageOption) {
152
+ showDropdown.value = false;
153
+ currentLanguageOption.value = languageOption;
154
+ emits('changeLanguage', languageOption.code);
155
+ }
87
156
  </script>
88
157
  <style lang="scss" scoped>
158
+ .chat-header-name {
159
+ display: flex;
160
+ flex-direction: row;
161
+ flex: 1;
162
+ min-width: 0;
163
+ }
89
164
  .chat-header {
90
165
  display: flex;
91
166
  min-width: 0;
@@ -93,9 +168,12 @@ function onTypingStatusUpdated(status: boolean) {
93
168
  align-items: center;
94
169
  justify-content: space-between;
95
170
  border-bottom:1px solid #f3f4f7;
171
+ font-family: PingFangSC-Regular;
172
+ width: 100%;
96
173
 
97
174
  &-container {
98
175
  display: flex;
176
+ flex: 1;
99
177
  min-width: 0;
100
178
  flex-direction: row;
101
179
  justify-content: flex-start;
@@ -113,6 +191,7 @@ function onTypingStatusUpdated(status: boolean) {
113
191
  overflow: hidden;
114
192
  white-space: nowrap;
115
193
  text-overflow: ellipsis;
194
+ min-width: 0;
116
195
  }
117
196
 
118
197
  &-back,
@@ -137,7 +216,130 @@ function onTypingStatusUpdated(status: boolean) {
137
216
  }
138
217
 
139
218
  &-content {
140
- text-align: center;
219
+ margin: 0 20px 0 10px;
141
220
  }
142
221
  }
222
+
223
+ .language-selector {
224
+ position: relative;
225
+ cursor: pointer;
226
+ user-select: none;
227
+ flex-shrink: 0;
228
+ }
229
+
230
+ .selected-language-option {
231
+ padding: 8px 6px;
232
+ border-radius: 4px;
233
+ display: flex;
234
+ justify-content: flex-end;
235
+ align-items: center;
236
+ font-size: 12px;
237
+ color: #0F1014;
238
+ gap: 5px;
239
+ }
240
+
241
+ .arrow {
242
+ transition: transform 0.2s;
243
+ font-size: 12px;
244
+ }
245
+
246
+ .arrow-up {
247
+ transform: rotate(180deg);
248
+ }
249
+
250
+ .dropdown-popup {
251
+ position: absolute;
252
+ top: 100%;
253
+ right: 10px;
254
+ font-size: 12px;
255
+ max-width: 200px;
256
+ max-height: 400px;
257
+ border-radius: 8px;
258
+ background-color: white;
259
+ box-shadow: 0px 1px 8px 0px var(---Black-8, rgba(0, 0, 0, 0.06)), 0px 4px 12px 0px var(---Black-8, rgba(0, 0, 0, 0.06)), 0px 10px 30px 0px var(---Black-8, rgba(0, 0, 0, 0.06));
260
+ z-index: 1000;
261
+ margin-top: 4px;
262
+ padding: 8px 0px;
263
+ overflow-y: auto;
264
+ scrollbar-width: 4px;
265
+ scrollbar-color: #E7EAEF;
266
+ }
267
+
268
+ .dropdown-popup::-webkit-scrollbar {
269
+ width: 4px;
270
+ }
271
+ .dropdown-popup::-webkit-scrollbar-track {
272
+ background: transparent;
273
+ }
274
+ .dropdown-popup::-webkit-scrollbar-thumb {
275
+ background-color: #E7EAEF;
276
+ border-radius: 2px;
277
+ }
278
+
279
+ .dropdown-item {
280
+ transition: background-color 0.2s;
281
+ display: flex;
282
+ flex-direction: row;
283
+ padding: 5px 12px;
284
+ justify-content: space-between;
285
+ align-items: center;
286
+ width: 100%;
287
+ box-sizing: border-box;
288
+ .language {
289
+ display: flex;
290
+ gap: 8px;
291
+ align-items: center;
292
+ flex: 1;
293
+ min-width: 0;
294
+ font-size: 12px;
295
+ .language-code {
296
+ color: #71798A;
297
+ display: flex;
298
+ justify-content: center;
299
+ align-items: center;
300
+ border-radius: 2px;
301
+ border: 1px solid rgba(114, 122, 138, 0.20);
302
+ padding: 0 8px;
303
+ font-weight: 600;
304
+ min-width: 12px;
305
+ flex-shrink: 0;
306
+ }
307
+ .language-name {
308
+ color: #0F1014;
309
+ white-space: nowrap;
310
+ overflow: hidden;
311
+ text-overflow: ellipsis;
312
+ flex: 1;
313
+ min-width: 0;
314
+ }
315
+ }
316
+ }
317
+
318
+ .dropdown-item:hover,.dropdown-item-selected {
319
+ background-color: #F2F6FF;
320
+ }
321
+
322
+ .dropdown-item-selected {
323
+ position: relative;
324
+ }
325
+
326
+ .dropdown-item-selected::after {
327
+ content: '';
328
+ position: absolute;
329
+ left: 0;
330
+ right: 0;
331
+ top: 0;
332
+ bottom: 0;
333
+ background-color: #F2F6FF;
334
+ z-index: -1;
335
+ }
336
+
337
+ .fade-enter-active, .fade-leave-active {
338
+ transition: opacity 0.2s, transform 0.2s;
339
+ }
340
+
341
+ .fade-enter, .fade-leave-to {
342
+ opacity: 0;
343
+ transform: translateY(-10px);
344
+ }
143
345
  </style>
@@ -2,9 +2,10 @@ import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
2
2
  import { CUSTOM_BASIC_EMOJI_URL, CUSTOM_BIG_EMOJI_URL, CUSTOM_BASIC_EMOJI_URL_MAPPING, CUSTOM_BIG_EMOJI_GROUP_LIST } from './custom-emoji';
3
3
  import { DEFAULT_BASIC_EMOJI_URL, BIG_EMOJI_GROUP_LIST, DEFAULT_BASIC_EMOJI_URL_MAPPING, BASIC_EMOJI_NAME_TO_KEY_MAPPING, DEFAULT_BIG_EMOJI_URL } from './default-emoji';
4
4
  import { default as emojiCNLocales } from './locales/zh_cn';
5
+ import { default as emojiENLocales } from './locales/en';
5
6
  import { IEmojiGroupList } from '../../../interface';
6
7
  import { EMOJI_TYPE } from '../../../constant';
7
- import { isWeChat } from '../../../utils/env';
8
+ import state from '../../../utils/state.js';
8
9
 
9
10
  const hasCustomBasicEmoji = CUSTOM_BASIC_EMOJI_URL && Object.keys(CUSTOM_BASIC_EMOJI_URL_MAPPING).length;
10
11
 
@@ -32,7 +33,7 @@ const EMOJI_GROUP_LIST: IEmojiGroupList = [
32
33
  */
33
34
  const convertKeyToEmojiName = (key: string): string => {
34
35
  // WeChat does not support emoji translation
35
- return isWeChat ? emojiCNLocales[key] : TUITranslateService.t(`Emoji.${key}`);
36
+ return state.get('currentLanguage') == 'zh' ? emojiCNLocales[key] : emojiENLocales[key];
36
37
  };
37
38
 
38
39
  /**
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div :class="['tui-chat', !isPC && 'tui-chat-h5']">
2
+ <div :class="['tui-chat', !isPC && 'tui-chat-h5']" :key="currentLanguage">
3
3
  <div
4
4
  v-if="currentConversationID"
5
5
  :class="['tui-chat', !isPC && 'tui-chat-h5']"
@@ -10,7 +10,11 @@
10
10
  !isPC && 'tui-chat-H5-header'
11
11
  ]"
12
12
  :canCloseChat="props.canCloseChat"
13
+ :enableMultilingual="props.enableMultilingual"
14
+ :langList="languageForShowList"
15
+ :currentLang="currentLanguage"
13
16
  @closeChat="closeChat"
17
+ @changeLanguage="changeLanguage"
14
18
  />
15
19
  <MessageList
16
20
  ref="messageListRef"
@@ -99,14 +103,13 @@ import MessageInputToolbar from './message-input-toolbar/index-web.vue';
99
103
  import EmojiDialog from './message-input-toolbar/emoji-dialog-mobile/emoji-dialog-mobile.vue';
100
104
  import { isH5, isPC } from '../../utils/env';
101
105
  import { ToolbarButtonModel, ToolbarDisplayType, InputToolbarModel, QuickOrderModel } from '../../interface';
102
- import { isSupportedLang } from '../../utils/';
103
106
  import Log from '../../utils/logger';
104
107
  import MessageToolbarButton from './message-toolbar-button/index.vue';
105
108
  import TUILocales from '../../locales';
106
109
  import { Toast, TOAST_TYPE } from '../common/Toast/index-web';
107
110
  import state from '../../utils/state.js';
108
111
  import { switchReadStatus,isNonEmptyObject } from '../../utils/utils';
109
-
112
+ import { getCountryForTimezone } from 'countries-and-timezones';
110
113
  const { ref, onMounted, onUnmounted, computed } = vue;
111
114
 
112
115
  interface IProps {
@@ -129,6 +132,8 @@ interface IProps {
129
132
  showReadStatus?: number;
130
133
  showTyping?: number;
131
134
  bottomQuickOrder?: QuickOrderModel;
135
+ enableMultilingual?: number;
136
+ langList?: Array<string>;
132
137
  }
133
138
 
134
139
  const emits = defineEmits(['closeChat']);
@@ -144,6 +149,10 @@ const toolShowH5 = ref(false);
144
149
  const languages = Object.keys(TUILocales);
145
150
  const quoteMessage = ref<IMessageModel>();
146
151
  const showBottomQuickOrder = ref(false);
152
+ const currentLanguage = ref('');
153
+ const languageForShowList = ref<Array<string>>([]);
154
+ let timezone = '';
155
+ let countryID = '';
147
156
  const props = withDefaults(defineProps<IProps>(), {
148
157
  canCloseChat: true,
149
158
  SDKAppID: 0,
@@ -161,6 +170,8 @@ const props = withDefaults(defineProps<IProps>(), {
161
170
  staffNickName: '',
162
171
  userNickName: '',
163
172
  showTyping: 0,
173
+ enableMultilingual: 0,
174
+ langList: () => [],
164
175
  });
165
176
 
166
177
  const loginCustomerUIKit = () => {
@@ -185,6 +196,12 @@ const loginCustomerUIKit = () => {
185
196
  })
186
197
  }
187
198
 
199
+ const getTimeZoneAndCountry = () => {
200
+ timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
201
+ const country = getCountryForTimezone(timezone);
202
+ countryID = country && country.id ? country.id : '';
203
+ }
204
+
188
205
  const convertLanguageToLowercase = (language) => {
189
206
  let lowercase = language.toLowerCase();
190
207
  if (lowercase === 'zh-cn' || lowercase === 'zh_cn') {
@@ -195,27 +212,53 @@ const convertLanguageToLowercase = (language) => {
195
212
  return lowercase;
196
213
  }
197
214
 
215
+ const getValidLanguage = (language: string) => {
216
+ let validLanguage = '';
217
+ if (props.enableMultilingual === 1 && languageForShowList.value.length !== 0) {
218
+ if (languageForShowList.value.includes(language)) {
219
+ validLanguage = language;
220
+ } else {
221
+ // choose default language (languageForShowList.value[0] for uikit) if language is not supported
222
+ validLanguage = languageForShowList.value[0];
223
+ }
224
+ } else if (languages.includes(language)) {
225
+ validLanguage = language;
226
+ } else {
227
+ if (props.userLang !== ''){
228
+ Log.w(`userLang:${props.userLang} is not supported`);
229
+ }
230
+ validLanguage = 'en';
231
+ }
232
+ return validLanguage;
233
+ }
234
+
198
235
  const initLanguage = () => {
199
236
  Log.i(`initLanguage ${props.userLang}`);
200
237
  TUITranslateService.provideLanguages({ ...TUILocales });
201
238
  TUITranslateService.useI18n();
239
+ if (props.enableMultilingual === 1) {
240
+ languageForShowList.value = props.langList.filter(language => languages.includes(language));
241
+ }
202
242
  let language;
203
- if (props.userLang !== '') {
204
- let userLang = convertLanguageToLowercase(props.userLang);
205
- if (!languages.includes(userLang)) {
206
- Log.w(`userLang:${props.userLang} is not supported`);
207
- language = 'en';
208
- } else {
209
- language = userLang;
243
+ try {
244
+ const prevLanguage = localStorage.getItem('AIDesk_language');
245
+ if (prevLanguage) {
246
+ language = prevLanguage;
210
247
  }
211
- } else {
212
- let navigatorLang = convertLanguageToLowercase(navigator.language);
213
- if (!languages.includes(navigatorLang)) {
214
- language = 'en';
248
+ } catch {
249
+ Log.w('No Previous Language');
250
+ }
251
+ if (!language) {
252
+ if (props.userLang !== '') {
253
+ let userLang = convertLanguageToLowercase(props.userLang);
254
+ language = getValidLanguage(userLang);
215
255
  } else {
216
- language = navigatorLang;
256
+ let navigatorLang = convertLanguageToLowercase(navigator.language);
257
+ language = getValidLanguage(navigatorLang);
217
258
  }
218
- }
259
+ }
260
+ currentLanguage.value = language;
261
+ state.set('currentLanguage', language);
219
262
  TUITranslateService.changeLanguage(language);
220
263
  }
221
264
 
@@ -244,7 +287,7 @@ try {
244
287
  const userContext = TUILogin.getContext();
245
288
  if (userContext.userID == '' && props.SDKAppID !==0 && props.userID !=='' && props.userSig !==''){
246
289
  loginCustomerUIKit();
247
- if (props.robotLang && !isSupportedLang(props.robotLang)) {
290
+ if (props.robotLang && !languages.includes(props.robotLang)) {
248
291
  Log.w(`robotLang:${props.robotLang} is not supported`);
249
292
  }
250
293
  }
@@ -252,6 +295,7 @@ try {
252
295
  setAvatarNickName();
253
296
  setShowReadStatus();
254
297
  setShowTyping();
298
+ getTimeZoneAndCountry();
255
299
  if (isNonEmptyObject(props.bottomQuickOrder)) {
256
300
  showBottomQuickOrder.value = true;
257
301
  }
@@ -355,14 +399,7 @@ function onCurrentConversationIDUpdate(conversationID: string) {
355
399
  currentConversationID.value = conversationID;
356
400
 
357
401
  // The TUICustomerServicePlugin plugin determines if the current conversation is a customer service conversation, then sets chatType and activates the conversation.
358
- TUICore.callService({
359
- serviceName: TUIConstants.TUICustomerServicePlugin.SERVICE.NAME,
360
- method: TUIConstants.TUICustomerServicePlugin.SERVICE.METHOD.ACTIVE_CONVERSATION,
361
- params: {
362
- conversationID: conversationID,
363
- robotLang: props.robotLang && isSupportedLang(props.robotLang) ? props.robotLang : undefined,
364
- },
365
- });
402
+ activeConversation();
366
403
  }
367
404
 
368
405
  function emojiShow(){
@@ -401,6 +438,31 @@ function onQuoteMessageUpdated(options?: {
401
438
  function closeBottomQuickOrder() {
402
439
  showBottomQuickOrder.value = false;
403
440
  }
441
+
442
+ function changeLanguage(languageCode: string) {
443
+ Log.l(`multilingual: change language to ${languageCode}`);
444
+ TUITranslateService.changeLanguage(languageCode).then(() => {
445
+ currentLanguage.value = languageCode;
446
+ try {
447
+ localStorage.setItem('AIDesk_language', languageCode);
448
+ } catch {
449
+ Log.w("Failed to set language to localStorage.");
450
+ }
451
+ activeConversation();
452
+ });
453
+ }
454
+ function activeConversation() {
455
+ TUICore.callService({
456
+ serviceName: TUIConstants.TUICustomerServicePlugin.SERVICE.NAME,
457
+ method: TUIConstants.TUICustomerServicePlugin.SERVICE.METHOD.ACTIVE_CONVERSATION,
458
+ params: {
459
+ conversationID: currentConversationID.value,
460
+ robotLang: props.robotLang && languages.includes(props.robotLang) ? props.robotLang : currentLanguage.value === 'zh_tw' ? 'zh-TW' : currentLanguage.value,
461
+ country: countryID,
462
+ timezone: timezone,
463
+ },
464
+ });
465
+ }
404
466
  </script>
405
467
 
406
468
  <style scoped lang="scss" src="./style/index.scss">
@@ -1,6 +1,8 @@
1
1
  <template>
2
2
  <div class="message-input-wrapper">
3
- <MessageInputQuote />
3
+ <MessageInputQuote
4
+ @getQuoteMessageCloudCustomData="getQuoteMessageCloudCustomData"
5
+ />
4
6
  <div
5
7
  :class="[
6
8
  'message-input-container',
@@ -96,6 +98,7 @@ const editor = ref<InstanceType<typeof MessageInputEditor>>();
96
98
  const currentConversation = ref<IConversationModel>();
97
99
  const h5Dialog = ref<HTMLElement>();
98
100
  const showSendButton = ref(false);
101
+ let quoteMessageCloudCustomData:string = '';
99
102
 
100
103
  onMounted(() => {
101
104
  // document.addEventListener('click', handleClick);
@@ -136,13 +139,17 @@ const sendMessage = async () => {
136
139
  }
137
140
  return editor;
138
141
  });
139
- await sendMessages(editorContentList, currentConversation.value);
142
+ await sendMessages(editorContentList, currentConversation.value, quoteMessageCloudCustomData);
140
143
  // 注意这里不要 emit 'sendMessage',避免写出死循环
141
144
  emit('messageSent');
142
145
  editor.value?.resetEditor();
143
146
  showSendButton.value = false;
144
147
  };
145
148
 
149
+ const getQuoteMessageCloudCustomData = (cloudCustomData: string) => {
150
+ quoteMessageCloudCustomData = cloudCustomData;
151
+ };
152
+
146
153
  const insertEmoji = (emoji: any) => {
147
154
  editor.value?.addEmoji(emoji);
148
155
  showSendButton.value = true;
@@ -121,6 +121,17 @@ function onQuoteMessageUpdated(options?: {
121
121
  type: string;
122
122
  }) {
123
123
  currentQuoteMessage.value = options;
124
+ if (options && options.message) {
125
+ focusEditor();
126
+ }
127
+ }
128
+
129
+ function focusEditor() {
130
+ if (isH5 && editorDom.value) {
131
+ editorDom.value.focus();
132
+ } else if (editor) {
133
+ editor.commands.focus()
134
+ }
124
135
  }
125
136
 
126
137
  onMounted(() => {
@@ -21,8 +21,8 @@
21
21
  <Icon
22
22
  class="input-quote-close-icon"
23
23
  :file="closeIcon"
24
- width="11px"
25
- height="11px"
24
+ width="14px"
25
+ height="14px"
26
26
  @onClick="cancelQuote"
27
27
  />
28
28
  </div>
@@ -42,6 +42,8 @@ import closeIcon from '../../../../assets/close-quote-icon.svg';
42
42
  import { isPC,isH5 } from '../../../../utils/env';
43
43
  import { transformTextWithKeysToEmojiNames } from '../../emoji-config';
44
44
  import { InputDisplayType } from '../../../../interface';
45
+ import { getQuoteContentForDesk } from '../../../../utils/utils';
46
+ import { MessageQuoteTypeEnum } from '../../message-list/message-elements/message-quote/interface';
45
47
  const { ref, computed, onMounted, onUnmounted } = vue;
46
48
 
47
49
  interface IProps {
@@ -54,6 +56,9 @@ const props = withDefaults(defineProps<IProps>(), {
54
56
 
55
57
  const TYPES = TUIChatEngine.TYPES;
56
58
  const quoteMessage = ref<IMessageModel>();
59
+ const quoteMessageType = ref<number>(0);
60
+
61
+ const emits = defineEmits(['getQuoteMessageCloudCustomData']);
57
62
 
58
63
  onMounted(() => {
59
64
  TUIStore.watch(StoreName.CHAT, {
@@ -74,30 +79,39 @@ const quoteContentText = computed(() => {
74
79
  _quoteContentText = transformTextWithKeysToEmojiNames(
75
80
  quoteMessage.value.payload?.text,
76
81
  );
82
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_TEXT;
77
83
  break;
78
84
  case TYPES.MSG_IMAGE:
79
85
  _quoteContentText = TUITranslateService.t('TUIChat.图片');
86
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_IMAGE;
80
87
  break;
81
88
  case TYPES.MSG_AUDIO:
82
89
  _quoteContentText = TUITranslateService.t('TUIChat.语音');
90
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_SOUND;
83
91
  break;
84
92
  case TYPES.MSG_VIDEO:
85
93
  _quoteContentText = TUITranslateService.t('TUIChat.视频');
94
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_VIDEO;
86
95
  break;
87
96
  case TYPES.MSG_FILE:
88
97
  _quoteContentText = TUITranslateService.t('TUIChat.文件');
98
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_FILE;
89
99
  break;
90
100
  case TYPES.MSG_CUSTOM:
91
- _quoteContentText = TUITranslateService.t('TUIChat.自定义');
101
+ _quoteContentText = getQuoteContentForDesk(quoteMessage.value);
102
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_CUSTOM;
92
103
  break;
93
104
  case TYPES.MSG_FACE:
94
105
  _quoteContentText = TUITranslateService.t('TUIChat.表情');
106
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_FACE;
95
107
  break;
96
108
  case TYPES.MSG_MERGER:
97
109
  _quoteContentText = TUITranslateService.t('TUIChat.聊天记录');
110
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_MERGER;
98
111
  break;
99
112
  default:
100
113
  _quoteContentText = TUITranslateService.t('TUIChat.消息');
114
+ quoteMessageType.value = MessageQuoteTypeEnum.TYPE_NONE;
101
115
  break;
102
116
  }
103
117
  return _quoteContentText;
@@ -114,10 +128,21 @@ function onQuoteMessageUpdated(options?: {
114
128
  message: IMessageModel;
115
129
  type: string;
116
130
  }) {
117
- if (options?.message && options?.type === 'quote') {
131
+ if (options && options.message && options.type === 'quote') {
118
132
  quoteMessage.value = options.message;
133
+ const quoteMessageCloudCustomData = JSON.stringify({
134
+ messageReply: {
135
+ messageAbstract: quoteContentText.value,
136
+ messageID: quoteMessage.value.ID,
137
+ messageSender: quoteMessage.value.from,
138
+ messageTime: quoteMessage.value.time,
139
+ messageType: quoteMessageType.value,
140
+ }
141
+ });
142
+ emits('getQuoteMessageCloudCustomData', quoteMessageCloudCustomData);
119
143
  } else {
120
144
  quoteMessage.value = undefined;
145
+ emits('getQuoteMessageCloudCustomData', undefined);
121
146
  }
122
147
  }
123
148
  </script>
@@ -152,7 +177,7 @@ function onQuoteMessageUpdated(options?: {
152
177
  .input-quote-content-h5 {
153
178
  display: flex;
154
179
  justify-content: space-between;
155
- background-color: #fff;
180
+ background-color: rgba(255, 255, 255, 0.5);
156
181
  padding: 8px 16px 8px 8px;
157
182
  font-size: 12px;
158
183
  align-items: center;
@@ -197,6 +222,6 @@ function onQuoteMessageUpdated(options?: {
197
222
  .input-quote-container-h5 {
198
223
  @extend %common-container-style;
199
224
  width:100%;
200
- margin: 0px 0 5px 5px;
225
+ margin: 5px 0 5px 0px;
201
226
  }
202
227
  </style>