@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +316 -71
  3. package/components/CustomerServiceChat/emoji-config/index.ts +3 -2
  4. package/components/CustomerServiceChat/index-web.vue +17 -4
  5. package/components/CustomerServiceChat/message-input/index-web.vue +9 -2
  6. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +11 -0
  7. package/components/CustomerServiceChat/message-input/message-input-quote/index.vue +31 -6
  8. package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +14 -1
  9. package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +26 -2
  10. package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +14 -1
  11. package/components/CustomerServiceChat/message-list/bottom-quick-order/index.vue +201 -0
  12. package/components/CustomerServiceChat/message-list/index-web.vue +46 -0
  13. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/marked.ts +6 -1
  14. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +1 -1
  15. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-product-card.vue +140 -87
  16. package/components/CustomerServiceChat/message-list/message-elements/message-quote/index-web.vue +18 -9
  17. package/components/CustomerServiceChat/style/h5.scss +1 -0
  18. package/components/CustomerServiceChat/utils/sendMessage.ts +5 -0
  19. package/constant.ts +3 -3
  20. package/interface.ts +15 -1
  21. package/locales/en/TUIChat.ts +2 -1
  22. package/locales/en/aidesk.ts +4 -1
  23. package/locales/fil/TUIChat.ts +1 -0
  24. package/locales/fil/aidesk.ts +3 -0
  25. package/locales/id/TUIChat.ts +161 -160
  26. package/locales/id/aidesk.ts +3 -0
  27. package/locales/ja/TUIChat.ts +162 -161
  28. package/locales/ja/aidesk.ts +3 -0
  29. package/locales/ms/TUIChat.ts +162 -161
  30. package/locales/ms/aidesk.ts +3 -0
  31. package/locales/ru/TUIChat.ts +155 -154
  32. package/locales/ru/aidesk.ts +3 -0
  33. package/locales/th/TUIChat.ts +162 -161
  34. package/locales/th/aidesk.ts +3 -0
  35. package/locales/vi/TUIChat.ts +153 -152
  36. package/locales/vi/aidesk.ts +3 -0
  37. package/locales/zh_cn/TUIChat.ts +1 -0
  38. package/locales/zh_cn/aidesk.ts +3 -0
  39. package/locales/zh_tw/TUIChat.ts +1 -0
  40. package/locales/zh_tw/aidesk.ts +3 -0
  41. package/package.json +4 -6
  42. package/server.ts +42 -7
  43. 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="11px"
25
- height="11px"
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 = TUITranslateService.t('TUIChat.自定义');
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?.message && options?.type === 'quote') {
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: #fff;
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: 0px 0 5px 5px;
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
- return `<a target="_blank" rel="noreferrer noopenner" class="message-marked_link" href="${matchedUrl || ''}" title="${title}">${matchedUrl}</a>`;
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}`);
@@ -17,7 +17,7 @@
17
17
  </div>
18
18
  <div
19
19
  v-if="
20
- payload.src === CUSTOM_MESSAGE_SRC.ROBOT_MSG
20
+ payload.src === CUSTOM_MESSAGE_SRC.ROBOT_MSG && payload.subtype === 'welcome_msg'
21
21
  "
22
22
  >
23
23
  <MessageIMRobotWelcome