@tencentcloud/ai-desk-customer-vue 1.5.10 → 1.5.11
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 -1
- package/README.md +103 -77
- package/README_EN.md +873 -0
- package/components/CustomerServiceChat/chat-header/index-web.vue +41 -10
- package/components/CustomerServiceChat/feedback-modal/index.vue +1 -1
- package/components/CustomerServiceChat/index-web.vue +3 -1
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/marked.ts +1 -1
- package/components/CustomerServiceChat/message-toolbar-button/index.vue +11 -6
- package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-end-human-service.vue +16 -5
- package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-human-service.vue +8 -5
- package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-service-rating.vue +8 -5
- package/interface.ts +8 -0
- package/package.json +1 -1
- package/utils/utils.ts +13 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="['chat-header',
|
|
2
|
+
<div :class="['chat-header', isH5 && 'chat-header-h5']">
|
|
3
3
|
<div class="chat-header-name">
|
|
4
4
|
<div
|
|
5
|
-
v-if="
|
|
6
|
-
:class="['chat-header-back',
|
|
5
|
+
v-if="isH5 && props.canCloseChat"
|
|
6
|
+
:class="['chat-header-back', isH5 && 'chat-header-h5-back']"
|
|
7
7
|
@click="closeChat(currentConversation.conversationID)"
|
|
8
8
|
>
|
|
9
9
|
<Icon :file="backSVG" />
|
|
10
10
|
</div>
|
|
11
11
|
<div class="chat-header-container">
|
|
12
|
-
<Icon v-if="
|
|
13
|
-
<div :class="['chat-header-content',
|
|
12
|
+
<Icon v-if="showHeaderIcon" :width="headerIconWidth" :height="headerIconHeight" :file="headerIcon" />
|
|
13
|
+
<div :class="['chat-header-content', isH5 && 'chat-header-h5-content']">
|
|
14
14
|
{{ currentConversationName }}
|
|
15
15
|
</div>
|
|
16
16
|
</div>
|
|
@@ -60,16 +60,18 @@ import backSVG from '../../../assets/back.svg';
|
|
|
60
60
|
import arrowDown from '../../../assets/language_arrow_down.svg';
|
|
61
61
|
import customerAvatarMobile from '../../../assets/customer_avatar_mobile.png';
|
|
62
62
|
import languageSelectedIcon from '../../../assets/language_check.svg';
|
|
63
|
-
import {
|
|
63
|
+
import { isH5 } from '../../../utils/env';
|
|
64
64
|
import state from '../../../utils/state';
|
|
65
65
|
import { SUPPORTED_LANGUAGES } from '../../../constant';
|
|
66
|
-
|
|
66
|
+
import { IHeaderConfig } from '../../../interface';
|
|
67
|
+
const { ref, onMounted, onUnmounted, computed } = vue;
|
|
67
68
|
|
|
68
69
|
interface IProps {
|
|
69
70
|
canCloseChat?: boolean;
|
|
70
71
|
currentLang?: string;
|
|
71
72
|
enableMultilingual: number;
|
|
72
73
|
langList?: Array<string>;
|
|
74
|
+
headerConfig?: IHeaderConfig;
|
|
73
75
|
}
|
|
74
76
|
interface ILanguage {
|
|
75
77
|
name: string;
|
|
@@ -92,6 +94,31 @@ const props = withDefaults(defineProps<IProps>(), {
|
|
|
92
94
|
langList: () => [],
|
|
93
95
|
});
|
|
94
96
|
|
|
97
|
+
const showHeaderIcon = computed(() => {
|
|
98
|
+
if (isH5) {
|
|
99
|
+
if (props.headerConfig && props.headerConfig.showLogo === false) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const headerIcon = computed(() => {
|
|
108
|
+
return (props.headerConfig && props.headerConfig.logo) || customerAvatarMobile;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const headerIconWidth = computed(() => {
|
|
112
|
+
return (props.headerConfig && props.headerConfig.logoWidth) ?? '32px';
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const headerIconHeight = computed(() => {
|
|
116
|
+
if (props.headerConfig) {
|
|
117
|
+
return props.headerConfig.logoHeight || '32px';
|
|
118
|
+
}
|
|
119
|
+
return '23px';
|
|
120
|
+
});
|
|
121
|
+
|
|
95
122
|
if (props.enableMultilingual === 1) {
|
|
96
123
|
languageOptionList.value = props.langList
|
|
97
124
|
.map(key => ({
|
|
@@ -130,10 +157,14 @@ function setCurrentConversationName() {
|
|
|
130
157
|
if (!currentConversation.value) {
|
|
131
158
|
return;
|
|
132
159
|
}
|
|
133
|
-
if (
|
|
134
|
-
currentConversationName.value =
|
|
160
|
+
if (props.headerConfig && props.headerConfig.title) {
|
|
161
|
+
currentConversationName.value = props.headerConfig.title;
|
|
135
162
|
} else {
|
|
136
|
-
|
|
163
|
+
if (isH5) {
|
|
164
|
+
currentConversationName.value = TUITranslateService.t('AIDesk.Hi,我是') + currentConversation.value.getShowName();
|
|
165
|
+
} else {
|
|
166
|
+
currentConversationName.value = currentConversation.value.getShowName() || '';
|
|
167
|
+
}
|
|
137
168
|
}
|
|
138
169
|
}
|
|
139
170
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
:enableMultilingual="props.enableMultilingual"
|
|
14
14
|
:langList="languageForShowList"
|
|
15
15
|
:currentLang="currentLanguage"
|
|
16
|
+
:headerConfig="props.headerConfig"
|
|
16
17
|
@closeChat="closeChat"
|
|
17
18
|
@changeLanguage="changeLanguage"
|
|
18
19
|
/>
|
|
@@ -111,7 +112,7 @@ import MessageInput from './message-input/index-web.vue';
|
|
|
111
112
|
import MessageInputToolbar from './message-input-toolbar/index-web.vue';
|
|
112
113
|
import EmojiDialog from './message-input-toolbar/emoji-dialog-mobile/emoji-dialog-mobile.vue';
|
|
113
114
|
import { isH5, isPC } from '../../utils/env';
|
|
114
|
-
import { ToolbarButtonModel, ToolbarDisplayType, InputToolbarModel, QuickOrderModel } from '../../interface';
|
|
115
|
+
import { ToolbarButtonModel, ToolbarDisplayType, InputToolbarModel, QuickOrderModel, IHeaderConfig} from '../../interface';
|
|
115
116
|
import Log from '../../utils/logger';
|
|
116
117
|
import MessageToolbarButton from './message-toolbar-button/index.vue';
|
|
117
118
|
import TUILocales from '../../locales';
|
|
@@ -147,6 +148,7 @@ interface IProps {
|
|
|
147
148
|
enableFeedback?: number;
|
|
148
149
|
enableAINote?: number;
|
|
149
150
|
enableURLDetection?: number;
|
|
151
|
+
headerConfig?: IHeaderConfig;
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
const emits = defineEmits(['closeChat']);
|
|
@@ -31,7 +31,7 @@ export const marked = new Marked(
|
|
|
31
31
|
link(this: any, href: string | null, title: string | null, text: string) {
|
|
32
32
|
if (href) {
|
|
33
33
|
// 匹配以 http:// 或 https:// 开头,所有 URL 主体字符,遇到第一个非主体字符(如中文括号、空格、表情符号等)时停止
|
|
34
|
-
let ret = href.replace(/https?:\/\/[\w\-./?=&:#]+(?=[^\w\-./?=&:#]|$)/g, (matchedUrl) => {
|
|
34
|
+
let ret = href.replace(/&/g, '&').replace(/https?:\/\/[\w\-./?=&:#]+(?=[^\w\-./?=&:#]|$)/g, (matchedUrl) => {
|
|
35
35
|
let isURLInText = false;
|
|
36
36
|
if (matchedUrl !== href) {
|
|
37
37
|
// 如果 text 里包含 url,我们就用 matchedUrl 作为 a 标签的值;否则用 text 作为值
|
|
@@ -27,7 +27,7 @@ import { isH5 } from '../../../utils/env';
|
|
|
27
27
|
import { ToolbarButtonModel } from '../../../interface';
|
|
28
28
|
import Icon from '../../common/Icon.vue';
|
|
29
29
|
import { TOOLBAR_BUTTON_TYPE } from '../../../constant';
|
|
30
|
-
import { isEnabledMessageReadReceiptGlobal, openSafeUrl, getTo, isNonEmptyObject, transferToTaskFlow, transferToHuman } from '../../../utils/utils';
|
|
30
|
+
import { isEnabledMessageReadReceiptGlobal, openSafeUrl, getTo, isNonEmptyObject, transferToTaskFlow, transferToHuman, debounce } from '../../../utils/utils';
|
|
31
31
|
import ToolbarButtonHumanService from './toolbar-button-human-service.vue';
|
|
32
32
|
import ToolbarButtonServiceRating from './toolbar-button-service-rating.vue';
|
|
33
33
|
import ToolbarButtonEndHumanService from './toolbar-button-end-human-service.vue';
|
|
@@ -77,11 +77,14 @@ const onCanEndConversationUpdate = (data: {conversationID: string, value: boolea
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
const onClick = debounce((item:ToolbarButtonModel, index: number) => {
|
|
81
|
+
if (!currentConversation.value) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
81
84
|
if (item.type === 1 && item.content) {
|
|
82
85
|
TUIChatService.sendTextMessage({
|
|
83
|
-
to: getTo(currentConversation
|
|
84
|
-
conversationType: currentConversation
|
|
86
|
+
to: getTo(currentConversation.value),
|
|
87
|
+
conversationType: currentConversation.value.type,
|
|
85
88
|
payload: {
|
|
86
89
|
text: item.content
|
|
87
90
|
},
|
|
@@ -96,9 +99,9 @@ function onClick(item:ToolbarButtonModel, index: number) {
|
|
|
96
99
|
} else if (props.toolbarButtonList !== undefined && typeof props.toolbarButtonList[index].clickEvent === 'function') {
|
|
97
100
|
props.toolbarButtonList[index].clickEvent();
|
|
98
101
|
}
|
|
99
|
-
}
|
|
102
|
+
}, 300);
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
const shouldRender = (item: ToolbarButtonModel) => {
|
|
102
105
|
if (item.isEnabled === 1) {
|
|
103
106
|
return true;
|
|
104
107
|
} else if (item.isEnabled === 0) {
|
|
@@ -155,5 +158,7 @@ function shouldRender(item: ToolbarButtonModel) {
|
|
|
155
158
|
text-overflow: ellipsis;
|
|
156
159
|
max-width: 100px;
|
|
157
160
|
overflow: hidden;
|
|
161
|
+
min-width: 25px;
|
|
162
|
+
text-align: center;
|
|
158
163
|
}
|
|
159
164
|
</style>
|
package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-end-human-service.vue
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="['toolbar-button', isH5 ? 'toolbar-button-h5' : '']" @click="onClick">
|
|
2
|
+
<div :class="['toolbar-button', isH5 ? 'toolbar-button-h5' : '', isClicked ? 'end-conversation-button-clicked' : '']" @click="onClick">
|
|
3
3
|
<Icon v-if="props.icon" class="toolbar-button-icon" :file="props.icon" width="14px" height="14px"/>
|
|
4
4
|
<div class="toolbar-button-text">
|
|
5
5
|
{{ props.title || TUITranslateService.t('AIDesk.结束人工会话') }}
|
|
6
6
|
</div>
|
|
7
|
-
|
|
7
|
+
</div>
|
|
8
8
|
</template>
|
|
9
9
|
<script lang="ts" setup>
|
|
10
10
|
import vue from '../../../adapter-vue';
|
|
@@ -15,6 +15,7 @@ import { TUITranslateService, TUIChatService, IConversationModel, TUIStore, Stor
|
|
|
15
15
|
import { CUSTOM_MESSAGE_SRC } from '../../../constant';
|
|
16
16
|
import { isEnabledMessageReadReceiptGlobal, getTo } from '../../../utils/utils';
|
|
17
17
|
const currentConversation = ref<IConversationModel>();
|
|
18
|
+
const isClicked = ref(false);
|
|
18
19
|
interface IProps {
|
|
19
20
|
title?:string;
|
|
20
21
|
icon?:string | undefined;
|
|
@@ -42,9 +43,13 @@ const onCurrentConversationUpdate = (conversation: IConversationModel) => {
|
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
const onClick = () => {
|
|
46
|
+
if (isClicked.value || !currentConversation.value) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
isClicked.value = true;
|
|
45
50
|
TUIChatService.sendCustomMessage({
|
|
46
|
-
to: getTo(currentConversation
|
|
47
|
-
conversationType: currentConversation
|
|
51
|
+
to: getTo(currentConversation.value),
|
|
52
|
+
conversationType: currentConversation.value.type,
|
|
48
53
|
payload: {
|
|
49
54
|
data: JSON.stringify({
|
|
50
55
|
customerServicePlugin: 0,
|
|
@@ -56,4 +61,10 @@ const onClick = () => {
|
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
</script>
|
|
59
|
-
<style
|
|
64
|
+
<style>
|
|
65
|
+
.end-conversation-button-clicked {
|
|
66
|
+
background: #f0f2f9;
|
|
67
|
+
color: #97A3B4;
|
|
68
|
+
cursor: default;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-human-service.vue
CHANGED
|
@@ -12,7 +12,7 @@ const { ref, onMounted, onUnmounted } = vue;
|
|
|
12
12
|
import { isH5 } from '../../../utils/env';
|
|
13
13
|
import Icon from '../../common/Icon.vue';
|
|
14
14
|
import { TUITranslateService, TUIChatService, IConversationModel, StoreName, TUIStore } from '@tencentcloud/chat-uikit-engine';
|
|
15
|
-
import { isEnabledMessageReadReceiptGlobal, getTo } from '../../../utils/utils';
|
|
15
|
+
import { isEnabledMessageReadReceiptGlobal, getTo, debounce } from '../../../utils/utils';
|
|
16
16
|
const currentConversation = ref<IConversationModel>();
|
|
17
17
|
interface IProps {
|
|
18
18
|
title?:string;
|
|
@@ -40,16 +40,19 @@ const onCurrentConversationUpdate = (conversation: IConversationModel) => {
|
|
|
40
40
|
currentConversation.value = conversation;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
const onClick = () => {
|
|
43
|
+
const onClick = debounce(() => {
|
|
44
|
+
if (!currentConversation.value) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
44
47
|
TUIChatService.sendTextMessage({
|
|
45
|
-
to: getTo(currentConversation
|
|
46
|
-
conversationType: currentConversation
|
|
48
|
+
to: getTo(currentConversation.value),
|
|
49
|
+
conversationType: currentConversation.value.type,
|
|
47
50
|
payload: {
|
|
48
51
|
text: TUITranslateService.t('AIDesk.转人工服务')
|
|
49
52
|
},
|
|
50
53
|
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
51
54
|
});
|
|
52
|
-
}
|
|
55
|
+
}, 300);
|
|
53
56
|
|
|
54
57
|
</script>
|
|
55
58
|
<style></style>
|
package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-service-rating.vue
CHANGED
|
@@ -13,7 +13,7 @@ import { isH5 } from '../../../utils/env';
|
|
|
13
13
|
import Icon from '../../common/Icon.vue';
|
|
14
14
|
import { TUITranslateService, TUIChatService, IConversationModel, StoreName, TUIStore } from '@tencentcloud/chat-uikit-engine';
|
|
15
15
|
import { CUSTOM_MESSAGE_SRC } from '../../../constant';
|
|
16
|
-
import { isEnabledMessageReadReceiptGlobal, getTo } from '../../../utils/utils';
|
|
16
|
+
import { isEnabledMessageReadReceiptGlobal, getTo, debounce } from '../../../utils/utils';
|
|
17
17
|
const currentConversation = ref<IConversationModel>();
|
|
18
18
|
interface IProps {
|
|
19
19
|
title?:string;
|
|
@@ -41,10 +41,13 @@ const onCurrentConversationUpdate = (conversation: IConversationModel) => {
|
|
|
41
41
|
currentConversation.value = conversation;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
const onClick = () => {
|
|
44
|
+
const onClick = debounce(() => {
|
|
45
|
+
if (!currentConversation.value) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
45
48
|
TUIChatService.sendCustomMessage({
|
|
46
|
-
to: getTo(currentConversation
|
|
47
|
-
conversationType: currentConversation
|
|
49
|
+
to: getTo(currentConversation.value),
|
|
50
|
+
conversationType: currentConversation.value.type,
|
|
48
51
|
payload: {
|
|
49
52
|
data:JSON.stringify({
|
|
50
53
|
src: CUSTOM_MESSAGE_SRC.USER_SATISFACTION,
|
|
@@ -53,7 +56,7 @@ const onClick = () => {
|
|
|
53
56
|
},
|
|
54
57
|
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
55
58
|
},{ onlineUserOnly: true });
|
|
56
|
-
}
|
|
59
|
+
}, 300);
|
|
57
60
|
|
|
58
61
|
</script>
|
|
59
62
|
<style></style>
|
package/interface.ts
CHANGED
|
@@ -238,4 +238,12 @@ export interface ITransferToHumanModel {
|
|
|
238
238
|
groupID?: number;
|
|
239
239
|
specificMemberList?: Array<string>;
|
|
240
240
|
description?: string;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface IHeaderConfig {
|
|
244
|
+
title?: string;
|
|
245
|
+
logo?: string;
|
|
246
|
+
showLogo?: boolean;
|
|
247
|
+
logoWidth?: string;
|
|
248
|
+
logoHeight?: string;
|
|
241
249
|
}
|
package/package.json
CHANGED
package/utils/utils.ts
CHANGED
|
@@ -363,4 +363,17 @@ export function validateUserID(userID: string) {
|
|
|
363
363
|
|
|
364
364
|
export function updateCustomStore(key: string, data: {conversationID: string, value: boolean}) {
|
|
365
365
|
TUIStore.update(StoreName.CUSTOM, key, data);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export function debounce(func, delay) {
|
|
369
|
+
let timeoutID;
|
|
370
|
+
return function (this: any, ...args) {
|
|
371
|
+
const context = this;
|
|
372
|
+
if (timeoutID) {
|
|
373
|
+
clearTimeout(timeoutID);
|
|
374
|
+
}
|
|
375
|
+
timeoutID = setTimeout(() => {
|
|
376
|
+
func.apply(context, args);
|
|
377
|
+
}, delay);
|
|
378
|
+
}
|
|
366
379
|
}
|