@tencentcloud/ai-desk-customer-vue 1.5.1 → 1.5.3
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 +14 -0
- package/README.md +316 -71
- package/components/CustomerServiceChat/emoji-config/index.ts +3 -2
- package/components/CustomerServiceChat/index-web.vue +17 -4
- package/components/CustomerServiceChat/message-input/index-web.vue +9 -2
- package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +11 -0
- package/components/CustomerServiceChat/message-input/message-input-quote/index.vue +31 -6
- package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +14 -1
- package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +26 -2
- package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +14 -1
- package/components/CustomerServiceChat/message-list/bottom-quick-order/index.vue +201 -0
- package/components/CustomerServiceChat/message-list/index-web.vue +46 -0
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/marked.ts +6 -1
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +1 -1
- package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-product-card.vue +140 -87
- package/components/CustomerServiceChat/message-list/message-elements/message-quote/index-web.vue +18 -9
- package/components/CustomerServiceChat/style/h5.scss +1 -0
- package/components/CustomerServiceChat/utils/sendMessage.ts +5 -0
- package/constant.ts +3 -3
- package/interface.ts +15 -1
- package/locales/en/TUIChat.ts +2 -1
- package/locales/en/aidesk.ts +4 -1
- package/locales/fil/TUIChat.ts +1 -0
- package/locales/fil/aidesk.ts +3 -0
- package/locales/id/TUIChat.ts +161 -160
- package/locales/id/aidesk.ts +3 -0
- package/locales/ja/TUIChat.ts +162 -161
- package/locales/ja/aidesk.ts +3 -0
- package/locales/ms/TUIChat.ts +162 -161
- package/locales/ms/aidesk.ts +3 -0
- package/locales/ru/TUIChat.ts +155 -154
- package/locales/ru/aidesk.ts +3 -0
- package/locales/th/TUIChat.ts +162 -161
- package/locales/th/aidesk.ts +3 -0
- package/locales/vi/TUIChat.ts +153 -152
- package/locales/vi/aidesk.ts +3 -0
- package/locales/zh_cn/TUIChat.ts +1 -0
- package/locales/zh_cn/aidesk.ts +3 -0
- package/locales/zh_tw/TUIChat.ts +1 -0
- package/locales/zh_tw/aidesk.ts +3 -0
- package/package.json +4 -6
- package/server.ts +42 -7
- package/utils/utils.ts +60 -0
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
<Icon
|
|
22
22
|
class="input-quote-close-icon"
|
|
23
23
|
:file="closeIcon"
|
|
24
|
-
width="
|
|
25
|
-
height="
|
|
24
|
+
width="14px"
|
|
25
|
+
height="14px"
|
|
26
26
|
@onClick="cancelQuote"
|
|
27
27
|
/>
|
|
28
28
|
</div>
|
|
@@ -42,6 +42,8 @@ import closeIcon from '../../../../assets/close-quote-icon.svg';
|
|
|
42
42
|
import { isPC,isH5 } from '../../../../utils/env';
|
|
43
43
|
import { transformTextWithKeysToEmojiNames } from '../../emoji-config';
|
|
44
44
|
import { InputDisplayType } from '../../../../interface';
|
|
45
|
+
import { getQuoteContentForDesk } from '../../../../utils/utils';
|
|
46
|
+
import { MessageQuoteTypeEnum } from '../../message-list/message-elements/message-quote/interface';
|
|
45
47
|
const { ref, computed, onMounted, onUnmounted } = vue;
|
|
46
48
|
|
|
47
49
|
interface IProps {
|
|
@@ -54,6 +56,9 @@ const props = withDefaults(defineProps<IProps>(), {
|
|
|
54
56
|
|
|
55
57
|
const TYPES = TUIChatEngine.TYPES;
|
|
56
58
|
const quoteMessage = ref<IMessageModel>();
|
|
59
|
+
const quoteMessageType = ref<number>(0);
|
|
60
|
+
|
|
61
|
+
const emits = defineEmits(['getQuoteMessageCloudCustomData']);
|
|
57
62
|
|
|
58
63
|
onMounted(() => {
|
|
59
64
|
TUIStore.watch(StoreName.CHAT, {
|
|
@@ -74,30 +79,39 @@ const quoteContentText = computed(() => {
|
|
|
74
79
|
_quoteContentText = transformTextWithKeysToEmojiNames(
|
|
75
80
|
quoteMessage.value.payload?.text,
|
|
76
81
|
);
|
|
82
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_TEXT;
|
|
77
83
|
break;
|
|
78
84
|
case TYPES.MSG_IMAGE:
|
|
79
85
|
_quoteContentText = TUITranslateService.t('TUIChat.图片');
|
|
86
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_IMAGE;
|
|
80
87
|
break;
|
|
81
88
|
case TYPES.MSG_AUDIO:
|
|
82
89
|
_quoteContentText = TUITranslateService.t('TUIChat.语音');
|
|
90
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_SOUND;
|
|
83
91
|
break;
|
|
84
92
|
case TYPES.MSG_VIDEO:
|
|
85
93
|
_quoteContentText = TUITranslateService.t('TUIChat.视频');
|
|
94
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_VIDEO;
|
|
86
95
|
break;
|
|
87
96
|
case TYPES.MSG_FILE:
|
|
88
97
|
_quoteContentText = TUITranslateService.t('TUIChat.文件');
|
|
98
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_FILE;
|
|
89
99
|
break;
|
|
90
100
|
case TYPES.MSG_CUSTOM:
|
|
91
|
-
_quoteContentText =
|
|
101
|
+
_quoteContentText = getQuoteContentForDesk(quoteMessage.value);
|
|
102
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_CUSTOM;
|
|
92
103
|
break;
|
|
93
104
|
case TYPES.MSG_FACE:
|
|
94
105
|
_quoteContentText = TUITranslateService.t('TUIChat.表情');
|
|
106
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_FACE;
|
|
95
107
|
break;
|
|
96
108
|
case TYPES.MSG_MERGER:
|
|
97
109
|
_quoteContentText = TUITranslateService.t('TUIChat.聊天记录');
|
|
110
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_MERGER;
|
|
98
111
|
break;
|
|
99
112
|
default:
|
|
100
113
|
_quoteContentText = TUITranslateService.t('TUIChat.消息');
|
|
114
|
+
quoteMessageType.value = MessageQuoteTypeEnum.TYPE_NONE;
|
|
101
115
|
break;
|
|
102
116
|
}
|
|
103
117
|
return _quoteContentText;
|
|
@@ -114,10 +128,21 @@ function onQuoteMessageUpdated(options?: {
|
|
|
114
128
|
message: IMessageModel;
|
|
115
129
|
type: string;
|
|
116
130
|
}) {
|
|
117
|
-
if (options
|
|
131
|
+
if (options && options.message && options.type === 'quote') {
|
|
118
132
|
quoteMessage.value = options.message;
|
|
133
|
+
const quoteMessageCloudCustomData = JSON.stringify({
|
|
134
|
+
messageReply: {
|
|
135
|
+
messageAbstract: quoteContentText.value,
|
|
136
|
+
messageID: quoteMessage.value.ID,
|
|
137
|
+
messageSender: quoteMessage.value.from,
|
|
138
|
+
messageTime: quoteMessage.value.time,
|
|
139
|
+
messageType: quoteMessageType.value,
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
emits('getQuoteMessageCloudCustomData', quoteMessageCloudCustomData);
|
|
119
143
|
} else {
|
|
120
144
|
quoteMessage.value = undefined;
|
|
145
|
+
emits('getQuoteMessageCloudCustomData', undefined);
|
|
121
146
|
}
|
|
122
147
|
}
|
|
123
148
|
</script>
|
|
@@ -152,7 +177,7 @@ function onQuoteMessageUpdated(options?: {
|
|
|
152
177
|
.input-quote-content-h5 {
|
|
153
178
|
display: flex;
|
|
154
179
|
justify-content: space-between;
|
|
155
|
-
background-color:
|
|
180
|
+
background-color: rgba(255, 255, 255, 0.5);
|
|
156
181
|
padding: 8px 16px 8px 8px;
|
|
157
182
|
font-size: 12px;
|
|
158
183
|
align-items: center;
|
|
@@ -197,6 +222,6 @@ function onQuoteMessageUpdated(options?: {
|
|
|
197
222
|
.input-quote-container-h5 {
|
|
198
223
|
@extend %common-container-style;
|
|
199
224
|
width:100%;
|
|
200
|
-
margin:
|
|
225
|
+
margin: 5px 0 5px 0px;
|
|
201
226
|
}
|
|
202
227
|
</style>
|
|
@@ -35,6 +35,8 @@ import fileIcon from '../../../../assets/files.svg';
|
|
|
35
35
|
import fileIconH5 from '../../../../assets/file-h5.png';
|
|
36
36
|
import { isPC, isH5 } from '../../../../utils/env';
|
|
37
37
|
import { isEnabledMessageReadReceiptGlobal, getTo } from '../../../../utils/utils';
|
|
38
|
+
import Log from '../../../../utils/logger';
|
|
39
|
+
import { Toast, TOAST_TYPE } from '../../../common/Toast/index-web';
|
|
38
40
|
const { ref } = vue;
|
|
39
41
|
|
|
40
42
|
const inputRef = ref();
|
|
@@ -63,7 +65,18 @@ const sendFileMessage = (e: any) => {
|
|
|
63
65
|
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
64
66
|
} as SendMessageParams;
|
|
65
67
|
const sendMessageOptions: SendMessageOptions = {};
|
|
66
|
-
TUIChatService.sendFileMessage(options, sendMessageOptions)
|
|
68
|
+
TUIChatService.sendFileMessage(options, sendMessageOptions)
|
|
69
|
+
.catch((err) => {
|
|
70
|
+
Toast({
|
|
71
|
+
message:
|
|
72
|
+
err.code === 2402 ?
|
|
73
|
+
TUITranslateService.t('AIDesk.file 大小超过 100MB,无法发送')
|
|
74
|
+
: err.message,
|
|
75
|
+
type: TOAST_TYPE.ERROR,
|
|
76
|
+
duration: 5000,
|
|
77
|
+
});
|
|
78
|
+
Log.l(`Send File Failed:${err.code} ${err.message}`);
|
|
79
|
+
});
|
|
67
80
|
e.target.value = '';
|
|
68
81
|
};
|
|
69
82
|
</script>
|
|
@@ -38,6 +38,8 @@ import ToolbarItemContainer from '../toolbar-item-container/index.vue';
|
|
|
38
38
|
import imageIcon from '../../../../assets/image.svg';
|
|
39
39
|
import imageUniIcon from '../../../../assets/image-uni.png';
|
|
40
40
|
import { getTo, isEnabledMessageReadReceiptGlobal } from '../../../../utils/utils';
|
|
41
|
+
import Log from '../../../../utils/logger';
|
|
42
|
+
import { Toast, TOAST_TYPE } from '../../../common/Toast/index-web';
|
|
41
43
|
const { ref, computed } = vue;
|
|
42
44
|
|
|
43
45
|
const props = defineProps({
|
|
@@ -119,7 +121,18 @@ const sendImageMessage = (files: any) => {
|
|
|
119
121
|
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
120
122
|
} as SendMessageParams;
|
|
121
123
|
const sendMessageOptions: SendMessageOptions = {};
|
|
122
|
-
TUIChatService.sendImageMessage(options, sendMessageOptions)
|
|
124
|
+
TUIChatService.sendImageMessage(options, sendMessageOptions)
|
|
125
|
+
.catch((err) => {
|
|
126
|
+
Toast({
|
|
127
|
+
message:
|
|
128
|
+
err.code === 2253 ?
|
|
129
|
+
TUITranslateService.t('AIDesk.image 大小超过 20MB,无法发送')
|
|
130
|
+
: err.message,
|
|
131
|
+
type: TOAST_TYPE.ERROR,
|
|
132
|
+
duration: 5000,
|
|
133
|
+
});
|
|
134
|
+
Log.l(`Send Image Failed:${err.code} ${err.message}`);
|
|
135
|
+
});
|
|
123
136
|
};
|
|
124
137
|
const sendVideoMessage = (file: any) => {
|
|
125
138
|
if (!file) {
|
|
@@ -134,7 +147,18 @@ const sendVideoMessage = (file: any) => {
|
|
|
134
147
|
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
135
148
|
} as SendMessageParams;
|
|
136
149
|
const sendMessageOptions: SendMessageOptions = {};
|
|
137
|
-
TUIChatService.sendVideoMessage(options, sendMessageOptions)
|
|
150
|
+
TUIChatService.sendVideoMessage(options, sendMessageOptions)
|
|
151
|
+
.catch((err) => {
|
|
152
|
+
Toast({
|
|
153
|
+
message:
|
|
154
|
+
err.code === 2351 ?
|
|
155
|
+
TUITranslateService.t('AIDesk.video 大小超过 100MB,无法发送')
|
|
156
|
+
: err.message,
|
|
157
|
+
type: TOAST_TYPE.ERROR,
|
|
158
|
+
duration: 5000,
|
|
159
|
+
});
|
|
160
|
+
Log.l(`Send Video Failed:${err.code} ${err.message}`);
|
|
161
|
+
});
|
|
138
162
|
};
|
|
139
163
|
</script>
|
|
140
164
|
|
|
@@ -35,6 +35,8 @@ import ToolbarItemContainer from '../toolbar-item-container/index.vue';
|
|
|
35
35
|
import videoIcon from '../../../../assets/video.svg';
|
|
36
36
|
import videoIconH5 from '../../../../assets/video_h5.svg'
|
|
37
37
|
import { isEnabledMessageReadReceiptGlobal, getTo } from '../../../../utils/utils';
|
|
38
|
+
import Log from '../../../../utils/logger';
|
|
39
|
+
import { Toast, TOAST_TYPE } from '../../../common/Toast/index-web';
|
|
38
40
|
const { ref } = vue;
|
|
39
41
|
|
|
40
42
|
const props = defineProps({
|
|
@@ -89,7 +91,18 @@ const sendVideoMessage = (file: any) => {
|
|
|
89
91
|
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
90
92
|
} as SendMessageParams;
|
|
91
93
|
const sendMessageOptions: SendMessageOptions = {};
|
|
92
|
-
TUIChatService.sendVideoMessage(options, sendMessageOptions)
|
|
94
|
+
TUIChatService.sendVideoMessage(options, sendMessageOptions)
|
|
95
|
+
.catch((err) => {
|
|
96
|
+
Toast({
|
|
97
|
+
message:
|
|
98
|
+
err.code === 2351 ?
|
|
99
|
+
TUITranslateService.t('AIDesk.video 大小超过 100MB,无法发送')
|
|
100
|
+
: err.message,
|
|
101
|
+
type: TOAST_TYPE.ERROR,
|
|
102
|
+
duration: 5000,
|
|
103
|
+
});
|
|
104
|
+
Log.l(`Send Video Failed:${err.code} ${err.message}`);
|
|
105
|
+
});
|
|
93
106
|
};
|
|
94
107
|
</script>
|
|
95
108
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="isPC ? 'bottom-quick-order-line':''">
|
|
3
|
+
<div :class="['bottom-quick-order-container',isPC && 'bottom-quick-order-container-pc']">
|
|
4
|
+
<div @click="closeOrder">
|
|
5
|
+
<Icon class="close-icon" :file="cardToolbarCloseIcon" width="12px" height="12px"/>
|
|
6
|
+
</div>
|
|
7
|
+
<div v-for="(item,index) in topCustomField" :key="index" class="quick-order-custom-field">
|
|
8
|
+
<div class="custom-field-line">
|
|
9
|
+
<div v-if="item.name" class="order-name">
|
|
10
|
+
{{ item.name }}:
|
|
11
|
+
</div>
|
|
12
|
+
<div class="order-value">
|
|
13
|
+
{{ item.value }}
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="quick-order-main">
|
|
18
|
+
<img
|
|
19
|
+
v-if="props.bottomQuickOrder.pic"
|
|
20
|
+
class="quick-order-img"
|
|
21
|
+
:src="props.bottomQuickOrder.pic"
|
|
22
|
+
>
|
|
23
|
+
<div class="quick-order-information">
|
|
24
|
+
<div class="quick-order-title">
|
|
25
|
+
{{ props.bottomQuickOrder.header }}
|
|
26
|
+
</div>
|
|
27
|
+
<div class="quick-order-description-block">
|
|
28
|
+
<div class="quick-order-description">
|
|
29
|
+
{{ props.bottomQuickOrder.desc }}
|
|
30
|
+
</div>
|
|
31
|
+
<div v-if="bottomCustomField.length === 0" class="quick-order-link" @click="sendOrder">
|
|
32
|
+
{{ TUITranslateService.t("发送") }}
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<div v-for="(item,index) in bottomCustomField" :key="index" class="quick-order-custom-field">
|
|
38
|
+
<div class="custom-field-line">
|
|
39
|
+
<div v-if="item.name" class="order-name">
|
|
40
|
+
{{ item.name }}:
|
|
41
|
+
</div>
|
|
42
|
+
<div class="order-value">
|
|
43
|
+
{{ item.value }}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
<div v-if="bottomCustomField.length !== 0" class="quick-order-link" @click="sendOrder">
|
|
48
|
+
{{ TUITranslateService.t("发送") }}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</template>
|
|
53
|
+
<script lang="ts" setup>
|
|
54
|
+
import vue from '../../../../adapter-vue';
|
|
55
|
+
import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
|
56
|
+
import cardToolbarCloseIcon from '../../../../assets/dialog-close.png';
|
|
57
|
+
import Icon from '../../../common/Icon.vue';
|
|
58
|
+
import { QuickOrderModel } from '../../../../interface';
|
|
59
|
+
import { isPC } from '../../../../utils/env';
|
|
60
|
+
const { computed } = vue;
|
|
61
|
+
interface IProps {
|
|
62
|
+
bottomQuickOrder: QuickOrderModel;
|
|
63
|
+
}
|
|
64
|
+
const emits = defineEmits(['closeOrder','sendOrder']);
|
|
65
|
+
|
|
66
|
+
const props = withDefaults(defineProps<IProps>(), {
|
|
67
|
+
bottomQuickOrder: () => ({} as QuickOrderModel),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const customField = computed(() => {
|
|
71
|
+
return Array.isArray(props.bottomQuickOrder?.customField) ? props.bottomQuickOrder.customField : [];
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const topCustomField = computed(() => {
|
|
75
|
+
return customField.value.filter((item) => item.position === 'top');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const bottomCustomField = computed(() => {
|
|
79
|
+
return customField.value.filter((item) => item.position !== 'top');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const sendOrder = () => {
|
|
83
|
+
emits('sendOrder');
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const closeOrder = () => {
|
|
87
|
+
emits('closeOrder');
|
|
88
|
+
};
|
|
89
|
+
</script>
|
|
90
|
+
<style scoped lang="scss">
|
|
91
|
+
.bottom-quick-order-line {
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: flex-end;
|
|
94
|
+
}
|
|
95
|
+
.bottom-quick-order-container {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
background: #fff;
|
|
99
|
+
padding: 10px 30px 10px 10px;
|
|
100
|
+
border-radius: 8px;
|
|
101
|
+
margin: 0 10px 0 10px;
|
|
102
|
+
position: relative;
|
|
103
|
+
font-family: PingFangSC-Regular;
|
|
104
|
+
}
|
|
105
|
+
.bottom-quick-order-container-pc {
|
|
106
|
+
width: 300px;
|
|
107
|
+
box-shadow: rgb(204, 204, 204) 0px 0px 10px;
|
|
108
|
+
margin-bottom: 5px;
|
|
109
|
+
}
|
|
110
|
+
.quick-order-custom-field {
|
|
111
|
+
font-size: 12px;
|
|
112
|
+
max-width: 100%;
|
|
113
|
+
overflow: hidden;
|
|
114
|
+
}
|
|
115
|
+
.custom-field-line {
|
|
116
|
+
display: flex;
|
|
117
|
+
gap: 5px;
|
|
118
|
+
font-size: 12px;
|
|
119
|
+
line-height: 22px;
|
|
120
|
+
}
|
|
121
|
+
.order-name {
|
|
122
|
+
color: #878787;
|
|
123
|
+
margin-right: 3px;
|
|
124
|
+
white-space: nowrap;
|
|
125
|
+
flex-shrink: 0;
|
|
126
|
+
}
|
|
127
|
+
.order-value {
|
|
128
|
+
flex: 1;
|
|
129
|
+
min-width: 0;
|
|
130
|
+
white-space: nowrap;
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
text-overflow: ellipsis;
|
|
133
|
+
}
|
|
134
|
+
.quick-order-main {
|
|
135
|
+
display: flex;
|
|
136
|
+
margin: 10px 0 10px 0;
|
|
137
|
+
}
|
|
138
|
+
.quick-order-img {
|
|
139
|
+
width: 60px;
|
|
140
|
+
height: 60px;
|
|
141
|
+
border-radius: 10px;
|
|
142
|
+
margin-right: 15px;
|
|
143
|
+
flex-shrink: 0;
|
|
144
|
+
object-fit: cover;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.quick-order-information {
|
|
148
|
+
width: 100%;
|
|
149
|
+
margin-right: 5px;
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-direction: column;
|
|
152
|
+
justify-content: space-between;
|
|
153
|
+
|
|
154
|
+
.quick-order-title {
|
|
155
|
+
color: #000000;
|
|
156
|
+
font-size: 14px;
|
|
157
|
+
display: -webkit-box;
|
|
158
|
+
overflow: hidden;
|
|
159
|
+
text-overflow: ellipsis;
|
|
160
|
+
-webkit-line-clamp: 2;
|
|
161
|
+
-webkit-box-orient: vertical;
|
|
162
|
+
word-break: break-word;
|
|
163
|
+
overflow-wrap: anywhere;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.quick-order-description-block {
|
|
167
|
+
display: flex;
|
|
168
|
+
justify-content: space-between;
|
|
169
|
+
align-items: center;
|
|
170
|
+
gap: 5px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.quick-order-description {
|
|
174
|
+
font-size: 16px;
|
|
175
|
+
max-width: 120px;
|
|
176
|
+
color: #1C66E5;
|
|
177
|
+
overflow: hidden;
|
|
178
|
+
text-overflow: ellipsis;
|
|
179
|
+
white-space: nowrap;
|
|
180
|
+
font-weight: 500;
|
|
181
|
+
flex: 0 1 120px;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
.quick-order-link {
|
|
185
|
+
cursor: pointer;
|
|
186
|
+
background-color: #1c66e5;
|
|
187
|
+
color: #ffffff;
|
|
188
|
+
font-size: 12px;
|
|
189
|
+
padding: 2px 18px;
|
|
190
|
+
line-height: 22px;
|
|
191
|
+
border-radius: 23px;
|
|
192
|
+
flex: 0 0 auto;
|
|
193
|
+
margin-left: auto;
|
|
194
|
+
}
|
|
195
|
+
.close-icon {
|
|
196
|
+
position: absolute;
|
|
197
|
+
right: 10px;
|
|
198
|
+
top: 10px;
|
|
199
|
+
cursor: pointer;
|
|
200
|
+
}
|
|
201
|
+
</style>
|
|
@@ -172,6 +172,13 @@
|
|
|
172
172
|
:imageList="imageMessageList"
|
|
173
173
|
@close="onImagePreviewerClose"
|
|
174
174
|
/>
|
|
175
|
+
<BottomQuickOrder
|
|
176
|
+
class="bottom-quick-order"
|
|
177
|
+
v-if="isNonEmptyObject(props.bottomQuickOrder) && props.showBottomQuickOrder"
|
|
178
|
+
:bottomQuickOrder="props.bottomQuickOrder"
|
|
179
|
+
@closeOrder="closeBottomQuickOrder"
|
|
180
|
+
@sendOrder="sendBottomQuickOrder"
|
|
181
|
+
/>
|
|
175
182
|
</div>
|
|
176
183
|
</div>
|
|
177
184
|
</template>
|
|
@@ -208,6 +215,7 @@ import MessageRevoked from './message-tool/message-revoked.vue';
|
|
|
208
215
|
import MessagePlugin from '../message-list/message-elements/message-desk/message-plugin-web.vue';
|
|
209
216
|
import MessageThinking from './message-elements/message-thinking.vue';
|
|
210
217
|
import ScrollButton from './scroll-button/index.vue';
|
|
218
|
+
import BottomQuickOrder from './bottom-quick-order/index.vue';
|
|
211
219
|
import { isPluginMessage } from './message-elements/message-desk/index';
|
|
212
220
|
import Dialog from '../../common/Dialog/index.vue';
|
|
213
221
|
import ImagePreviewer from '../../common/ImagePreviewer/index-web.vue';
|
|
@@ -217,10 +225,12 @@ import chatStorage from '../../../utils/chatStorage';
|
|
|
217
225
|
import {
|
|
218
226
|
isEnabledMessageReadReceiptGlobal,
|
|
219
227
|
deepCopy,
|
|
228
|
+
isNonEmptyObject,
|
|
220
229
|
} from '../../../utils/utils';
|
|
221
230
|
import { isMessageInvisible, isThinkingMessage, isThinkingMessageOverTime, JSONToObject } from '../../../utils/index';
|
|
222
231
|
import { isCustomerConversation } from '../../../index';
|
|
223
232
|
import { CUSTOM_MESSAGE_SRC } from '../../../constant';
|
|
233
|
+
import { QuickOrderModel } from '../../../interface';
|
|
224
234
|
|
|
225
235
|
interface ScrollConfig {
|
|
226
236
|
scrollToMessage?: IMessageModel;
|
|
@@ -231,9 +241,18 @@ interface ScrollConfig {
|
|
|
231
241
|
};
|
|
232
242
|
}
|
|
233
243
|
|
|
244
|
+
interface IProps {
|
|
245
|
+
bottomQuickOrder?: QuickOrderModel;
|
|
246
|
+
showBottomQuickOrder: boolean;
|
|
247
|
+
}
|
|
248
|
+
const props = withDefaults(defineProps<IProps>(), {
|
|
249
|
+
showBottomQuickOrder: false,
|
|
250
|
+
});
|
|
251
|
+
|
|
234
252
|
interface IEmits {
|
|
235
253
|
(key: 'closeInputToolBar'): void;
|
|
236
254
|
(key: 'handleEditor', message: IMessageModel, type: string): void;
|
|
255
|
+
(key: 'closeBottomQuickOrder'): void;
|
|
237
256
|
}
|
|
238
257
|
|
|
239
258
|
const emits = defineEmits<IEmits>();
|
|
@@ -742,6 +761,26 @@ function setAudioPlayed(messageID: string) {
|
|
|
742
761
|
};
|
|
743
762
|
}
|
|
744
763
|
|
|
764
|
+
function closeBottomQuickOrder() {
|
|
765
|
+
emits('closeBottomQuickOrder');
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function sendBottomQuickOrder() {
|
|
769
|
+
TUIChatService.sendCustomMessage({
|
|
770
|
+
to: currentConversationID.value.replace('C2C', ''),
|
|
771
|
+
conversationType: 'C2C',
|
|
772
|
+
payload: {
|
|
773
|
+
data: JSON.stringify({
|
|
774
|
+
src: CUSTOM_MESSAGE_SRC.PRODUCT_CARD,
|
|
775
|
+
content: props.bottomQuickOrder,
|
|
776
|
+
customerServicePlugin: 0,
|
|
777
|
+
}),
|
|
778
|
+
},
|
|
779
|
+
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
|
780
|
+
});
|
|
781
|
+
closeBottomQuickOrder();
|
|
782
|
+
}
|
|
783
|
+
|
|
745
784
|
defineExpose({
|
|
746
785
|
scrollToLatestMessage,
|
|
747
786
|
});
|
|
@@ -752,4 +791,11 @@ defineExpose({
|
|
|
752
791
|
.row-reverse {
|
|
753
792
|
flex-direction: row-reverse;
|
|
754
793
|
}
|
|
794
|
+
.bottom-quick-order {
|
|
795
|
+
position: absolute;
|
|
796
|
+
bottom: 0;
|
|
797
|
+
right: 0;
|
|
798
|
+
z-index: 1000;
|
|
799
|
+
width: 100%;
|
|
800
|
+
}
|
|
755
801
|
</style>
|
|
@@ -19,7 +19,12 @@ export const marked = new Marked(
|
|
|
19
19
|
if (href) {
|
|
20
20
|
// 匹配以 http:// 或 https:// 开头,所有 URL 主体字符,遇到第一个非主体字符(如中文括号、空格、表情符号等)时停止
|
|
21
21
|
let ret = href.replace(/https?:\/\/[\w\-./?=&:#]+(?=[^\w\-./?=&:#]|$)/g, (matchedUrl) => {
|
|
22
|
-
|
|
22
|
+
let isURLInText = false;
|
|
23
|
+
if (matchedUrl !== href) {
|
|
24
|
+
// 如果 text 里包含 url,我们就用 matchedUrl 作为 a 标签的值;否则用 text 作为值
|
|
25
|
+
isURLInText = true;
|
|
26
|
+
}
|
|
27
|
+
return `<a target="_blank" rel="noreferrer noopenner" class="message-marked_link" href="${matchedUrl || ''}" title="${title}">${isURLInText ? matchedUrl : text}</a>`;
|
|
23
28
|
});
|
|
24
29
|
if (ret === href) {
|
|
25
30
|
Log.w(`Unable to extract url, href:${href}`);
|