@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.
- package/CHANGELOG.md +12 -0
- package/assets/language_arrow_down.svg +3 -0
- package/assets/language_check.svg +3 -0
- package/components/CustomerServiceChat/chat-header/index-web.vue +215 -13
- package/components/CustomerServiceChat/index-web.vue +86 -25
- package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +14 -4
- package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +19 -16
- package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +4 -4
- package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +10 -3
- package/components/CustomerServiceChat/message-list/index-web.vue +27 -14
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +6 -1
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +20 -13
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +1 -0
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-robot-welcome.vue +73 -29
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-transfer-with-desc.vue +51 -0
- package/components/CustomerServiceChat/message-toolbar-button/index.vue +13 -2
- package/components/CustomerServiceChat/style/web.scss +2 -0
- package/constant.ts +21 -1
- package/interface.ts +15 -2
- package/locales/en/aidesk.ts +4 -4
- package/locales/en/index.ts +2 -2
- package/locales/en/time.ts +2 -2
- package/locales/fil/index.ts +2 -2
- package/locales/fil/time.ts +2 -2
- package/locales/id/index.ts +2 -2
- package/locales/id/time.ts +2 -2
- package/locales/ja/index.ts +2 -2
- package/locales/ja/time.ts +2 -2
- package/locales/ms/index.ts +2 -2
- package/locales/ms/time.ts +2 -2
- package/locales/ru/index.ts +2 -2
- package/locales/ru/time.ts +2 -2
- package/locales/th/index.ts +2 -2
- package/locales/th/time.ts +2 -2
- package/locales/vi/index.ts +2 -2
- package/locales/vi/time.ts +2 -2
- package/locales/zh_cn/aidesk.ts +1 -1
- package/locales/zh_cn/index.ts +2 -2
- package/locales/zh_cn/time.ts +2 -2
- package/locales/zh_tw/aidesk.ts +1 -1
- package/locales/zh_tw/index.ts +2 -2
- package/locales/zh_tw/time.ts +2 -2
- package/package.json +2 -1
- package/server.ts +21 -3
- package/utils/index.ts +6 -13
- 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>
|
|
@@ -1,17 +1,49 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="['chat-header', !isPC && 'chat-header-h5']">
|
|
3
|
-
<div
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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="
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
|
|
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
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
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 && !
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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"
|