@tencentcloud/ai-desk-customer-vue 1.6.6 → 1.6.7
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 +9 -0
- package/components/CustomerServiceChat/customer-queue-page/index.vue +7 -16
- package/components/CustomerServiceChat/index-web.vue +9 -3
- package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +1 -1
- package/components/CustomerServiceChat/message-input-toolbar/rating-tool/index.vue +1 -1
- package/components/CustomerServiceChat/message-list/index-web.vue +1 -1
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-stream.vue +3 -0
- package/components/CustomerServiceChat/message-list/message-elements/message-thinking.vue +1 -1
- package/components/CustomerServiceChat/message-list/message-elements/simple-message-list/index.vue +0 -1
- package/components/CustomerServiceChat/message-toolbar-button/index.vue +2 -2
- package/locales/en/aidesk.ts +1 -1
- package/package.json +1 -3
- package/server.ts +12 -2
- package/utils/utils.ts +30 -2
- package/assets/video-play.png +0 -0
- package/components/CustomerServiceChat/message-list/message-elements/video-play.vue +0 -59
package/CHANGELOG.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
</div>
|
|
16
16
|
</div>
|
|
17
17
|
<div class="customer-queue-end-button">
|
|
18
|
-
<div :class="['customer-queue-end-button-content', isClicked ? 'button-clicked' : '']" @click="
|
|
18
|
+
<div :class="['customer-queue-end-button-content', isClicked ? 'button-clicked' : '']" @click="leaveQueue">
|
|
19
19
|
{{ TUITranslateService.t("AIDesk.结束排队") }}
|
|
20
20
|
</div>
|
|
21
21
|
</div>
|
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
<script lang="ts" setup>
|
|
25
25
|
import vue from '../../../adapter-vue';
|
|
26
26
|
const { ref, onMounted, onUnmounted } = vue;
|
|
27
|
-
import { IConversationModel, StoreName,
|
|
28
|
-
import {
|
|
29
|
-
import { getTo, isEnabledMessageReadReceiptGlobal } from '../../../utils/utils';
|
|
27
|
+
import { IConversationModel, StoreName, TUIStore, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
|
28
|
+
import { getTo, leaveQueuing } from '../../../utils/utils';
|
|
30
29
|
const currentConversation = ref<IConversationModel>();
|
|
31
30
|
const isClicked = ref(false);
|
|
32
31
|
const props = defineProps({
|
|
@@ -35,6 +34,7 @@ const props = defineProps({
|
|
|
35
34
|
default: 0
|
|
36
35
|
},
|
|
37
36
|
});
|
|
37
|
+
const emits = defineEmits(['closeQueuePage']);
|
|
38
38
|
onMounted(() => {
|
|
39
39
|
TUIStore.watch(StoreName.CONV, {
|
|
40
40
|
currentConversation: onCurrentConversationUpdate,
|
|
@@ -51,22 +51,13 @@ const onCurrentConversationUpdate = (conversation: IConversationModel) => {
|
|
|
51
51
|
currentConversation.value = conversation;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
const
|
|
54
|
+
const leaveQueue = () => {
|
|
55
55
|
if (isClicked.value) {
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
isClicked.value = true;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
conversationType: currentConversation.value.type,
|
|
62
|
-
payload: {
|
|
63
|
-
data: JSON.stringify({
|
|
64
|
-
customerServicePlugin: 0,
|
|
65
|
-
src: CUSTOM_MESSAGE_SRC.USER_END_CONVERSATION,
|
|
66
|
-
}),
|
|
67
|
-
},
|
|
68
|
-
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
69
|
-
},{ onlineUserOnly:true });
|
|
59
|
+
leaveQueuing(getTo(currentConversation.value));
|
|
60
|
+
emits('closeQueuePage');
|
|
70
61
|
}
|
|
71
62
|
</script>
|
|
72
63
|
<style lang="scss">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="['tui-chat', !isPC && 'tui-chat-h5']" :key="currentLanguage">
|
|
3
3
|
<div
|
|
4
|
-
v-if="currentConversationID && (!props.showQueuePage || queueNumber < 0 || queueNumber === undefined)"
|
|
4
|
+
v-if="currentConversationID && (!props.showQueuePage || queueNumber < 0 || queueNumber === undefined || hasLeftQueue)"
|
|
5
5
|
:class="['tui-chat', !isPC && 'tui-chat-h5']"
|
|
6
6
|
>
|
|
7
7
|
<ChatHeader
|
|
@@ -94,8 +94,8 @@
|
|
|
94
94
|
@close="() => { showFeedbackModal = false }"
|
|
95
95
|
/>
|
|
96
96
|
</div>
|
|
97
|
-
<div v-if="props.showQueuePage && queueNumber >= 0" :class="['tui-chat', !isPC && 'tui-chat-h5']">
|
|
98
|
-
<CustomerQueuePage :queueNumber="queueNumber"/>
|
|
97
|
+
<div v-if="props.showQueuePage && queueNumber >= 0 && !hasLeftQueue" :class="['tui-chat', !isPC && 'tui-chat-h5']">
|
|
98
|
+
<CustomerQueuePage :queueNumber="queueNumber" @closeQueuePage="closeQueuePage"/>
|
|
99
99
|
</div>
|
|
100
100
|
</div>
|
|
101
101
|
</template>
|
|
@@ -178,6 +178,7 @@ const languageForShowList = ref<Array<string>>([]);
|
|
|
178
178
|
const feedbackModalRef = ref();
|
|
179
179
|
const showFeedbackModal = ref(false);
|
|
180
180
|
const queueNumber = ref(-1);
|
|
181
|
+
const hasLeftQueue = ref(false);
|
|
181
182
|
let timezone = '';
|
|
182
183
|
let countryID = '';
|
|
183
184
|
const props = withDefaults(defineProps<IProps>(), {
|
|
@@ -528,8 +529,13 @@ function onDislike(messageInfo: Object) {
|
|
|
528
529
|
function onIsQueuingUpdate(data: {conversationID: string, value: number}) {
|
|
529
530
|
if (data && data.conversationID === currentConversationID.value) {
|
|
530
531
|
queueNumber.value = data.value;
|
|
532
|
+
hasLeftQueue.value = false;
|
|
531
533
|
}
|
|
532
534
|
}
|
|
535
|
+
|
|
536
|
+
function closeQueuePage() {
|
|
537
|
+
hasLeftQueue.value = true;
|
|
538
|
+
}
|
|
533
539
|
</script>
|
|
534
540
|
|
|
535
541
|
<style scoped lang="scss" src="./style/index.scss">
|
|
@@ -361,7 +361,7 @@ function handlePasteText(e: ClipboardEvent) {
|
|
|
361
361
|
const text = e.clipboardData?.getData('text/plain') || '';
|
|
362
362
|
// if paste html in pc, paste by tiptap editor default
|
|
363
363
|
// if paste text in pc or mobile, parse text to html to render emoji
|
|
364
|
-
if (!html) {
|
|
364
|
+
if (isH5 || !html) {
|
|
365
365
|
const renderArray = parseTextToRenderArray(text);
|
|
366
366
|
insertEditorContent(renderArray);
|
|
367
367
|
}
|
|
@@ -24,7 +24,7 @@ import ratingToolIcon from '../../../../assets/rating_tool_icon.svg'
|
|
|
24
24
|
import ratingToolIconH5 from '../../../../assets/rating_tool_icon_h5.svg';
|
|
25
25
|
import { getTo } from '../../../../utils/utils';
|
|
26
26
|
import { CUSTOM_MESSAGE_SRC } from '../../../../constant';
|
|
27
|
-
const { ref
|
|
27
|
+
const { ref } = vue;
|
|
28
28
|
|
|
29
29
|
const props = defineProps({
|
|
30
30
|
isH5ToolShow:{
|
|
@@ -208,7 +208,6 @@ import {
|
|
|
208
208
|
getBoundingClientRect,
|
|
209
209
|
getScrollInfo,
|
|
210
210
|
} from '@tencentcloud/universal-api';
|
|
211
|
-
import { throttle } from 'lodash';
|
|
212
211
|
import MessageText from './message-elements/message-text.vue';
|
|
213
212
|
import MessageImage from './message-elements/message-image-web.vue';
|
|
214
213
|
import MessageAudio from './message-elements/message-audio-web.vue';
|
|
@@ -237,6 +236,7 @@ import {
|
|
|
237
236
|
deepCopy,
|
|
238
237
|
isNonEmptyObject,
|
|
239
238
|
updateCustomStore,
|
|
239
|
+
throttle
|
|
240
240
|
} from '../../../utils/utils';
|
|
241
241
|
import { isMessageInvisible, isThinkingMessage, isThinkingMessageOverTime, JSONToObject, isTransferMessageWithoutDesc } from '../../../utils/index';
|
|
242
242
|
import { isCustomerConversation } from '../../../index';
|
|
@@ -85,6 +85,9 @@ watch(() => props.payload, (newValue: string, oldValue: string) => {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const _payloadObject = JSONToObject(props.payload);
|
|
88
|
+
if (!_payloadObject.chunks) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
88
91
|
chunks.value = Array.isArray(_payloadObject.chunks) ? _payloadObject.chunks.join('') : _payloadObject.chunks;
|
|
89
92
|
isFinished.value = _payloadObject.isFinished === 1;
|
|
90
93
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import vue from '../../../../adapter-vue';
|
|
12
12
|
import Icon from '../../../common/Icon.vue';
|
|
13
13
|
import loading_message from '../../../../assets/loading_message.svg';
|
|
14
|
-
const { ref,
|
|
14
|
+
const { ref, onMounted, onUnmounted} = vue;
|
|
15
15
|
export default {
|
|
16
16
|
components:{
|
|
17
17
|
Icon
|
package/components/CustomerServiceChat/message-list/message-elements/simple-message-list/index.vue
CHANGED
|
@@ -158,7 +158,6 @@ import TUIChatEngine, {
|
|
|
158
158
|
TUITranslateService,
|
|
159
159
|
} from '@tencentcloud/chat-uikit-engine';
|
|
160
160
|
import addIcon from '../../../../../assets/back.svg';
|
|
161
|
-
import playIcon from '../../../../../assets/video-play.png';
|
|
162
161
|
import Icon from '../../../../common/Icon.vue';
|
|
163
162
|
import MessageContainer from './message-container.vue';
|
|
164
163
|
import MessageRecord from '../message-record/index.vue';
|
|
@@ -44,7 +44,7 @@ import { isH5 } from '../../../utils/env';
|
|
|
44
44
|
import { ToolbarButtonModel } from '../../../interface';
|
|
45
45
|
import Icon from '../../common/Icon.vue';
|
|
46
46
|
import { TOOLBAR_BUTTON_TYPE } from '../../../constant';
|
|
47
|
-
import { isEnabledMessageReadReceiptGlobal, openSafeUrl, getTo, isNonEmptyObject, transferToTaskFlow, transferToHuman, debounce,
|
|
47
|
+
import { isEnabledMessageReadReceiptGlobal, openSafeUrl, getTo, isNonEmptyObject, transferToTaskFlow, transferToHuman, debounce, leaveQueuing } from '../../../utils/utils';
|
|
48
48
|
import ToolbarButtonHumanService from './toolbar-button-human-service.vue';
|
|
49
49
|
import ToolbarButtonServiceRating from './toolbar-button-service-rating.vue';
|
|
50
50
|
import ToolbarButtonEndHumanService from './toolbar-button-end-human-service.vue';
|
|
@@ -190,7 +190,7 @@ const onClick = debounce((item:ToolbarButtonModel, index: number) => {
|
|
|
190
190
|
} else if (item.type === 4 && isNonEmptyObject(item.content)) {
|
|
191
191
|
transferToHuman(getTo(currentConversation.value), item.content.groupID, item.content.specificMemberList, item.content.description);
|
|
192
192
|
} else if (item.type === 5 && queueNumber.value >= 0) {
|
|
193
|
-
|
|
193
|
+
leaveQueuing(getTo(currentConversation.value));
|
|
194
194
|
} else if (props.toolbarButtonList !== undefined && typeof props.toolbarButtonList[index].clickEvent === 'function') {
|
|
195
195
|
props.toolbarButtonList[index].clickEvent();
|
|
196
196
|
}
|
package/locales/en/aidesk.ts
CHANGED
|
@@ -38,6 +38,6 @@ const AIDesk = {
|
|
|
38
38
|
"排队等待中": "In Queue",
|
|
39
39
|
"排队等待话术": "We apologize for the wait. All agents are currently assisting other customers. You will be connected shortly. Thank you for your patience.",
|
|
40
40
|
"当前前方排队人数": "Queue Position",
|
|
41
|
-
"结束排队": "
|
|
41
|
+
"结束排队": "Leave Queue"
|
|
42
42
|
}
|
|
43
43
|
export default AIDesk;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tencentcloud/ai-desk-customer-vue",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"description": "Vue2/Vue3 UIKit for AI Desk",
|
|
5
5
|
"main": "index",
|
|
6
6
|
"keywords": [
|
|
@@ -31,11 +31,9 @@
|
|
|
31
31
|
"@tiptap/extension-text": "2.0.0-beta.220",
|
|
32
32
|
"@tiptap/pm": "2.0.0-beta.220",
|
|
33
33
|
"@tiptap/suggestion": "2.0.0-beta.220",
|
|
34
|
-
"@types/lodash": "^4.14.202",
|
|
35
34
|
"countries-and-timezones": "^3.8.0",
|
|
36
35
|
"dayjs": "^1.11.10",
|
|
37
36
|
"js-audio-recorder": "^1.0.7",
|
|
38
|
-
"lodash": "^4.17.21",
|
|
39
37
|
"marked": "^6.0.0",
|
|
40
38
|
"mp-html": "^2.5.0",
|
|
41
39
|
"vue-clipboard3": "2.0.0"
|
package/server.ts
CHANGED
|
@@ -29,6 +29,7 @@ interface IInitWithProfile {
|
|
|
29
29
|
nickName?: string,
|
|
30
30
|
avatar?: string,
|
|
31
31
|
customerServiceID?: string,
|
|
32
|
+
clientCustomData?: string,
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
interface IProfile {
|
|
@@ -45,6 +46,7 @@ export default class TUICustomerServer {
|
|
|
45
46
|
private myProfile: IProfile;
|
|
46
47
|
private paramsForActiveAgain: any;
|
|
47
48
|
private loginFailToasts: any[];
|
|
49
|
+
private clientCustomData: string;
|
|
48
50
|
constructor() {
|
|
49
51
|
TUICore.registerService(TUIConstants.TUICustomerServicePlugin.SERVICE.NAME, this);
|
|
50
52
|
TUICore.registerExtension(TUIConstants.TUIContact.EXTENSION.CONTACT_LIST.EXT_ID, this);
|
|
@@ -54,6 +56,7 @@ export default class TUICustomerServer {
|
|
|
54
56
|
this.isLoggedIn = false;
|
|
55
57
|
this.loggedInUserID = '';
|
|
56
58
|
this.myProfile = {};
|
|
59
|
+
this.clientCustomData = '';
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
static getInstance(): TUICustomerServer {
|
|
@@ -138,7 +141,7 @@ export default class TUICustomerServer {
|
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
public async initWithProfile(options: IInitWithProfile) {
|
|
141
|
-
const { SDKAppID, userID, userSig, nickName, avatar, customerServiceID } = options;
|
|
144
|
+
const { SDKAppID, userID, userSig, nickName, avatar, customerServiceID, clientCustomData } = options;
|
|
142
145
|
Log.l(`TUICustomerServer.initWithProfile version:${version}`);
|
|
143
146
|
if (nickName) {
|
|
144
147
|
// chat 个人资料的昵称是 nick
|
|
@@ -155,6 +158,12 @@ export default class TUICustomerServer {
|
|
|
155
158
|
this.customerServiceIDList.push(this.currentCustomerServiceID);
|
|
156
159
|
}
|
|
157
160
|
}
|
|
161
|
+
|
|
162
|
+
if (clientCustomData) {
|
|
163
|
+
this.clientCustomData = clientCustomData;
|
|
164
|
+
} else {
|
|
165
|
+
this.clientCustomData = '';
|
|
166
|
+
}
|
|
158
167
|
return this.init(SDKAppID, userID, userSig);
|
|
159
168
|
}
|
|
160
169
|
|
|
@@ -276,7 +285,7 @@ export default class TUICustomerServer {
|
|
|
276
285
|
|
|
277
286
|
// 激活会话服务流
|
|
278
287
|
private activeServiceFlow(params: any) {
|
|
279
|
-
Log.i(`TUICustomerServer.activeServiceFlow params: language:${params.robotLang} country:${params.country} timezone:${params.timezone}`);
|
|
288
|
+
Log.i(`TUICustomerServer.activeServiceFlow params: language:${params.robotLang} country:${params.country} timezone:${params.timezone} clientCustomData:${this.clientCustomData}`);
|
|
280
289
|
this.paramsForActiveAgain = { ...params };
|
|
281
290
|
TUIChatService.sendCustomMessage({
|
|
282
291
|
to: params.conversationID.slice(3),
|
|
@@ -289,6 +298,7 @@ export default class TUICustomerServer {
|
|
|
289
298
|
language: params.robotLang,
|
|
290
299
|
country: params.country,
|
|
291
300
|
timezone: params.timezone,
|
|
301
|
+
clientCustomData: this.clientCustomData,
|
|
292
302
|
}
|
|
293
303
|
}),
|
|
294
304
|
},
|
package/utils/utils.ts
CHANGED
|
@@ -378,7 +378,7 @@ export function debounce(func, delay) {
|
|
|
378
378
|
}
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
-
export function
|
|
381
|
+
export function leaveQueuing(conversationID: string) {
|
|
382
382
|
TUIChatService.sendCustomMessage({
|
|
383
383
|
to: conversationID,
|
|
384
384
|
conversationType: TUIChatEngine.TYPES.CONV_C2C,
|
|
@@ -390,4 +390,32 @@ export function endQueuing(conversationID: string) {
|
|
|
390
390
|
},
|
|
391
391
|
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
392
392
|
},{ onlineUserOnly: true });
|
|
393
|
-
};
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
export function throttle(
|
|
396
|
+
func,
|
|
397
|
+
wait = 200,
|
|
398
|
+
options?: { leading?: boolean;}
|
|
399
|
+
) {
|
|
400
|
+
let last = 0;
|
|
401
|
+
let timeout;
|
|
402
|
+
const leading = options?.leading !== undefined ? options.leading : true;
|
|
403
|
+
|
|
404
|
+
return function (this: any, ...args: any[]) {
|
|
405
|
+
const now = Date.now();
|
|
406
|
+
if (!last && !leading) {
|
|
407
|
+
last = now;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const remaining = wait - (now - last);
|
|
411
|
+
|
|
412
|
+
if (remaining <= 0) {
|
|
413
|
+
if (timeout) {
|
|
414
|
+
clearTimeout(timeout);
|
|
415
|
+
timeout = null;
|
|
416
|
+
}
|
|
417
|
+
last = now;
|
|
418
|
+
func.apply(this, args);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
}
|
package/assets/video-play.png
DELETED
|
Binary file
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="dialog-video">
|
|
3
|
-
<video
|
|
4
|
-
v-if="isShow"
|
|
5
|
-
id="videoEle"
|
|
6
|
-
class="video-box"
|
|
7
|
-
:src="videoData"
|
|
8
|
-
controls
|
|
9
|
-
autoplay
|
|
10
|
-
/>
|
|
11
|
-
</div>
|
|
12
|
-
</template>
|
|
13
|
-
|
|
14
|
-
<script lang="ts" setup>
|
|
15
|
-
import vue from '../../TencentCloudCustomer/tui-customer-service-plugin-test/adapter-vue';
|
|
16
|
-
import { TUIGlobal } from '@tencentcloud/universal-api';
|
|
17
|
-
import { onLoad, onReady } from '@dcloudio/uni-app';
|
|
18
|
-
const { ref } = vue;
|
|
19
|
-
|
|
20
|
-
const videoData = ref();
|
|
21
|
-
const isShow = ref(false);
|
|
22
|
-
const videoContext = ref();
|
|
23
|
-
onLoad((option: any) => {
|
|
24
|
-
const decodedUrl = decodeURIComponent(option?.videoUrl);
|
|
25
|
-
videoData.value = decodedUrl;
|
|
26
|
-
isShow.value = true;
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
onReady(() => {
|
|
30
|
-
isShow.value = true;
|
|
31
|
-
videoContext.value = TUIGlobal.createVideoContext('videoEle');
|
|
32
|
-
});
|
|
33
|
-
</script>
|
|
34
|
-
<style lang="scss" scoped>
|
|
35
|
-
.dialog-video {
|
|
36
|
-
position: fixed;
|
|
37
|
-
z-index: 999;
|
|
38
|
-
width: 100vw;
|
|
39
|
-
height: 100vh;
|
|
40
|
-
background: rgba(#000, 0.6);
|
|
41
|
-
top: 0;
|
|
42
|
-
left: 0;
|
|
43
|
-
right: 0;
|
|
44
|
-
bottom: 0;
|
|
45
|
-
display: flex;
|
|
46
|
-
justify-content: center;
|
|
47
|
-
align-items: center;
|
|
48
|
-
|
|
49
|
-
.video-box {
|
|
50
|
-
position: absolute;
|
|
51
|
-
width: 100vw;
|
|
52
|
-
height: 100vh;
|
|
53
|
-
top: 0;
|
|
54
|
-
left: 0;
|
|
55
|
-
right: 0;
|
|
56
|
-
bottom: 0;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
</style>
|