@tencentcloud/ai-desk-customer-vue 1.3.0 → 1.5.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +23 -4
  2. package/assets/customer_avatar.png +0 -0
  3. package/assets/face.svg +10 -0
  4. package/assets/feedback_dialog_close.svg +3 -0
  5. package/assets/feedback_dislike_after.svg +3 -0
  6. package/assets/feedback_dislike_before.svg +10 -0
  7. package/assets/feedback_dislike_hover.svg +10 -0
  8. package/assets/feedback_like_after.svg +14 -0
  9. package/assets/feedback_like_before.svg +10 -0
  10. package/assets/feedback_like_hover.svg +10 -0
  11. package/assets/files.svg +5 -0
  12. package/assets/green_check.svg +4 -0
  13. package/assets/image.svg +8 -0
  14. package/assets/rating_tool_icon.svg +5 -0
  15. package/assets/rating_tool_icon_h5.svg +1 -0
  16. package/assets/video.svg +8 -0
  17. package/assets/video_h5.svg +1 -0
  18. package/components/CustomerServiceChat/chat-header/index-web.vue +16 -14
  19. package/components/CustomerServiceChat/index-web.vue +87 -13
  20. package/components/CustomerServiceChat/message-input/index-web.vue +31 -5
  21. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +25 -0
  22. package/components/CustomerServiceChat/message-input/message-input-quote/index.vue +29 -20
  23. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/emoji-picker-dialog.vue +36 -36
  24. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/index.vue +1 -1
  25. package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +6 -8
  26. package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +11 -16
  27. package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +61 -18
  28. package/components/CustomerServiceChat/message-input-toolbar/rating-tool/index.vue +72 -0
  29. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/h5.scss +10 -1
  30. package/components/CustomerServiceChat/message-input-toolbar/user-define-input-tool.vue +80 -0
  31. package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +9 -14
  32. package/components/CustomerServiceChat/message-list/index-web.vue +38 -6
  33. package/components/CustomerServiceChat/message-list/message-elements/feedback-button.vue +369 -0
  34. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +81 -15
  35. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/marked.ts +17 -10
  36. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-branch.vue +18 -10
  37. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-concurrency-limit.vue +1 -1
  38. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +13 -6
  39. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-ivr-form/form-branch.vue +117 -0
  40. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/{message-single-form → message-ivr-form}/form-input.vue +65 -111
  41. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/{message-single-form → message-ivr-form}/index.vue +7 -2
  42. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +25 -9
  43. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +5 -3
  44. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-mobile/input-mobile.vue +1 -0
  45. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-mobile/label-mobile.vue +4 -2
  46. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-pc/label-pc.vue +5 -3
  47. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-mobile.vue +17 -5
  48. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-pc.vue +21 -1
  49. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-order.vue +3 -3
  50. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-product-card.vue +2 -1
  51. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-rating/message-rating-number.vue +9 -13
  52. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-rating/message-rating-star.vue +11 -18
  53. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-robot-welcome.vue +1 -0
  54. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-stream.vue +14 -10
  55. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-timeout-warning.vue +29 -0
  56. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/styles/common.scss +1 -0
  57. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-plugin-web.vue +22 -2
  58. package/components/CustomerServiceChat/message-list/message-elements/message-file.vue +1 -1
  59. package/components/CustomerServiceChat/message-list/message-elements/message-quote/index-web.vue +6 -24
  60. package/components/CustomerServiceChat/message-list/message-elements/message-text.vue +0 -9
  61. package/components/CustomerServiceChat/message-list/message-elements/read-status/index.vue +31 -20
  62. package/components/CustomerServiceChat/message-list/message-elements/simple-message-list/index.vue +2 -1
  63. package/components/CustomerServiceChat/message-list/scroll-button/index.vue +3 -3
  64. package/components/CustomerServiceChat/message-list/style/web.scss +2 -1
  65. package/components/CustomerServiceChat/message-toolbar-button/index.vue +111 -42
  66. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-end-human-service.vue +59 -0
  67. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-human-service.vue +55 -0
  68. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-service-rating.vue +59 -0
  69. package/components/common/Toast/index-web.vue +4 -2
  70. package/constant.ts +25 -0
  71. package/interface.ts +35 -5
  72. package/locales/en/aidesk.ts +28 -15
  73. package/locales/fil/aidesk.ts +28 -15
  74. package/locales/id/aidesk.ts +28 -15
  75. package/locales/ja/aidesk.ts +28 -15
  76. package/locales/ms/aidesk.ts +28 -15
  77. package/locales/ru/aidesk.ts +28 -15
  78. package/locales/th/aidesk.ts +28 -15
  79. package/locales/vi/aidesk.ts +28 -15
  80. package/locales/zh_cn/aidesk.ts +28 -15
  81. package/locales/zh_tw/aidesk.ts +28 -15
  82. package/package.json +1 -1
  83. package/server.ts +5 -1
  84. package/utils/state.js +30 -0
  85. package/utils/utils.ts +48 -1
  86. package/assets/face.png +0 -0
  87. package/assets/files.png +0 -0
  88. package/assets/image.png +0 -0
  89. package/assets/video.png +0 -0
  90. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-single-form/form-branch.vue +0 -68
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="message-rating-star">
3
- <p class="rating-head">
3
+ <p class="rating-head-tail">
4
4
  {{ props.ratingTemplate.head }}
5
5
  </p>
6
6
  <div class="rating-card">
@@ -49,7 +49,7 @@
49
49
  </div>
50
50
  <p
51
51
  v-if="hasReply"
52
- class="rating-tail"
52
+ class="rating-head-tail"
53
53
  :style="{
54
54
  marginTop: 20 + 'px',
55
55
  }"
@@ -125,17 +125,15 @@ export default {
125
125
  });
126
126
 
127
127
  const setValue = (val: number) => {
128
- if (hasReply.value) {
129
- return;
128
+ if (!hasReply.value && !hasExpire.value) {
129
+ value.value = val;
130
130
  }
131
- value.value = val;
132
131
  };
133
132
 
134
133
  const setHoverValue = (value: number) => {
135
- if (hasReply.value) {
136
- return;
134
+ if (!hasReply.value && !hasExpire.value) {
135
+ hoverValue.value = value;
137
136
  }
138
- hoverValue.value = value;
139
137
  };
140
138
 
141
139
  const submitRatingStar = async () => {
@@ -176,16 +174,11 @@ export default {
176
174
  };
177
175
  </script>
178
176
  <style lang="scss" scoped>
179
- .rating-head {
180
- font-size: 14px;
181
- font-weight: 400;
182
- color: #999;
183
- }
184
-
185
- .rating-tail {
177
+ .rating-head-tail {
186
178
  font-size: 14px;
187
179
  font-weight: 400;
188
180
  color: #999;
181
+ word-break: break-word;
189
182
  }
190
183
 
191
184
  .card-title {
@@ -199,11 +192,11 @@ export default {
199
192
  border-radius: 20px;
200
193
  border: 0;
201
194
  margin-top: 10px;
202
- padding-top: 20px;
203
- padding-bottom: 20px;
204
-
195
+ min-width: 240px;
196
+ padding: 10px;
205
197
  button:disabled {
206
198
  background: #d8d8d8;
199
+ cursor: default;
207
200
  }
208
201
  }
209
202
 
@@ -164,6 +164,7 @@ export default {
164
164
  font-weight: 500;
165
165
  color: #1c66e5;
166
166
  gap: 10px;
167
+ word-break: break-word;
167
168
  }
168
169
 
169
170
  .welcome-item:hover {
@@ -1,13 +1,15 @@
1
1
  <template>
2
- <div class="message-stream">
3
- <span v-if="isCursorBlinking" class="blinking-cursor">|</span>
4
- <pre ref="preRef" :class="['message-marked']" v-html="displayedContent" />
5
- </div>
6
- <div v-if="image" class="rich-image-previewer" @click="closeImage">
7
- <img
8
- class="rich-image-preview"
9
- :src="imageSrc"
10
- />
2
+ <div>
3
+ <div class="message-stream">
4
+ <span v-if="isCursorBlinking" class="blinking-cursor">|</span>
5
+ <pre ref="preRef" :class="['message-marked']" v-html="displayedContent" />
6
+ </div>
7
+ <div v-if="image" class="rich-image-previewer" @click="closeImage">
8
+ <img
9
+ class="rich-image-preview"
10
+ :src="imageSrc"
11
+ />
12
+ </div>
11
13
  </div>
12
14
  </template>
13
15
 
@@ -146,9 +148,11 @@ const closeImage = () => {
146
148
  <style lang="scss" scoped>
147
149
  .message-stream {
148
150
  overflow-wrap: break-word;
149
- word-break: keep-all;
151
+ word-break: normal;
150
152
  white-space: normal;
151
153
  font-size: 14px;
154
+ font-family: PingFangSC-Regular;
155
+ user-select: text;
152
156
  }
153
157
 
154
158
  .blinking-cursor {
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <div>
3
+ {{ props.payload.content }}
4
+ </div>
5
+ </template>
6
+
7
+ <script lang="ts">
8
+ import { customerServicePayloadType } from '../../../../../../interface';
9
+
10
+ interface Props {
11
+ payload: customerServicePayloadType;
12
+ }
13
+
14
+ export default {
15
+ props: {
16
+ payload: {
17
+ type: Object as () => customerServicePayloadType,
18
+ default: () => ({}),
19
+ },
20
+ },
21
+ setup(props: Props) {
22
+ return {
23
+ props,
24
+ };
25
+ },
26
+ };
27
+ </script>
28
+ <style lang="scss" scoped>
29
+ </style>
@@ -125,6 +125,7 @@
125
125
  margin: 0;
126
126
  padding: 0;
127
127
  font-family: PingFangSC-Regular;
128
+ user-select: text;
128
129
 
129
130
  .message-marked_code-container {
130
131
  display: flex;
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <MessagePluginLayout
3
3
  :message="props.message"
4
+ :blinkMessageIDList="props.blinkMessageIDList"
4
5
  :showStyle="pluginMessageType.showStyle"
5
6
  :bubbleClassNameList="[
6
7
  pluginMessageType.pluginType === 'room' ? 'message-bubble-room' : '',
@@ -14,8 +15,12 @@
14
15
  v-if="pluginMessageType.pluginType === 'customer'"
15
16
  :message="props.message"
16
17
  @heightChanged="onHeightChanged"
18
+ @messageSent="onMessageSent"
17
19
  />
18
20
  </template>
21
+ <template #messageTip>
22
+ <TimeoutWarning :payload="payload"/>
23
+ </template>
19
24
  </MessagePluginLayout>
20
25
  </template>
21
26
 
@@ -29,6 +34,10 @@ import {
29
34
  } from './message-desk-elements/index';
30
35
  import MessagePluginLayout from './message-plugin-layout-web.vue';
31
36
  import MessageCustomerService from './message-desk-elements/message-desk.vue';
37
+ import { CUSTOM_MESSAGE_SRC } from '../../../../../constant';
38
+ import TimeoutWarning from './message-desk-elements/message-timeout-warning.vue';
39
+ import { customerServicePayloadType } from '../../../../../interface';
40
+ import { JSONToObject } from '../../../../../utils/index';
32
41
  const { computed } = vue;
33
42
 
34
43
  interface IProps {
@@ -46,18 +55,26 @@ const emits = defineEmits([
46
55
  'handleToggleMessageItem',
47
56
  'handleH5LongPress',
48
57
  'heightChanged',
58
+ 'messageSent',
49
59
  ]);
50
60
  const messageModel = computed(() => TUIStore.getMessageModel(props.message.ID));
51
61
 
62
+ const payload = computed<customerServicePayloadType>(() => {
63
+ return props.message && JSONToObject(props.message?.payload?.data);
64
+ });
65
+
52
66
  const pluginMessageType = computed<{ pluginType: string; showStyle: string }>(
53
67
  () => {
54
68
  let typeObj = { pluginType: '', showStyle: '' };
55
69
  if (isCustomerServicePluginMessage(messageModel.value as any)) {
70
+ const src = payload.value.src;
56
71
  typeObj = {
57
- pluginType: 'customer',
72
+ pluginType: src === CUSTOM_MESSAGE_SRC.TIMEOUT_WARNING ? 'customer_tip' : 'customer',
58
73
  showStyle: isCustomServiceMessageInvisible(messageModel.value as any)
59
74
  ? ''
60
- : 'bubble',
75
+ : src === CUSTOM_MESSAGE_SRC.TIMEOUT_WARNING
76
+ ? 'tip'
77
+ : 'bubble',
61
78
  };
62
79
  }
63
80
  return typeObj;
@@ -81,6 +98,9 @@ const handleH5LongPress = (e: any, message: IMessageModel, type: string) => {
81
98
  const onHeightChanged = () => {
82
99
  emits('heightChanged');
83
100
  };
101
+ const onMessageSent = () => {
102
+ emits('messageSent');
103
+ };
84
104
  </script>
85
105
 
86
106
  <style lang="scss" scoped>
@@ -22,7 +22,7 @@ import {
22
22
  IMessageModel,
23
23
  } from '@tencentcloud/chat-uikit-engine';
24
24
  import Icon from '../../../common/Icon.vue';
25
- import files from '../../../../assets/files.png';
25
+ import files from '../../../../assets/files.svg';
26
26
  import { isUniFrameWork } from '../../../../utils/env';
27
27
  import type { IFileMessageContent } from '../../../../interface';
28
28
  const { withDefaults } = vue;
@@ -1,10 +1,5 @@
1
1
  <template>
2
2
  <div style="display:flex;width: fit-content;">
3
- <div v-if="hasQuoteContent&&isH5" :class="{
4
- 'mobile-left-box': true,
5
- reverse: message.flow === 'out',
6
- in:message.flow === 'in',
7
- }"></div>
8
3
  <div
9
4
  v-if="hasQuoteContent"
10
5
  :class="{
@@ -218,8 +213,8 @@ async function scrollToOriginalMessage() {
218
213
  padding: 12px;
219
214
  font-size: 12px;
220
215
  color: #666;
221
- word-wrap: break-word;
222
- word-break: break-all;
216
+ overflow-wrap: break-word;
217
+ word-break: normal;
223
218
  background-color: #fbfbfb;
224
219
  border-radius: 8px;
225
220
  line-height: 16.8px;
@@ -233,12 +228,12 @@ async function scrollToOriginalMessage() {
233
228
  .reverse.reference-content {
234
229
  margin-right: 44px;
235
230
  margin-left: auto;
236
- border-radius: 0 8px 8px 0;
231
+ border-radius: 8px;
237
232
  }
238
233
  .isMobile.reference-content.in {
239
234
  margin-right: auto;
240
235
  background: #f3f4f7;
241
- border-radius: 0 8px 8px 0;
236
+ border-radius: 8px;
242
237
  }
243
238
  .isMobile.reverse.reference-content {
244
239
  margin-right: 10px;
@@ -247,20 +242,6 @@ async function scrollToOriginalMessage() {
247
242
  color: white;
248
243
 
249
244
  }
250
- .mobile-left-box {
251
- display:inline-block;
252
- background: #FFFFFF33;
253
- width: 5px;
254
- margin-top: 4px;
255
- border-radius: 8px 0 0 8px;
256
- }
257
- .mobile-left-box.in {
258
- display:inline-block;
259
- background: #e1e2e5;
260
- width: 5px;
261
- margin-top: 4px;
262
- border-radius: 8px 0 0 8px;
263
- }
264
245
  .revoked-text {
265
246
  color: #999;
266
247
  }
@@ -274,7 +255,8 @@ async function scrollToOriginalMessage() {
274
255
  .max-double-line {
275
256
  font-family: PingFangSC-Regular;
276
257
  font-size: 14px;
277
- word-break: break-all;
258
+ overflow-wrap: break-word;
259
+ word-break: normal;
278
260
  overflow: hidden;
279
261
  display: -webkit-box;
280
262
  max-height: 33px;
@@ -84,15 +84,6 @@ watchEffect(() => {
84
84
  }
85
85
 
86
86
  .text {
87
- white-space: pre-wrap;
88
- font-size: 14px;
89
- text-size-adjust: none;
90
- font-family: PingFangSC-Regular;
91
- overflow-wrap: break-word;
92
- word-break: break-all;
93
- }
94
-
95
- .text:lang(en) {
96
87
  white-space: pre-wrap;
97
88
  font-size: 14px;
98
89
  text-size-adjust: none;
@@ -18,24 +18,24 @@ import TUIChatEngine, {
18
18
  IMessageModel,
19
19
  TUITranslateService,
20
20
  } from '@tencentcloud/chat-uikit-engine';
21
+ import { ReadState } from '../../../../../constant';
21
22
  const { computed, ref, onMounted, onUnmounted } = vue;
22
23
 
23
24
  interface IProps {
24
25
  message: IMessageModel;
26
+ prevStatus: ReadState;
25
27
  }
26
28
 
27
29
  const props = withDefaults(defineProps<IProps>(), {
28
30
  message: () => ({} as IMessageModel),
31
+ prevStatus: ReadState.Unread,
29
32
  });
30
- const ReadStatus = true;
31
-
32
- enum ReadState {
33
- Read,
34
- Unread,
35
- AllRead,
36
- NotShow,
37
- PartiallyRead,
33
+ const isInHumanService = ref(false);
34
+
35
+ interface IEmits {
36
+ (e: 'setStatus', status:ReadState): void;
38
37
  }
38
+ const emits = defineEmits<IEmits>();
39
39
 
40
40
  const TYPES = TUIChatEngine.TYPES;
41
41
  // User-level read receipt toggle has the highest priority.
@@ -48,18 +48,25 @@ onMounted(() => {
48
48
  TUIStore.watch(StoreName.USER, {
49
49
  displayMessageReadReceipt: onDisplayMessageReadReceiptUpdate,
50
50
  });
51
+ TUIStore.watch(StoreName.CUSTOM, {
52
+ isInHumanService: onInHumanServiceUpdate,
53
+ });
51
54
  });
52
55
 
53
56
  onUnmounted(() => {
54
57
  TUIStore.unwatch(StoreName.USER, {
55
58
  displayMessageReadReceipt: onDisplayMessageReadReceiptUpdate,
56
59
  });
60
+ TUIStore.unwatch(StoreName.CUSTOM, {
61
+ isInHumanService: onInHumanServiceUpdate,
62
+ });
57
63
  });
58
64
 
65
+ const onInHumanServiceUpdate = (value: boolean) => {
66
+ isInHumanService.value = value;
67
+ };
68
+
59
69
  const isShowReadStatus = computed<boolean>(() => {
60
- if (!ReadStatus) {
61
- return false;
62
- }
63
70
  if (!isDisplayMessageReadReceipt.value) {
64
71
  return false;
65
72
  }
@@ -127,23 +134,27 @@ const readState = computed<ReadState>(() => {
127
134
  return isPeerRead ? ReadState.Read : ReadState.Unread;
128
135
  }
129
136
  } else if (conversationType === 'GROUP') {
130
- if (needReadReceipt) {
131
- if (readCount === 0) {
132
- return ReadState.Unread;
133
- } else if (unreadCount === 0) {
134
- return ReadState.AllRead;
135
- } else {
136
- return ReadState.PartiallyRead;
137
- }
138
- } else {
137
+ if (!needReadReceipt) {
139
138
  return ReadState.NotShow;
140
139
  }
140
+ if (!isInHumanService.value) {
141
+ return ReadState.AllRead;
142
+ }
143
+ // 特殊判断: 群里只有1人时设置已读 (未接到人工时)
144
+ if (readCount === 0) {
145
+ return props.prevStatus === ReadState.AllRead ? ReadState.AllRead : ReadState.Unread;
146
+ } else if (unreadCount === 0) {
147
+ return ReadState.AllRead;
148
+ } else {
149
+ return ReadState.PartiallyRead;
150
+ }
141
151
  }
142
152
  return ReadState.Unread;
143
153
  });
144
154
 
145
155
  const readStatusText = computed(() => {
146
156
  const { readCount = 0 } = props.message.readReceiptInfo;
157
+ emits('setStatus', readState.value);
147
158
  switch (readState.value) {
148
159
  case ReadState.Read:
149
160
  return TUITranslateService.t('TUIChat.已读');
@@ -374,7 +374,8 @@ function resolveBigFaceUrl(bigFaceKey: string): string {
374
374
  .text {
375
375
  vertical-align: bottom;
376
376
  display: inline;
377
- word-break: break-all;
377
+ overflow-wrap: break-word;
378
+ word-break: normal;
378
379
  }
379
380
 
380
381
  .simple-emoji {
@@ -64,7 +64,7 @@ watch(
64
64
  onMounted(() => {
65
65
  TUIStore.watch(StoreName.CHAT, {
66
66
  messageList: onMessageListUpdated,
67
- newMessageList: onNewMessageListUpdated,
67
+ newMessageList: onNewMessageList,
68
68
  });
69
69
  TUIStore.watch(StoreName.CONV, {
70
70
  currentConversation: onCurrentConversationUpdated,
@@ -74,7 +74,7 @@ onMounted(() => {
74
74
  onUnmounted(() => {
75
75
  TUIStore.unwatch(StoreName.CHAT, {
76
76
  messageList: onMessageListUpdated,
77
- newMessageList: onNewMessageListUpdated,
77
+ newMessageList: onNewMessageList,
78
78
  });
79
79
  TUIStore.unwatch(StoreName.CONV, {
80
80
  currentConversation: onCurrentConversationUpdated,
@@ -97,7 +97,7 @@ function onMessageListUpdated(newMessageList: IMessageModel[]) {
97
97
  );
98
98
  }
99
99
 
100
- function onNewMessageListUpdated(newMessageList: IMessageModel[]) {
100
+ function onNewMessageList(newMessageList: IMessageModel[]) {
101
101
  if (Array.isArray(newMessageList) && isScrollButtonVisible.value) {
102
102
  newMessageList.forEach((message: IMessageModel) => {
103
103
  if (
@@ -48,7 +48,8 @@
48
48
  .tui-message-list {
49
49
  flex: 1;
50
50
  height: 100%;
51
- overflow: auto;
51
+ overflow-y: auto;
52
+ overflow-x: hidden;
52
53
 
53
54
  .message-more {
54
55
  font-size: 14px;
@@ -1,75 +1,144 @@
1
1
  <template>
2
- <div class="toolbar-button-container">
3
- <template v-for="(item, index) in props.toolbarButtonList">
4
- <div v-if="shouldRender(item)" :key="index"
5
- :class="['toolbar-button', isH5 ? 'toolbar-button-h5' : '']" @click="onClick(index)">
6
- <Icon v-if="item.icon" class="toolbar-button-icon" :file="item.icon" width="18px" height="18px"/>
7
- <div class="toolbar-button-text">
8
- {{ item.title }}
9
- </div>
10
-
11
- </div>
12
- </template>
13
- </div>
2
+ <div class="toolbar-button-container">
3
+ <template v-for="(item, index) in props.toolbarButtonList">
4
+ <ToolbarButtonHumanService v-if="item.presetId === TOOLBAR_BUTTON_TYPE.HUMAN_SERVICE && shouldRender(item) && !isInHumanService" :title="item.title" :icon="item.icon"/>
5
+ <ToolbarButtonServiceRating v-else-if="item.presetId === TOOLBAR_BUTTON_TYPE.SERVICE_RATING && shouldRender(item) && isInHumanService" :title="item.title" :icon="item.icon"/>
6
+ <ToolbarButtonEndHumanService v-else-if="item.presetId === TOOLBAR_BUTTON_TYPE.END_HUMAN_SERVICE && shouldRender(item) && isInHumanService" :title="item.title" :icon="item.icon"/>
7
+ <div v-else-if="shouldRender(item) && !item.presetId" :key="index"
8
+ :class="['toolbar-button', isH5 ? 'toolbar-button-h5' : '']" @click="onClick(item, index)">
9
+ <Icon v-if="item.icon" class="toolbar-button-icon" :file="item.icon" width="18px" height="18px"/>
10
+ <div class="toolbar-button-text">
11
+ {{ item.title }}
12
+ </div>
13
+ </div>
14
+ </template>
15
+ </div>
14
16
  </template>
15
17
  <script lang="ts" setup>
18
+ import vue from '../../../adapter-vue';
19
+ const { ref, onMounted, onUnmounted } = vue;
20
+ import {
21
+ TUIChatService,
22
+ TUIStore,
23
+ StoreName,
24
+ IConversationModel
25
+ } from '@tencentcloud/chat-uikit-engine';
16
26
  import { isH5 } from '../../../utils/env';
17
27
  import { ToolbarButtonModel } from '../../../interface';
18
28
  import Icon from '../../common/Icon.vue';
19
-
29
+ import { TOOLBAR_BUTTON_TYPE } from '../../../constant';
30
+ import { isEnabledMessageReadReceiptGlobal, openSafeUrl, getTo } from '../../../utils/utils';
31
+ import ToolbarButtonHumanService from './toolbar-button-human-service.vue';
32
+ import ToolbarButtonServiceRating from './toolbar-button-service-rating.vue';
33
+ import ToolbarButtonEndHumanService from './toolbar-button-end-human-service.vue';
20
34
  interface IProps {
21
- toolbarButtonList?: ToolbarButtonModel[]
35
+ toolbarButtonList?: ToolbarButtonModel[] | undefined;
22
36
  }
23
37
 
24
- const props = withDefaults(defineProps<IProps>(), {
25
- toolbarButtonList: () => [] as ToolbarButtonModel[]
38
+ const props = withDefaults(defineProps<IProps>(), {});
39
+
40
+ const isInHumanService = ref(false);
41
+ const currentConversation = ref<IConversationModel>();
42
+
43
+ onMounted(() => {
44
+ TUIStore.watch(StoreName.CONV, {
45
+ currentConversation: onCurrentConversationUpdate,
46
+ });
47
+ TUIStore.watch(StoreName.CUSTOM, {
48
+ isInHumanService: onInHumanServiceUpdate,
49
+ });
50
+ });
51
+
52
+ onUnmounted(() => {
53
+ TUIStore.unwatch(StoreName.CONV, {
54
+ currentConversation: onCurrentConversationUpdate,
55
+ });
56
+ TUIStore.unwatch(StoreName.CUSTOM, {
57
+ isInHumanService: onInHumanServiceUpdate,
58
+ });
26
59
  });
27
60
 
28
- function onClick(index: number) {
29
- if (typeof props.toolbarButtonList[index].clickEvent === 'function') {
30
- props.toolbarButtonList[index].clickEvent();
31
- }
61
+ const onCurrentConversationUpdate = (conversation: IConversationModel) => {
62
+ currentConversation.value = conversation;
63
+ }
64
+
65
+ const onInHumanServiceUpdate = (value: boolean) => {
66
+ isInHumanService.value = value;
67
+ };
68
+
69
+ function onClick(item:ToolbarButtonModel, index: number) {
70
+ if (item.type === 1 && item.content) {
71
+ TUIChatService.sendTextMessage({
72
+ to: getTo(currentConversation?.value),
73
+ conversationType: currentConversation?.value?.type,
74
+ payload: {
75
+ text: item.content
76
+ },
77
+ needReadReceipt: isEnabledMessageReadReceiptGlobal(),
78
+ });
79
+ } else if (item.type === 2 && item.content) {
80
+ openSafeUrl(item.content);
81
+ } else if (props.toolbarButtonList !== undefined && typeof props.toolbarButtonList[index].clickEvent === 'function') {
82
+ props.toolbarButtonList[index].clickEvent();
83
+ }
32
84
  }
33
85
 
34
86
  function shouldRender(item: ToolbarButtonModel) {
87
+ if (item.isEnabled === 1) {
88
+ return true;
89
+ } else if (item.isEnabled === 0) {
90
+ return false;
91
+ } else if (item.renderCondition) {
35
92
  return typeof item.renderCondition === 'function' ? item.renderCondition() : false;
93
+ }
94
+ return false;
36
95
  }
37
96
 
38
97
  </script>
39
98
  <style>
40
99
  .toolbar-button-container {
41
- display: flex;
42
- flex-direction: row !important;
43
- margin: 5px !important;
44
- align-items: center ;
100
+ display: flex;
101
+ flex-direction: row !important;
102
+ margin: 5px !important;
103
+ align-items: center ;
104
+ overflow-x: auto; /* 允许横向滚动 */
105
+ scrollbar-width: none; /* Firefox 隐藏滚动条 */
106
+ -ms-overflow-style: none; /* IE/Edge 隐藏滚动条 */
107
+ &::-webkit-scrollbar {
108
+ display: none; /* Chrome 隐藏滚动条 */
109
+ }
45
110
  }
46
111
 
47
112
  .toolbar-button {
48
- border: 1px solid #E7EAEF;
49
- padding: 5px 10px;
50
- border-radius: 20px;
51
- cursor: pointer;
52
- display: flex;
53
- align-items: center;
54
- margin-left: 10px;
55
- white-space: nowrap;
56
- user-select: none;
113
+ border: 1px solid #E7EAEF;
114
+ padding: 5px 10px;
115
+ border-radius: 20px;
116
+ cursor: pointer;
117
+ display: flex;
118
+ align-items: center;
119
+ margin-left: 10px;
120
+ white-space: nowrap;
121
+ user-select: none;
122
+ }
123
+
124
+ .toolbar-button:first-child {
125
+ margin-left: 5px;
57
126
  }
58
127
 
59
128
  .toolbar-button-h5 {
60
- border: none;
61
- background-color: #fff;
62
- box-shadow: 0px 2px 2px 0px rgba(70, 98, 140, 0.06);
129
+ border: none;
130
+ background-color: #fff;
131
+ box-shadow: 0px 2px 2px 0px rgba(70, 98, 140, 0.06);
63
132
  }
64
133
 
65
134
  .toolbar-button-icon {
66
- margin-right: 3px;
135
+ margin-right: 3px;
67
136
  }
68
137
  .toolbar-button-text {
69
- font-size: 12px;
70
- text-overflow: ellipsis;
71
- max-width: 100px;
72
- overflow: hidden;
73
- font-family: PingFangSC-Regular;
138
+ font-size: 12px;
139
+ text-overflow: ellipsis;
140
+ max-width: 100px;
141
+ overflow: hidden;
142
+ font-family: PingFangSC-Regular;
74
143
  }
75
144
  </style>