@tencentcloud/ai-desk-customer-vue 1.3.0 → 1.4.0
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 +11 -4
- package/assets/customer_avatar.png +0 -0
- package/assets/face.svg +10 -0
- package/assets/files.svg +5 -0
- package/assets/image.svg +8 -0
- package/assets/rating_tool_icon.svg +5 -0
- package/assets/rating_tool_icon_h5.svg +1 -0
- package/assets/video.svg +8 -0
- package/assets/video_h5.svg +1 -0
- package/components/CustomerServiceChat/chat-header/index-web.vue +16 -14
- package/components/CustomerServiceChat/index-web.vue +69 -13
- package/components/CustomerServiceChat/message-input/index-web.vue +31 -5
- package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +24 -0
- package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/emoji-picker-dialog.vue +1 -1
- package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/index.vue +1 -1
- package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +6 -8
- package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +11 -16
- package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +61 -18
- package/components/CustomerServiceChat/message-input-toolbar/rating-tool/index.vue +72 -0
- package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/h5.scss +10 -1
- package/components/CustomerServiceChat/message-input-toolbar/user-define-input-tool.vue +80 -0
- package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +9 -14
- package/components/CustomerServiceChat/message-list/index-web.vue +33 -6
- package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +53 -6
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-branch.vue +5 -5
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +8 -3
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +20 -6
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +2 -2
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-order.vue +1 -2
- package/components/CustomerServiceChat/message-list/message-elements/message-file.vue +1 -1
- package/components/CustomerServiceChat/message-list/scroll-button/index.vue +3 -3
- package/components/CustomerServiceChat/message-list/style/web.scss +2 -1
- package/components/CustomerServiceChat/message-toolbar-button/index.vue +111 -42
- package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-end-human-service.vue +59 -0
- package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-human-service.vue +55 -0
- package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-service-rating.vue +59 -0
- package/constant.ts +15 -0
- package/interface.ts +35 -5
- package/locales/en/aidesk.ts +4 -3
- package/locales/fil/aidesk.ts +2 -1
- package/locales/id/aidesk.ts +3 -2
- package/locales/ja/aidesk.ts +1 -0
- package/locales/ms/aidesk.ts +1 -0
- package/locales/ru/aidesk.ts +1 -0
- package/locales/th/aidesk.ts +1 -0
- package/locales/vi/aidesk.ts +1 -0
- package/locales/zh_cn/aidesk.ts +1 -0
- package/locales/zh_tw/aidesk.ts +1 -0
- package/package.json +1 -1
- package/server.ts +5 -1
- package/utils/state.js +30 -0
- package/utils/utils.ts +48 -1
- package/assets/face.png +0 -0
- package/assets/files.png +0 -0
- package/assets/image.png +0 -0
- package/assets/video.png +0 -0
|
@@ -11,20 +11,48 @@
|
|
|
11
11
|
!isPC && 'message-input-toolbar-h5-list',
|
|
12
12
|
]"
|
|
13
13
|
>
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
<template v-if="props.inputToolbarList === undefined">
|
|
15
|
+
<EmojiPicker
|
|
16
|
+
v-if="isPC"
|
|
17
|
+
ref="emojiPickerRef"
|
|
18
|
+
@insertEmoji="insertEmoji"
|
|
19
|
+
@dialogShowInH5="dialogShowInH5"
|
|
20
|
+
@dialogCloseInH5="dialogCloseInH5"
|
|
21
|
+
@changeToolbarDisplayType="
|
|
22
|
+
(type) => emits('changeToolbarDisplayType', type)
|
|
23
|
+
"
|
|
24
|
+
:isH5EmojiShow="isH5EmojiShow"
|
|
25
|
+
@sendMessage="sendMessage"
|
|
26
|
+
/>
|
|
27
|
+
<ImageUpload imageSourceType="album" :isH5ToolShow="isH5ToolShow"/>
|
|
28
|
+
<FileUpload :isH5ToolShow="isH5ToolShow"/>
|
|
29
|
+
</template>
|
|
30
|
+
<template v-else v-for="(item, index) in props.inputToolbarList" :key="index">
|
|
31
|
+
<EmojiPicker
|
|
32
|
+
v-if="isPC && item.presetId === INPUT_TOOLBAR_TYPE.EMOJI && item.isEnabled === 1"
|
|
33
|
+
ref="emojiPickerRef"
|
|
34
|
+
@insertEmoji="insertEmoji"
|
|
35
|
+
@dialogShowInH5="dialogShowInH5"
|
|
36
|
+
@dialogCloseInH5="dialogCloseInH5"
|
|
37
|
+
@changeToolbarDisplayType="
|
|
38
|
+
(type) => emits('changeToolbarDisplayType', type)
|
|
39
|
+
"
|
|
40
|
+
:isH5EmojiShow="isH5EmojiShow"
|
|
41
|
+
@sendMessage="sendMessage"
|
|
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"/>
|
|
47
|
+
<UserDefineTool
|
|
48
|
+
v-else-if="item.isEnabled === 1 && item.isPreset === 0 && item.presetId !== INPUT_TOOLBAR_TYPE.EMOJI"
|
|
49
|
+
:isH5ToolShow="isH5ToolShow"
|
|
50
|
+
:title="item.title"
|
|
51
|
+
:icon="item.icon"
|
|
52
|
+
:type="item.type"
|
|
53
|
+
:content="item.content"
|
|
54
|
+
/>
|
|
55
|
+
</template>
|
|
28
56
|
</div>
|
|
29
57
|
<div
|
|
30
58
|
v-if="isH5"
|
|
@@ -44,14 +72,18 @@ import EmojiPicker from './emoji-picker/index.vue';
|
|
|
44
72
|
import ImageUpload from './image-upload/index.vue';
|
|
45
73
|
import FileUpload from './file-upload/index.vue';
|
|
46
74
|
import VideoUpload from './video-upload/index.vue';
|
|
75
|
+
import RatingTool from './rating-tool/index.vue';
|
|
47
76
|
import { isPC, isH5 } from '../../../utils/env';
|
|
48
|
-
import { ToolbarDisplayType } from '../../../interface';
|
|
77
|
+
import { ToolbarDisplayType, InputToolbarModel } from '../../../interface';
|
|
78
|
+
import UserDefineTool from './user-define-input-tool.vue'
|
|
79
|
+
import { INPUT_TOOLBAR_TYPE } from '../../../constant';
|
|
49
80
|
const { ref, onMounted, onUnmounted, watch } = vue;
|
|
50
81
|
|
|
51
82
|
interface IProps {
|
|
52
83
|
displayType: ToolbarDisplayType;
|
|
53
|
-
isH5EmojiShow?:boolean;
|
|
54
|
-
isH5ToolShow?:boolean;
|
|
84
|
+
isH5EmojiShow?: boolean;
|
|
85
|
+
isH5ToolShow?: boolean;
|
|
86
|
+
inputToolbarList?: InputToolbarModel[];
|
|
55
87
|
}
|
|
56
88
|
interface IEmits {
|
|
57
89
|
(e: 'scrollToLatestMessage'): void;
|
|
@@ -70,17 +102,24 @@ const h5Dialog = ref<HTMLElement>();
|
|
|
70
102
|
const currentConversation = ref<IConversationModel>();
|
|
71
103
|
const isGroup = ref<boolean>(false);
|
|
72
104
|
const emojiPickerRef = ref<InstanceType<typeof EmojiPicker>>();
|
|
105
|
+
const isInHumanService = ref(false);
|
|
73
106
|
|
|
74
107
|
onMounted(() => {
|
|
75
108
|
TUIStore.watch(StoreName.CONV, {
|
|
76
109
|
currentConversation: onCurrentConversationUpdate,
|
|
77
110
|
});
|
|
111
|
+
TUIStore.watch(StoreName.CUSTOM, {
|
|
112
|
+
isInHumanService: onInHumanServiceUpdate,
|
|
113
|
+
});
|
|
78
114
|
});
|
|
79
115
|
|
|
80
116
|
onUnmounted(() => {
|
|
81
117
|
TUIStore.unwatch(StoreName.CONV, {
|
|
82
118
|
currentConversation: onCurrentConversationUpdate,
|
|
83
119
|
});
|
|
120
|
+
TUIStore.unwatch(StoreName.CUSTOM, {
|
|
121
|
+
isInHumanService: onInHumanServiceUpdate,
|
|
122
|
+
});
|
|
84
123
|
});
|
|
85
124
|
|
|
86
125
|
watch(
|
|
@@ -99,7 +138,7 @@ watch(
|
|
|
99
138
|
(newValue) => {
|
|
100
139
|
if (newValue === false) {
|
|
101
140
|
emojiPickerRef.value?.closeEmojiPicker();
|
|
102
|
-
}
|
|
141
|
+
}
|
|
103
142
|
},
|
|
104
143
|
);
|
|
105
144
|
|
|
@@ -109,6 +148,10 @@ const onCurrentConversationUpdate = (conversation: IConversationModel) => {
|
|
|
109
148
|
= currentConversation?.value?.type === TUIChatEngine.TYPES.CONV_GROUP;
|
|
110
149
|
};
|
|
111
150
|
|
|
151
|
+
const onInHumanServiceUpdate = (value: boolean) => {
|
|
152
|
+
isInHumanService.value = value;
|
|
153
|
+
};
|
|
154
|
+
|
|
112
155
|
const insertEmoji = (emojiObj: object) => {
|
|
113
156
|
emits('insertEmoji', emojiObj);
|
|
114
157
|
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ToolbarItemContainer
|
|
3
|
+
:iconFile="getIcon()"
|
|
4
|
+
:title="props.title"
|
|
5
|
+
:iconWidth="isPC ? '20px' : '25px'"
|
|
6
|
+
:iconHeight="isPC ? '20px' : '25px'"
|
|
7
|
+
:needDialog="false"
|
|
8
|
+
@onIconClick="onIconClick"
|
|
9
|
+
:isH5ToolShow="isH5ToolShow"
|
|
10
|
+
/>
|
|
11
|
+
</template>
|
|
12
|
+
<script lang="ts" setup>
|
|
13
|
+
import {
|
|
14
|
+
TUIChatService,
|
|
15
|
+
TUIStore,
|
|
16
|
+
StoreName,
|
|
17
|
+
IConversationModel,
|
|
18
|
+
TUITranslateService,
|
|
19
|
+
} from '@tencentcloud/chat-uikit-engine';
|
|
20
|
+
import vue from '../../../../adapter-vue';
|
|
21
|
+
import { isPC, isH5 } from '../../../../utils/env';
|
|
22
|
+
import ToolbarItemContainer from '../toolbar-item-container/index.vue';
|
|
23
|
+
import ratingToolIcon from '../../../../assets/rating_tool_icon.svg'
|
|
24
|
+
import ratingToolIconH5 from '../../../../assets/rating_tool_icon_h5.svg';
|
|
25
|
+
import { getTo } from '../../../../utils/utils';
|
|
26
|
+
import { CUSTOM_MESSAGE_SRC } from '../../../../constant';
|
|
27
|
+
const { ref, onMounted, onUnmounted } = vue;
|
|
28
|
+
|
|
29
|
+
const props = defineProps({
|
|
30
|
+
isH5ToolShow:{
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: false,
|
|
33
|
+
},
|
|
34
|
+
title: {
|
|
35
|
+
type: String,
|
|
36
|
+
default: TUITranslateService.t('AIDesk.提交评价')
|
|
37
|
+
},
|
|
38
|
+
icon: {
|
|
39
|
+
type: String,
|
|
40
|
+
default: '',
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const currentConversation = ref<IConversationModel>();
|
|
45
|
+
|
|
46
|
+
TUIStore.watch(StoreName.CONV, {
|
|
47
|
+
currentConversation: (conversation: IConversationModel) => {
|
|
48
|
+
currentConversation.value = conversation;
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const getIcon = (): string => {
|
|
53
|
+
return isH5 ? ratingToolIconH5 : ratingToolIcon;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const onIconClick = () => {
|
|
57
|
+
TUIChatService.sendCustomMessage({
|
|
58
|
+
to: getTo(currentConversation?.value),
|
|
59
|
+
conversationType: currentConversation?.value?.type,
|
|
60
|
+
payload:{
|
|
61
|
+
data:JSON.stringify({
|
|
62
|
+
src: CUSTOM_MESSAGE_SRC.USER_SATISFACTION,
|
|
63
|
+
customerServicePlugin: 0,
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
},{ onlineUserOnly: true })
|
|
67
|
+
};
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style lang="scss" scoped>
|
|
71
|
+
@import "../../style/common";
|
|
72
|
+
</style>
|
package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/h5.scss
CHANGED
|
@@ -6,11 +6,20 @@
|
|
|
6
6
|
margin-right:15px;
|
|
7
7
|
&-title {
|
|
8
8
|
margin-top:5px;
|
|
9
|
+
font-size: 14px;
|
|
10
|
+
white-space: nowrap;
|
|
11
|
+
max-width: 85px;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
text-overflow: ellipsis;
|
|
9
14
|
}
|
|
10
15
|
&-icon{
|
|
11
16
|
background-color: #fff;
|
|
12
|
-
|
|
17
|
+
width: 40px;
|
|
13
18
|
border-radius: 10px;
|
|
19
|
+
height: 40px;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
14
23
|
}
|
|
15
24
|
&-dialog {
|
|
16
25
|
position: static !important;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ToolbarItemContainer
|
|
3
|
+
:iconFile="getIcon()"
|
|
4
|
+
:title="props.title"
|
|
5
|
+
:iconWidth="isPC ? '20px' : '25px'"
|
|
6
|
+
:iconHeight="isPC ? '20px' : '25px'"
|
|
7
|
+
:needDialog="false"
|
|
8
|
+
@onIconClick="onIconClick"
|
|
9
|
+
:isH5ToolShow="isH5ToolShow">
|
|
10
|
+
<div :class="['image-upload', !isPC && 'image-upload-h5', 'image-video']">
|
|
11
|
+
</div>
|
|
12
|
+
</ToolbarItemContainer>
|
|
13
|
+
</template>
|
|
14
|
+
<script lang="ts" setup>
|
|
15
|
+
import {
|
|
16
|
+
TUIChatService,
|
|
17
|
+
TUIStore,
|
|
18
|
+
StoreName,
|
|
19
|
+
IConversationModel,
|
|
20
|
+
} from '@tencentcloud/chat-uikit-engine';
|
|
21
|
+
import vue from '../../../adapter-vue';
|
|
22
|
+
import { isPC } from '../../../utils/env';
|
|
23
|
+
import ToolbarItemContainer from './toolbar-item-container/index.vue';
|
|
24
|
+
import { isEnabledMessageReadReceiptGlobal, openSafeUrl, getTo } from '../../../utils/utils';
|
|
25
|
+
const { ref } = vue;
|
|
26
|
+
|
|
27
|
+
const props = defineProps({
|
|
28
|
+
isH5ToolShow: {
|
|
29
|
+
type: Boolean,
|
|
30
|
+
default: false,
|
|
31
|
+
},
|
|
32
|
+
title: {
|
|
33
|
+
type: String,
|
|
34
|
+
default: ''
|
|
35
|
+
},
|
|
36
|
+
icon: {
|
|
37
|
+
type: String,
|
|
38
|
+
default: '',
|
|
39
|
+
},
|
|
40
|
+
type: {
|
|
41
|
+
type: Number,
|
|
42
|
+
default: 1
|
|
43
|
+
},
|
|
44
|
+
content: {
|
|
45
|
+
type: String,
|
|
46
|
+
default: ''
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const currentConversation = ref<IConversationModel>();
|
|
51
|
+
|
|
52
|
+
TUIStore.watch(StoreName.CONV, {
|
|
53
|
+
currentConversation: (conversation: IConversationModel) => {
|
|
54
|
+
currentConversation.value = conversation;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const getIcon = (): string => {
|
|
59
|
+
return props.icon;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const onIconClick = () => {
|
|
63
|
+
if (props.type === 1 && props.content) {
|
|
64
|
+
TUIChatService.sendTextMessage({
|
|
65
|
+
to: getTo(currentConversation?.value),
|
|
66
|
+
conversationType: currentConversation?.value?.type,
|
|
67
|
+
payload: {
|
|
68
|
+
text: props.content
|
|
69
|
+
},
|
|
70
|
+
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
71
|
+
});
|
|
72
|
+
} else if (props.type === 2 && props.content) {
|
|
73
|
+
openSafeUrl(props.content);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<style lang="scss" scoped>
|
|
79
|
+
@import "../style/common";
|
|
80
|
+
</style>
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
:iconFile="handleIcon()"
|
|
4
4
|
:title="handleTitle()"
|
|
5
5
|
:needDialog="false"
|
|
6
|
-
iconWidth='
|
|
7
|
-
iconHeight='
|
|
6
|
+
iconWidth='20px'
|
|
7
|
+
iconHeight='20px'
|
|
8
8
|
@onIconClick="onIconClick"
|
|
9
9
|
>
|
|
10
10
|
<div :class="['video-upload', !isPC && 'video-upload-h5']">
|
|
@@ -32,8 +32,9 @@ import {
|
|
|
32
32
|
import vue from '../../../../adapter-vue';
|
|
33
33
|
import { isPC } from '../../../../utils/env';
|
|
34
34
|
import ToolbarItemContainer from '../toolbar-item-container/index.vue';
|
|
35
|
-
import videoIcon from '../../../../assets/video.
|
|
36
|
-
import
|
|
35
|
+
import videoIcon from '../../../../assets/video.svg';
|
|
36
|
+
import videoIconH5 from '../../../../assets/video_h5.svg'
|
|
37
|
+
import { isEnabledMessageReadReceiptGlobal, getTo } from '../../../../utils/utils';
|
|
37
38
|
const { ref } = vue;
|
|
38
39
|
|
|
39
40
|
const props = defineProps({
|
|
@@ -56,19 +57,15 @@ TUIStore.watch(StoreName.CONV, {
|
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
const handleIcon = (): string => {
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
return isPC ? videoIcon : videoIconH5;
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
const handleTitle = (): string => {
|
|
64
|
-
|
|
65
|
-
return '视频';
|
|
66
|
-
|
|
64
|
+
return TUITranslateService.t('视频');
|
|
67
65
|
};
|
|
68
66
|
|
|
69
67
|
const onIconClick = () => {
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
inputRef?.value?.click && inputRef?.value?.click();
|
|
72
69
|
};
|
|
73
70
|
|
|
74
71
|
const sendVideoInWeb = (e: any) => {
|
|
@@ -84,9 +81,7 @@ const sendVideoMessage = (file: any) => {
|
|
|
84
81
|
return;
|
|
85
82
|
}
|
|
86
83
|
const options = {
|
|
87
|
-
to:
|
|
88
|
-
currentConversation?.value?.groupProfile?.groupID
|
|
89
|
-
|| currentConversation?.value?.userProfile?.userID,
|
|
84
|
+
to: getTo(currentConversation?.value),
|
|
90
85
|
conversationType: currentConversation?.value?.type,
|
|
91
86
|
payload: {
|
|
92
87
|
file,
|
|
@@ -217,8 +217,9 @@ import {
|
|
|
217
217
|
isEnabledMessageReadReceiptGlobal,
|
|
218
218
|
deepCopy,
|
|
219
219
|
} from '../../../utils/utils';
|
|
220
|
-
import { isMessageInvisible, isThinkingMessage, isThinkingMessageOverTime } from '../../../utils/index';
|
|
220
|
+
import { isMessageInvisible, isThinkingMessage, isThinkingMessageOverTime, JSONToObject } from '../../../utils/index';
|
|
221
221
|
import { isCustomerConversation } from '../../../index';
|
|
222
|
+
import { CUSTOM_MESSAGE_SRC } from '../../../constant';
|
|
222
223
|
|
|
223
224
|
interface ScrollConfig {
|
|
224
225
|
scrollToMessage?: IMessageModel;
|
|
@@ -290,9 +291,9 @@ onMounted(() => {
|
|
|
290
291
|
messageList: onMessageListUpdated,
|
|
291
292
|
messageSource: onMessageSourceUpdated,
|
|
292
293
|
isCompleted: isCompletedUpdated,
|
|
294
|
+
newMessageList: onNewMessageList,
|
|
293
295
|
});
|
|
294
296
|
|
|
295
|
-
|
|
296
297
|
TUIStore.watch(StoreName.CUSTOM, {
|
|
297
298
|
isShowMessagePopMenu: isShowMessagePopMenuUpdated,
|
|
298
299
|
});
|
|
@@ -329,6 +330,29 @@ onUnmounted(() => {
|
|
|
329
330
|
observer = null;
|
|
330
331
|
});
|
|
331
332
|
|
|
333
|
+
function onNewMessageList(list: IMessageModel[]) {
|
|
334
|
+
list.forEach((message:IMessageModel) => {
|
|
335
|
+
if (message?.type === TUIChatEngine.TYPES.MSG_CUSTOM) {
|
|
336
|
+
const data = JSONToObject(message?.payload?.data);
|
|
337
|
+
if (data?.src === CUSTOM_MESSAGE_SRC.SEAT_STATUS) {
|
|
338
|
+
if (data?.content.command === "updateSeatStatus") {
|
|
339
|
+
if (data.content.content === 'inSeat') {
|
|
340
|
+
TUIStore.update(StoreName.CUSTOM, "isInHumanService", true);
|
|
341
|
+
} else if (data.content.content === 'outSeat') {
|
|
342
|
+
TUIStore.update(StoreName.CUSTOM, "isInHumanService", false);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
} else if (data?.src === CUSTOM_MESSAGE_SRC.TYPING_STATE) {
|
|
346
|
+
if (data?.typingStatus === 1) {
|
|
347
|
+
TUIStore.update(StoreName.CUSTOM, 'isTyping', true);
|
|
348
|
+
} else {
|
|
349
|
+
TUIStore.update(StoreName.CUSTOM, 'isTyping', false);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
332
356
|
async function onMessageListUpdated(list: IMessageModel[]) {
|
|
333
357
|
if (!isCustomerConversation(currentConversationID.value)) {
|
|
334
358
|
return;
|
|
@@ -370,7 +394,6 @@ async function onMessageListUpdated(list: IMessageModel[]) {
|
|
|
370
394
|
await scrollToPosition({
|
|
371
395
|
scrollToOffset: { bottom: beforeHistoryGetScrollHeight.value },
|
|
372
396
|
});
|
|
373
|
-
beforeHistoryGetScrollHeight.value = 0;
|
|
374
397
|
} else if (
|
|
375
398
|
scrollButtonInstanceRef.value?.isScrollButtonVisible
|
|
376
399
|
&& newLastMessage?.flow === 'in'
|
|
@@ -425,8 +448,7 @@ async function scrollToPosition(config: ScrollConfig = {}): Promise<void> {
|
|
|
425
448
|
if (config.scrollToOffset?.top) {
|
|
426
449
|
container!.scrollTop = config.scrollToOffset.top;
|
|
427
450
|
} else if (config.scrollToOffset?.bottom) {
|
|
428
|
-
container!.scrollTop
|
|
429
|
-
= container!.scrollHeight - config.scrollToOffset.bottom;
|
|
451
|
+
container!.scrollTop = container!.scrollHeight - config.scrollToOffset.bottom;
|
|
430
452
|
}
|
|
431
453
|
}
|
|
432
454
|
resolve();
|
|
@@ -574,6 +596,11 @@ const handleH5LongPress = (e: any, message: IMessageModel, type: string) => {
|
|
|
574
596
|
};
|
|
575
597
|
|
|
576
598
|
function onHeightChanged() {
|
|
599
|
+
// 如果 view more 导致了子组件派发了 heightChanged 事件,不滚动到最底部,保留原来的滚动位置
|
|
600
|
+
if (beforeHistoryGetScrollHeight.value > 0) {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
577
604
|
scrollToLatestMessage();
|
|
578
605
|
}
|
|
579
606
|
|
|
@@ -617,6 +644,7 @@ async function scrollToLatestMessage() {
|
|
|
617
644
|
messageListRef.value.scrollTop = scrollHeight - height;
|
|
618
645
|
}
|
|
619
646
|
scrollButtonInstanceRef.value?.hideScrollButton();
|
|
647
|
+
beforeHistoryGetScrollHeight.value = 0;
|
|
620
648
|
}
|
|
621
649
|
|
|
622
650
|
const handelScrollListScroll = throttle(
|
|
@@ -709,7 +737,6 @@ function setAudioPlayed(messageID: string) {
|
|
|
709
737
|
};
|
|
710
738
|
}
|
|
711
739
|
|
|
712
|
-
|
|
713
740
|
defineExpose({
|
|
714
741
|
scrollToLatestMessage,
|
|
715
742
|
});
|
|
@@ -12,21 +12,19 @@
|
|
|
12
12
|
:class="[message.flow === 'in' ? '' : 'reverse']"
|
|
13
13
|
>
|
|
14
14
|
<Avatar
|
|
15
|
-
|
|
15
|
+
v-if="isPC && showAvatar === 1"
|
|
16
16
|
useSkeletonAnimation
|
|
17
|
-
:url="
|
|
17
|
+
:url="avatarUrl"
|
|
18
18
|
/>
|
|
19
19
|
<main
|
|
20
20
|
:class="['message-body',message.flow==='out' && 'message-body-reverse']"
|
|
21
21
|
@click.stop
|
|
22
22
|
>
|
|
23
23
|
<div
|
|
24
|
-
v-if="
|
|
25
|
-
isPC
|
|
26
|
-
"
|
|
24
|
+
v-if="isPC && showNickName === 1"
|
|
27
25
|
class="message-body-nick-name"
|
|
28
26
|
>
|
|
29
|
-
{{
|
|
27
|
+
{{ nickName }}
|
|
30
28
|
</div>
|
|
31
29
|
<div
|
|
32
30
|
:class="[
|
|
@@ -142,6 +140,8 @@ import loadingIcon from '../../../../assets/loading.png';
|
|
|
142
140
|
import customerAvatar from '../../../../assets/customer_avatar.png';
|
|
143
141
|
import { shallowCopyMessage } from '../../../../utils/utils';
|
|
144
142
|
import { isPC,isH5 } from '../../../../utils/env';
|
|
143
|
+
import { JSONToObject } from '../../../../utils/index';
|
|
144
|
+
import state from '../../../../utils/state.js';
|
|
145
145
|
import { CUSTOM_MESSAGE_SRC } from '../../../../constant';
|
|
146
146
|
const { computed, toRefs } = vue;
|
|
147
147
|
|
|
@@ -182,6 +182,16 @@ const needLoadingIconMessageType = [
|
|
|
182
182
|
];
|
|
183
183
|
|
|
184
184
|
const { blinkMessageIDList, messageItem: message } = toRefs(props);
|
|
185
|
+
const {
|
|
186
|
+
showAvatar,
|
|
187
|
+
showNickName,
|
|
188
|
+
robotAvatar,
|
|
189
|
+
staffAvatar,
|
|
190
|
+
userAvatar,
|
|
191
|
+
robotNickName,
|
|
192
|
+
staffNickName,
|
|
193
|
+
userNickName,
|
|
194
|
+
} = state.get('avatarNickName');
|
|
185
195
|
|
|
186
196
|
const isDisplayUnplayMark = computed<boolean>(() => {
|
|
187
197
|
return (
|
|
@@ -213,6 +223,43 @@ const isMultiBranchMsg = computed(()=>{
|
|
|
213
223
|
return false;
|
|
214
224
|
});
|
|
215
225
|
|
|
226
|
+
function isFromRobot(cloudCustomData: string) {
|
|
227
|
+
try {
|
|
228
|
+
const jsonObj = JSONToObject(cloudCustomData);
|
|
229
|
+
return jsonObj.hasOwnProperty("role") && jsonObj.role === "robot";
|
|
230
|
+
} catch (e) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const avatarUrl = computed(() => {
|
|
236
|
+
let url = '';
|
|
237
|
+
if (message.value?.flow === 'in') {
|
|
238
|
+
if (isFromRobot(message.value?.cloudCustomData)) {
|
|
239
|
+
url = robotAvatar || customerAvatar;
|
|
240
|
+
} else {
|
|
241
|
+
url = staffAvatar || message.value?.avatar;
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
url = userAvatar || message.value?.avatar || '';
|
|
245
|
+
}
|
|
246
|
+
return url;
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const nickName = computed(() => {
|
|
250
|
+
let nick = '';
|
|
251
|
+
if (message.value?.flow === 'in') {
|
|
252
|
+
if (isFromRobot(message.value?.cloudCustomData)) {
|
|
253
|
+
nick = robotNickName || props.content.showName;
|
|
254
|
+
} else {
|
|
255
|
+
nick = staffNickName || props.content.showName;
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
nick = userNickName || props.content.showName;
|
|
259
|
+
}
|
|
260
|
+
return nick;
|
|
261
|
+
});
|
|
262
|
+
|
|
216
263
|
const isProductCardOrOrderMessage = computed(() => {
|
|
217
264
|
if (message.value?.type == "TIMCustomElem") {
|
|
218
265
|
const src = JSON.parse(message.value.payload.data).src;
|
|
@@ -122,7 +122,7 @@ export default {
|
|
|
122
122
|
margin-bottom: 8px;
|
|
123
123
|
}
|
|
124
124
|
.branch-item {
|
|
125
|
-
color
|
|
125
|
+
color: #1c66e5;
|
|
126
126
|
cursor: pointer;
|
|
127
127
|
}
|
|
128
128
|
.warning-item {
|
|
@@ -131,14 +131,14 @@ export default {
|
|
|
131
131
|
.branch-card {
|
|
132
132
|
min-width: 250px;
|
|
133
133
|
max-width: 350px;
|
|
134
|
-
display:flex;
|
|
135
|
-
flex-direction:column;
|
|
134
|
+
display: flex;
|
|
135
|
+
flex-direction: column;
|
|
136
136
|
align-items: flex-start;
|
|
137
137
|
}
|
|
138
138
|
.branch-item-selected {
|
|
139
|
-
cursor:
|
|
139
|
+
cursor: default;
|
|
140
140
|
color: #a0a7b8;
|
|
141
|
-
opacity:0.6;
|
|
141
|
+
opacity: 0.6;
|
|
142
142
|
}
|
|
143
143
|
.branch-title-icon {
|
|
144
144
|
margin-right: 5px;
|
|
@@ -79,6 +79,7 @@ import { CustomMessagePayload, TextMessagePayload, customerServicePayloadType }
|
|
|
79
79
|
import MessageRating from './message-rating/index.vue';
|
|
80
80
|
import vue from '../../../../../../adapter-vue';
|
|
81
81
|
import { JSONToObject } from '../../../../../../utils/index';
|
|
82
|
+
import { isEnabledMessageReadReceiptGlobal } from '../../../../../../utils/utils';
|
|
82
83
|
import { CUSTOM_MESSAGE_SRC } from '../../../../../../constant';
|
|
83
84
|
import MessageBranch from './message-branch.vue';
|
|
84
85
|
import MessageForm from './message-single-form/index.vue';
|
|
@@ -123,11 +124,15 @@ export default {
|
|
|
123
124
|
const payload = computed<customerServicePayloadType>(() => {
|
|
124
125
|
return props.message && JSONToObject(props.message?.payload?.data);
|
|
125
126
|
});
|
|
126
|
-
const sendTextMessage = (payload: TextMessagePayload) => {
|
|
127
|
-
TUIChatService.sendTextMessage({
|
|
127
|
+
const sendTextMessage = (payload: TextMessagePayload, cloudCustomData?: string) => {
|
|
128
|
+
TUIChatService.sendTextMessage({
|
|
129
|
+
payload,
|
|
130
|
+
cloudCustomData: cloudCustomData || '',
|
|
131
|
+
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
|
132
|
+
});
|
|
128
133
|
};
|
|
129
134
|
const sendCustomMessage = (payload: CustomMessagePayload) => {
|
|
130
|
-
TUIChatService.sendCustomMessage({payload});
|
|
135
|
+
TUIChatService.sendCustomMessage({ payload });
|
|
131
136
|
};
|
|
132
137
|
const handleShowFormPopup = (data: boolean) => {
|
|
133
138
|
emit('showFormPopup', data);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
{{ props.payload.content.header }}
|
|
8
8
|
</div>
|
|
9
9
|
<div
|
|
10
|
-
v-if="props.payload.status
|
|
10
|
+
v-if="props.payload.status === 0 && canSelect"
|
|
11
11
|
v-for="(item, index) in props.payload.content.items"
|
|
12
12
|
:key="index"
|
|
13
13
|
:class="['form-branch-item', item.content ? '' : 'warning-item']"
|
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
</template>
|
|
20
20
|
|
|
21
21
|
<script lang="ts">
|
|
22
|
+
import vue from '../../../../../../../adapter-vue';
|
|
22
23
|
import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
|
23
24
|
import { customerServicePayloadType } from '../../../../../../../interface';
|
|
24
25
|
import { isPC } from '../../../../../../../utils/env'
|
|
26
|
+
const { ref } = vue;
|
|
25
27
|
interface branchItem {
|
|
26
28
|
content: string;
|
|
27
29
|
desc: string;
|
|
@@ -35,23 +37,35 @@ export default {
|
|
|
35
37
|
props: {
|
|
36
38
|
payload: {
|
|
37
39
|
type: Object as () => customerServicePayloadType,
|
|
38
|
-
default: () => ({content: { header: '', items: [] },status:0}),
|
|
40
|
+
default: () => ({optionType: 0, taskInfo: { taskID: 0, nodeID: '', env: '' }, content: { header: '', items: [] }, status: 0}),
|
|
39
41
|
},
|
|
40
42
|
},
|
|
41
43
|
emits: ['input-click'],
|
|
42
44
|
setup(props: Props, { emit }) {
|
|
43
|
-
let
|
|
45
|
+
let canSelect = ref(true);
|
|
44
46
|
const listItemClick = (branch: branchItem): void => {
|
|
45
47
|
if (!branch.content) {
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
let cloudCustomData;
|
|
51
|
+
if (props.payload.optionType === 0) {
|
|
52
|
+
cloudCustomData = '';
|
|
53
|
+
canSelect.value = false;
|
|
54
|
+
} else if (props.payload.optionType === 1) {
|
|
55
|
+
cloudCustomData = JSON.stringify({
|
|
56
|
+
BranchOptionInfo: {
|
|
57
|
+
taskID: props.payload.taskInfo?.taskID,
|
|
58
|
+
nodeID: props.payload.taskInfo?.nodeID,
|
|
59
|
+
env: props.payload.taskInfo?.env,
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
emit('input-click', branch, cloudCustomData);
|
|
50
64
|
};
|
|
51
65
|
|
|
52
66
|
return {
|
|
53
67
|
props,
|
|
54
|
-
|
|
68
|
+
canSelect,
|
|
55
69
|
listItemClick,
|
|
56
70
|
isPC,
|
|
57
71
|
TUITranslateService
|
|
@@ -37,8 +37,8 @@ export default {
|
|
|
37
37
|
const payload = computed<customerServicePayloadType>(() => {
|
|
38
38
|
return props.payload;
|
|
39
39
|
});
|
|
40
|
-
const handleContentListItemClick = (branch: branchItem) => {
|
|
41
|
-
emit('sendMessage', { text: branch.content });
|
|
40
|
+
const handleContentListItemClick = (branch: branchItem, cloudCustomData?: string) => {
|
|
41
|
+
emit('sendMessage', { text: branch.content }, cloudCustomData || '');
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
const handleFormSaveInputSubmit = (text: string) => {
|