@tencentcloud/ai-desk-customer-vue 1.5.3 → 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 (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/assets/language_arrow_down.svg +3 -0
  3. package/assets/language_check.svg +3 -0
  4. package/components/CustomerServiceChat/chat-header/index-web.vue +215 -13
  5. package/components/CustomerServiceChat/index-web.vue +86 -25
  6. package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +14 -4
  7. package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +19 -16
  8. package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +4 -4
  9. package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +10 -3
  10. package/components/CustomerServiceChat/message-list/index-web.vue +27 -14
  11. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +6 -1
  12. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +20 -13
  13. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +1 -0
  14. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-robot-welcome.vue +73 -29
  15. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-transfer-with-desc.vue +51 -0
  16. package/components/CustomerServiceChat/message-toolbar-button/index.vue +13 -2
  17. package/components/CustomerServiceChat/style/web.scss +2 -0
  18. package/constant.ts +21 -1
  19. package/interface.ts +15 -2
  20. package/locales/en/aidesk.ts +4 -4
  21. package/locales/en/index.ts +2 -2
  22. package/locales/en/time.ts +2 -2
  23. package/locales/fil/index.ts +2 -2
  24. package/locales/fil/time.ts +2 -2
  25. package/locales/id/index.ts +2 -2
  26. package/locales/id/time.ts +2 -2
  27. package/locales/ja/index.ts +2 -2
  28. package/locales/ja/time.ts +2 -2
  29. package/locales/ms/index.ts +2 -2
  30. package/locales/ms/time.ts +2 -2
  31. package/locales/ru/index.ts +2 -2
  32. package/locales/ru/time.ts +2 -2
  33. package/locales/th/index.ts +2 -2
  34. package/locales/th/time.ts +2 -2
  35. package/locales/vi/index.ts +2 -2
  36. package/locales/vi/time.ts +2 -2
  37. package/locales/zh_cn/aidesk.ts +1 -1
  38. package/locales/zh_cn/index.ts +2 -2
  39. package/locales/zh_cn/time.ts +2 -2
  40. package/locales/zh_tw/aidesk.ts +1 -1
  41. package/locales/zh_tw/index.ts +2 -2
  42. package/locales/zh_tw/time.ts +2 -2
  43. package/package.json +2 -1
  44. package/server.ts +21 -3
  45. package/utils/index.ts +6 -13
  46. package/utils/utils.ts +44 -3
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 1.5.4 @2025.7.24
2
+
3
+ ### Features
4
+ - 支持直接转指定人工或者客服分组,支持转指定任务流,提升交互体验。
5
+ - 支持点击分支选项打开 url。
6
+ - 新增参数 `enableMultilingual` 和 `langList`,支持多语言切换。
7
+ - 支持手动结束机器人会话并对机器人评分。
8
+ - 优化欢迎卡片样式。
9
+
10
+ ### Fixed
11
+ - 人工会话状态可能不准确的问题。
12
+
1
13
  ## 1.5.3 @2025.7.10
2
14
 
3
15
  ### Features
@@ -0,0 +1,3 @@
1
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M5.999 7.26559L8.88284 4.16016L9.6156 4.84063L5.99895 8.7352L2.38281 4.84061L3.11563 4.16018L5.999 7.26559Z" fill="#727A8A" stroke="#727A8A" stroke-width="0.5"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M15.96 6.81396L8.76147 14.2847L4.05078 9.57397L4.62573 9.00024L5.20068 8.42529L8.7395 11.9641L14.7905 5.68604L15.96 6.81396Z" fill="#4086FF"/>
3
+ </svg>
@@ -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>
@@ -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,52 @@ 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;
219
261
  state.set('currentLanguage', language);
220
262
  TUITranslateService.changeLanguage(language);
221
263
  }
@@ -245,7 +287,7 @@ try {
245
287
  const userContext = TUILogin.getContext();
246
288
  if (userContext.userID == '' && props.SDKAppID !==0 && props.userID !=='' && props.userSig !==''){
247
289
  loginCustomerUIKit();
248
- if (props.robotLang && !isSupportedLang(props.robotLang)) {
290
+ if (props.robotLang && !languages.includes(props.robotLang)) {
249
291
  Log.w(`robotLang:${props.robotLang} is not supported`);
250
292
  }
251
293
  }
@@ -253,6 +295,7 @@ try {
253
295
  setAvatarNickName();
254
296
  setShowReadStatus();
255
297
  setShowTyping();
298
+ getTimeZoneAndCountry();
256
299
  if (isNonEmptyObject(props.bottomQuickOrder)) {
257
300
  showBottomQuickOrder.value = true;
258
301
  }
@@ -356,14 +399,7 @@ function onCurrentConversationIDUpdate(conversationID: string) {
356
399
  currentConversationID.value = conversationID;
357
400
 
358
401
  // The TUICustomerServicePlugin plugin determines if the current conversation is a customer service conversation, then sets chatType and activates the conversation.
359
- TUICore.callService({
360
- serviceName: TUIConstants.TUICustomerServicePlugin.SERVICE.NAME,
361
- method: TUIConstants.TUICustomerServicePlugin.SERVICE.METHOD.ACTIVE_CONVERSATION,
362
- params: {
363
- conversationID: conversationID,
364
- robotLang: props.robotLang && isSupportedLang(props.robotLang) ? props.robotLang : undefined,
365
- },
366
- });
402
+ activeConversation();
367
403
  }
368
404
 
369
405
  function emojiShow(){
@@ -402,6 +438,31 @@ function onQuoteMessageUpdated(options?: {
402
438
  function closeBottomQuickOrder() {
403
439
  showBottomQuickOrder.value = false;
404
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
+ }
405
466
  </script>
406
467
 
407
468
  <style scoped lang="scss" src="./style/index.scss">
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <ToolbarItemContainer
3
- :iconFile="isH5?fileIconH5:fileIcon"
4
- :title="TUITranslateService.t('文件')"
3
+ :iconFile="isH5 ? fileIconH5 : fileIcon"
4
+ :title="props.title || TUITranslateService.t('文件')"
5
5
  iconWidth='20px'
6
6
  iconHeight='20px'
7
7
  :needDialog="false"
@@ -10,7 +10,7 @@
10
10
  <div :class="['file-upload', !isPC && 'file-upload-h5']">
11
11
  <input
12
12
  ref="inputRef"
13
- :title="TUITranslateService.t('文件')"
13
+ :title="props.title || TUITranslateService.t('文件')"
14
14
  type="file"
15
15
  data-type="file"
16
16
  accept="*"
@@ -39,6 +39,12 @@ import Log from '../../../../utils/logger';
39
39
  import { Toast, TOAST_TYPE } from '../../../common/Toast/index-web';
40
40
  const { ref } = vue;
41
41
 
42
+ const props = defineProps({
43
+ title: {
44
+ type: String,
45
+ },
46
+ });
47
+
42
48
  const inputRef = ref();
43
49
  const currentConversation = ref<IConversationModel>();
44
50
 
@@ -56,11 +62,15 @@ const sendFileMessage = (e: any) => {
56
62
  if (e?.target?.files?.length <= 0) {
57
63
  return;
58
64
  }
65
+ const file = e.target.files[0];
66
+ if (!file || !file.size) {
67
+ return;
68
+ }
59
69
  const options = {
60
70
  to: getTo(currentConversation?.value),
61
71
  conversationType: currentConversation?.value?.type,
62
72
  payload: {
63
- file: e?.target,
73
+ file: e.target,
64
74
  },
65
75
  needReadReceipt: isEnabledMessageReadReceiptGlobal(),
66
76
  } as SendMessageParams;
@@ -13,7 +13,7 @@
13
13
  >
14
14
  <input
15
15
  ref="inputRef"
16
- :title="TUITranslateService.t('图片')"
16
+ :title="props.title || TUITranslateService.t('图片')"
17
17
  type="file"
18
18
  accept="image/gif,image/jpeg,image/jpg,image/png,image/bmp,image/webp,video/mov,video/mp4"
19
19
  @change="sendImageInWeb"
@@ -31,7 +31,6 @@ import {
31
31
  SendMessageOptions,
32
32
  TUITranslateService
33
33
  } from '@tencentcloud/chat-uikit-engine';
34
- import { TUIGlobal } from '@tencentcloud/universal-api';
35
34
  import vue from '../../../../adapter-vue';
36
35
  import { isPC, isH5 } from '../../../../utils/env';
37
36
  import ToolbarItemContainer from '../toolbar-item-container/index.vue';
@@ -50,9 +49,12 @@ const props = defineProps({
50
49
  type: String,
51
50
  default: 'album',
52
51
  },
53
- isH5ToolShow:{
54
- type:Boolean,
55
- default:false,
52
+ isH5ToolShow: {
53
+ type: Boolean,
54
+ default: false,
55
+ },
56
+ title: {
57
+ type: String,
56
58
  }
57
59
  });
58
60
 
@@ -61,11 +63,11 @@ const currentConversation = ref<IConversationModel>();
61
63
  const IMAGE_TOOLBAR_SHOW_MAP = {
62
64
  web_album: {
63
65
  icon: imageIcon,
64
- title: TUITranslateService.t("图片"),
66
+ title: props.title || TUITranslateService.t("图片"),
65
67
  },
66
68
  uni_album: {
67
69
  icon: imageUniIcon,
68
- title: TUITranslateService.t("图片"),
70
+ title: props.title || TUITranslateService.t("图片"),
69
71
  }
70
72
  };
71
73
 
@@ -95,15 +97,16 @@ const sendImageInWeb = (e: any) => {
95
97
  return;
96
98
  }
97
99
  const file = e.target.files[0];
98
- console.log("file")
99
- console.log(file)
100
- if (file.type.startsWith('image/')) {
101
- // 处理图片文件
102
- sendImageMessage(e?.target);
103
- } else if (file.type.startsWith('video/')) {
104
- // 处理视频文件
105
- sendVideoMessage(e?.target);
106
- }
100
+ if (!file || !file.size) {
101
+ return;
102
+ }
103
+ if (file.type.startsWith('image/')) {
104
+ // 处理图片文件
105
+ sendImageMessage(e.target);
106
+ } else if (file.type.startsWith('video/')) {
107
+ // 处理视频文件
108
+ sendVideoMessage(e.target);
109
+ }
107
110
 
108
111
  e.target.value = '';
109
112
  };
@@ -40,10 +40,10 @@
40
40
  :isH5EmojiShow="isH5EmojiShow"
41
41
  @sendMessage="sendMessage"
42
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"/>
43
+ <ImageUpload v-else-if="item.presetId === INPUT_TOOLBAR_TYPE.IMAGE && item.isEnabled === 1" imageSourceType="album" :isH5ToolShow="isH5ToolShow" :title="item.title"/>
44
+ <FileUpload v-else-if="item.presetId === INPUT_TOOLBAR_TYPE.FILE && item.isEnabled === 1" :isH5ToolShow="isH5ToolShow" :title="item.title"/>
45
+ <VideoUpload v-else-if="item.presetId === INPUT_TOOLBAR_TYPE.VIDEO && item.isEnabled === 1" videoSourceType="album" :isH5ToolShow="isH5ToolShow" :title="item.title"/>
46
+ <RatingTool v-else-if="item.presetId === INPUT_TOOLBAR_TYPE.RATING && item.isEnabled === 1 && isInHumanService" :isH5ToolShow="isH5ToolShow" :title="item.title"/>
47
47
  <UserDefineTool
48
48
  v-else-if="item.isEnabled === 1 && item.isPreset === 0 && item.presetId !== INPUT_TOOLBAR_TYPE.EMOJI"
49
49
  :isH5ToolShow="isH5ToolShow"