@tencentcloud/ai-desk-customer-vue 1.5.10 → 1.5.11

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