@tencentcloud/ai-desk-customer-vue 1.1.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +16 -3
  2. package/assets/customer_avatar.png +0 -0
  3. package/assets/face.svg +10 -0
  4. package/assets/files.svg +5 -0
  5. package/assets/image.svg +8 -0
  6. package/assets/rating_tool_icon.svg +5 -0
  7. package/assets/rating_tool_icon_h5.svg +1 -0
  8. package/assets/video.svg +8 -0
  9. package/assets/video_h5.svg +1 -0
  10. package/components/CustomerServiceChat/chat-header/index-web.vue +16 -14
  11. package/components/CustomerServiceChat/index-web.vue +72 -16
  12. package/components/CustomerServiceChat/message-input/index-web.vue +31 -5
  13. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +24 -0
  14. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/emoji-picker-dialog.vue +1 -1
  15. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/index.vue +1 -1
  16. package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +6 -8
  17. package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +11 -16
  18. package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +61 -18
  19. package/components/CustomerServiceChat/message-input-toolbar/rating-tool/index.vue +72 -0
  20. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/h5.scss +10 -1
  21. package/components/CustomerServiceChat/message-input-toolbar/user-define-input-tool.vue +80 -0
  22. package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +9 -14
  23. package/components/CustomerServiceChat/message-list/index-web.vue +34 -6
  24. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +65 -19
  25. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/marked.ts +1 -1
  26. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-branch.vue +30 -11
  27. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-concurrency-limit.vue +40 -0
  28. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-desk.vue +29 -7
  29. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/branch-pc.vue +107 -73
  30. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-branch/index.vue +53 -52
  31. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-mobile/input-mobile.vue +73 -80
  32. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-mobile/label-mobile.vue +21 -24
  33. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-mobile/radios-mobile.vue +115 -116
  34. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-pc/input-pc.vue +69 -73
  35. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-pc/label-pc.vue +21 -25
  36. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/component-pc/radio-pc.vue +87 -77
  37. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-mobile.vue +213 -200
  38. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/form-pc.vue +122 -113
  39. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-multi-form/index.vue +7 -7
  40. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-order.vue +141 -0
  41. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-rich-text.vue +5 -1
  42. package/components/CustomerServiceChat/message-list/message-elements/message-desk/message-desk-elements/message-stream.vue +25 -1
  43. package/components/CustomerServiceChat/message-list/message-elements/message-file.vue +1 -1
  44. package/components/CustomerServiceChat/message-list/scroll-button/index.vue +18 -6
  45. package/components/CustomerServiceChat/message-list/style/web.scss +2 -1
  46. package/components/CustomerServiceChat/message-toolbar-button/index.vue +111 -42
  47. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-end-human-service.vue +59 -0
  48. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-human-service.vue +55 -0
  49. package/components/CustomerServiceChat/message-toolbar-button/toolbar-button-service-rating.vue +59 -0
  50. package/components/common/BottomPopup/index.vue +1 -1
  51. package/constant.ts +25 -4
  52. package/interface.ts +35 -5
  53. package/locales/en/aidesk.ts +6 -3
  54. package/locales/fil/aidesk.ts +4 -1
  55. package/locales/id/aidesk.ts +7 -4
  56. package/locales/ja/aidesk.ts +5 -2
  57. package/locales/ms/aidesk.ts +5 -2
  58. package/locales/ru/aidesk.ts +6 -3
  59. package/locales/th/aidesk.ts +4 -1
  60. package/locales/vi/aidesk.ts +5 -2
  61. package/locales/zh_cn/aidesk.ts +5 -3
  62. package/locales/zh_tw/aidesk.ts +4 -1
  63. package/package.json +1 -1
  64. package/server.ts +11 -2
  65. package/utils/state.js +30 -0
  66. package/utils/utils.ts +48 -1
  67. package/assets/face.png +0 -0
  68. package/assets/files.png +0 -0
  69. package/assets/image.png +0 -0
  70. package/assets/video.png +0 -0
@@ -1,39 +1,37 @@
1
1
  <template>
2
- <div>
2
+ <div class="form-pc-container">
3
3
  <div class="title-container">
4
- <div class="form-title" v-if="checkTip()">
5
- {{ props.payload.content.tip }}
6
- </div>
7
- <div class="form-finish-title-right" v-if="finishSubmit || props.payload.nodeStatus == 2" >
8
- <Icon :src="iconSucess" style="margin:0px 4px"/>
9
- {{TUITranslateService.t("AIDesk.已提交")}}
10
- </div>
4
+ <div class="form-title" v-if="checkTip()">
5
+ {{ props.payload.content.tip }}
6
+ </div>
7
+ <div class="form-finish-title-right" v-if="finishSubmit || props.payload.nodeStatus == 2" >
8
+ <Icon :src="iconSucess" style="margin:0px 4px"/>
9
+ {{ TUITranslateService.t("AIDesk.已提交") }}
10
+ </div>
11
11
  </div>
12
-
12
+
13
13
  <div
14
- v-for="(item, index) in props.payload.content.inputVariables"
15
- :key="index"
14
+ v-for="(item, index) in props.payload.content.inputVariables"
15
+ :key="index"
16
16
  >
17
- <LabelPC :name="item.name" :is-required="item.isRequired" />
18
- <div v-if="!finishSubmit && item.formType == 0 && props.payload.nodeStatus == 0">
19
- <InputPC :placeholder="item.placeholder" :variableValue="item.variableValue" :name="item.name" :isRequired="item.isRequired" @input-change="handleInputChange" :validator="item.isRequired == 1 && isValid(item.name)"/>
20
- </div>
21
- <div v-else-if="!finishSubmit && item.formType == 1 && props.payload.nodeStatus == 0">
22
- <RadioPC :chooseItemList="item.chooseItemList" :name="item.name" :isRequired="item.isRequired" @input-change="handleInputChange" :validator="item.isRequired == 1 && isValid(item.name)"/>
23
- </div>
24
- <div v-else class="variable-value-container">
25
- {{ item.variableValue == '' || item.variableValue == null ? mapValue[item.name] : item.variableValue}}
26
- </div>
17
+ <LabelPC :name="item.name" :is-required="item.isRequired" />
18
+ <div v-if="!finishSubmit && item.formType == 0 && props.payload.nodeStatus !== 2">
19
+ <InputPC :placeholder="item.placeholder" :variableValue="item.variableValue" :name="item.name" :isRequired="item.isRequired" @input-change="handleInputChange" :validator="item.isRequired == 1 && isValid(item.name)" :isDisabled="props.payload.nodeStatus === 1"/>
20
+ </div>
21
+ <div v-else-if="!finishSubmit && item.formType == 1 && props.payload.nodeStatus !== 2">
22
+ <RadioPC :chooseItemList="item.chooseItemList" :name="item.name" :isRequired="item.isRequired" @input-change="handleInputChange" :validator="item.isRequired == 1 && isValid(item.name)" :isDisabled="props.payload.nodeStatus === 1"/>
23
+ </div>
24
+ <div v-else class="variable-value-container">
25
+ {{ item.variableValue == '' || item.variableValue == null ? mapValue[item.name] : item.variableValue}}
26
+ </div>
27
27
  </div>
28
-
29
- <div class="button-container"v-if="!finishSubmit && props.payload.nodeStatus == 0">
30
- <div class="button" @click="handleSendForm">
31
- {{TUITranslateService.t("AIDesk.提交")}}
32
- </div>
33
-
28
+ <div class="button-container"v-if="!finishSubmit && props.payload.nodeStatus !== 2">
29
+ <div :class="props.payload.nodeStatus === 1 ? 'button-disable' : 'button'" @click="handleSendForm">
30
+ {{ TUITranslateService.t("AIDesk.提交") }}
31
+ </div>
34
32
  </div>
35
33
  </div>
36
-
34
+
37
35
  </template>
38
36
  <script lang="ts">
39
37
  import vue from '../../../../../../../adapter-vue';
@@ -50,17 +48,17 @@ interface Props {
50
48
  payload: customerServicePayloadType;
51
49
  }
52
50
  export default {
53
- components: {
54
- LabelPC,
55
- InputPC,
56
- RadioPC,
57
- Icon
58
- },
51
+ components: {
52
+ LabelPC,
53
+ InputPC,
54
+ RadioPC,
55
+ Icon
56
+ },
59
57
  props: {
60
- payload: {
61
- type: Object as () => customerServicePayloadType,
62
- default: () => ({}),
63
- },
58
+ payload: {
59
+ type: Object as () => customerServicePayloadType,
60
+ default: () => ({}),
61
+ },
64
62
  },
65
63
  emits: ['sendMessage'],
66
64
  setup(props: Props, { emit }) {
@@ -69,73 +67,71 @@ export default {
69
67
  const finishSubmit = ref<boolean>(false);
70
68
  const hasNullValue = ref<boolean>(true);
71
69
  onMounted(()=>{
72
- let inputVariables = props.payload.content.inputVariables ?? [];
73
- for(let i=0;i<inputVariables.length;i++){
74
- const name = inputVariables[i].name;
75
- const variableValue = inputVariables[i].variableValue;
76
- mapValue.value[name]=variableValue;
77
- }
70
+ let inputVariables = props.payload.content.inputVariables ?? [];
71
+ for (let i = 0; i < inputVariables.length; i++) {
72
+ const name = inputVariables[i].name;
73
+ const variableValue = inputVariables[i].variableValue;
74
+ mapValue.value[name] = variableValue;
75
+ }
78
76
  });
79
-
80
- const checkTip = () => {
81
- return props.payload.content.tip != '' && props.payload.content.tip != null;
82
- }
83
77
 
78
+ const checkTip = () => {
79
+ return props.payload.content.tip != '' && props.payload.content.tip != null;
80
+ }
84
81
 
85
82
  const checkValidator = (name:string) => {
86
- hasNullValue.value = false;
87
- if(isSubmit.value == true){
88
- if(mapValue.value[name] == null || mapValue.value[name] == ''){
89
- hasNullValue.value = true;
90
- return true;
91
-
92
- }
83
+ hasNullValue.value = false;
84
+ if (isSubmit.value == true) {
85
+ if (mapValue.value[name] == null || mapValue.value[name] == '') {
86
+ hasNullValue.value = true;
87
+ return true;
93
88
  }
94
- return false;
89
+ }
90
+ return false;
95
91
  }
96
92
 
97
-
98
-
99
93
  const handleSendForm = (data: any) => {
100
- isSubmit.value = true;
101
- let list = props.payload.content.inputVariables;
102
- for(let i=0;i<list.length;i++){
103
- let value = mapValue.value[list[i].name];
104
- if(value !='' && value != null){
105
- list[i].variableValue = value;
106
- }else {
107
- if(list[i].isRequired === 1 && checkValidator(list[i].name)){
108
- return;
109
- }
110
- }
94
+ if (props.payload.nodeStatus === 1) {
95
+ return;
96
+ }
97
+ isSubmit.value = true;
98
+ let list = props.payload.content.inputVariables;
99
+ for (let i = 0; i < list.length; i++) {
100
+ let value = mapValue.value[list[i].name];
101
+ if (value !='' && value != null) {
102
+ list[i].variableValue = value;
103
+ } else {
104
+ if (list[i].isRequired === 1 && checkValidator(list[i].name)) {
105
+ return;
106
+ }
111
107
  }
112
- const submitData = {
113
- data: JSON.stringify({
114
- src: CUSTOM_MESSAGE_SRC.MULTI_FORM,
115
- content: {
116
- inputVariables: list
117
- },
118
- customerServicePlugin: 0,
108
+ }
109
+ const submitData = {
110
+ data: JSON.stringify({
111
+ src: CUSTOM_MESSAGE_SRC.MULTI_FORM,
112
+ content: {
113
+ inputVariables: list
114
+ },
115
+ customerServicePlugin: 0,
119
116
  }),
120
- };
121
-
122
- emit('sendMessage', submitData);
123
- finishSubmit.value = true;
124
-
125
- isSubmit.value = false;
117
+ };
118
+ emit('sendMessage', submitData);
119
+ finishSubmit.value = true;
120
+ isSubmit.value = false;
126
121
  };
122
+
127
123
  const handleInputChange = ({name,value}) =>{
128
- mapValue.value[name] = value;
124
+ mapValue.value[name] = value;
129
125
  }
130
126
 
131
127
  const showValue = (name:string,variableValue:string) => {
132
- if(variableValue != null && variableValue != ''){
133
- return variableValue
134
- }
135
- return mapValue.value[name];
128
+ if (variableValue != null && variableValue != '') {
129
+ return variableValue
130
+ }
131
+ return mapValue.value[name];
136
132
  }
137
133
  const isValid = (name:string) => {
138
- return isSubmit.value && (mapValue.value[name] == null || mapValue.value[name] == '' || mapValue.value[name] == undefined)
134
+ return isSubmit.value && (mapValue.value[name] == null || mapValue.value[name] == '' || mapValue.value[name] == undefined);
139
135
  }
140
136
  return {
141
137
  props,
@@ -147,7 +143,7 @@ export default {
147
143
  showValue,
148
144
  mapValue,
149
145
  isSubmit,
150
- checkTip,
146
+ checkTip,
151
147
  isValid,
152
148
  TUITranslateService
153
149
  };
@@ -155,32 +151,45 @@ export default {
155
151
  };
156
152
  </script>
157
153
  <style lang="scss">
158
- .title-container{
159
- display: flex;
160
- justify-content: space-between;
161
- align-items: center;
162
- margin-bottom: 12px;
163
- min-width: 300px;
154
+ .form-pc-container {
155
+ font-family: PingFangSC-Regular;
156
+ max-width: 350px;
157
+ }
158
+ .title-container {
159
+ display: flex;
160
+ justify-content: space-between;
161
+ align-items: center;
162
+ margin-bottom: 12px;
164
163
  }
165
164
  .form-finish-title-right {
166
- display:flex;
167
- }
168
- .button-container{
169
- display:flex;
165
+ display: flex;
166
+ }
167
+ .button-container {
168
+ display: flex;
169
+ justify-content: center;
170
+ margin-top: 15px;
171
+ .button {
172
+ display: flex;
170
173
  justify-content: center;
171
- margin-top:15px;
172
- .button {
173
- display:flex;
174
- justify-content: center;
175
- width: 87px;
176
- padding:6px 10px;
177
- background-color: #1c66e5;
178
- color:white;
179
- border-radius:25px ;
180
- }
174
+ width: 87px;
175
+ padding: 6px 10px;
176
+ background-color: #1c66e5;
177
+ color: white;
178
+ border-radius: 20px;
179
+ cursor: pointer;
180
+ }
181
+ .button-disable {
182
+ background-color: #dbdbdb;
183
+ display: flex;
184
+ justify-content: center;
185
+ width: 87px;
186
+ padding: 6px 10px;
187
+ color: white;
188
+ border-radius: 20px;
189
+ }
181
190
  }
182
- .variable-value-container{
183
- padding-bottom: 8px;
184
- margin-top:3px;
191
+ .variable-value-container {
192
+ padding-bottom: 8px;
193
+ margin-top: 3px;
185
194
  }
186
- </style>
195
+ </style>
@@ -1,10 +1,10 @@
1
1
  <template>
2
- <div v-if="isPC" class="message-input">
3
- <FormPC :payload="payloads" @sendMessage="handleSendForm" />
4
- </div>
5
- <div v-else>
6
- <FormMobile :payload="payloads" @sendMessage="handleSendForm" @showFormPopup="handleShowFormPopup"/>
7
- </div>
2
+ <div v-if="isPC" class="message-input">
3
+ <FormPC :payload="payloads" @sendMessage="handleSendForm" />
4
+ </div>
5
+ <div v-else>
6
+ <FormMobile :payload="payloads" @sendMessage="handleSendForm" @showFormPopup="handleShowFormPopup"/>
7
+ </div>
8
8
  </template>
9
9
 
10
10
  <script lang="ts">
@@ -13,7 +13,7 @@ import FormPC from './form-pc.vue';
13
13
  import FormMobile from './form-mobile.vue';
14
14
  import { customerServicePayloadType } from '../../../../../../../interface';
15
15
  import { isPC } from '../../../../../../../utils/env';
16
- const { computed} = vue;
16
+ const { computed } = vue;
17
17
 
18
18
  interface branchItem {
19
19
  content: string;
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <div
3
+ class="message-order"
4
+ >
5
+ <div class="order-guide">
6
+ {{ props.payload.content.guide }}
7
+ </div>
8
+ <div class="order-main">
9
+ <img
10
+ v-if="props.payload.content.pic"
11
+ class="order-img"
12
+ :src="props.payload.content.pic"
13
+ >
14
+ <div class="order-information">
15
+ <div class="order-name">
16
+ {{ props.payload.content.name }}
17
+ </div>
18
+ <div class="order-description">
19
+ {{ props.payload.content.desc }}
20
+ </div>
21
+ </div>
22
+ </div>
23
+ <div class="order-custom" v-for="item in props.payload.content.customField">
24
+ <div class="order-field">
25
+ <span class="field-name"> {{ item.name }} </span>
26
+ <span class="field-value"> {{ item.value }} </span>
27
+ <span class="field-customer-value"> {{ item.customerValue }} </span>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script lang="ts">
34
+ import { customerServicePayloadType } from '../../../../../../interface';
35
+
36
+ interface Props {
37
+ payload: customerServicePayloadType;
38
+ }
39
+
40
+ export default {
41
+ props: {
42
+ payload: {
43
+ type: Object as () => customerServicePayloadType,
44
+ default: () => ({}),
45
+ },
46
+ },
47
+ setup(props: Props) {
48
+ return {
49
+ props,
50
+ };
51
+ },
52
+ };
53
+ </script>
54
+ <style lang="scss" scoped>
55
+ .message-order {
56
+ min-width: 200px;
57
+ max-width: 400px;
58
+ color: #000;
59
+ font-family: PingFangSC-Regular;
60
+
61
+ .order-guide {
62
+ font-size: 14px;
63
+ margin-bottom: 10px;
64
+ overflow: hidden;
65
+ text-overflow: ellipsis;
66
+ white-space: nowrap;
67
+ }
68
+
69
+ .order-main {
70
+ display:flex;
71
+
72
+ .order-img {
73
+ width: 65px;
74
+ height: 65px;
75
+ border-radius: 10px;
76
+ flex-shrink: 0;
77
+ object-fit: cover;
78
+ margin-right: 15px;
79
+ }
80
+
81
+ .order-information {
82
+ width:100%;
83
+ margin-right:5px;
84
+ display: flex;
85
+ flex-direction: column;
86
+ justify-content: space-between;
87
+
88
+ .order-name {
89
+ max-width: 200px;
90
+ min-width: 100px;
91
+ color: #000000;
92
+ font-size: 14px;
93
+ display: -webkit-box;
94
+ overflow: hidden;
95
+ text-overflow: ellipsis;
96
+ -webkit-line-clamp: 1;
97
+ -webkit-box-orient: vertical;
98
+ word-break: break-all;
99
+ }
100
+
101
+ .order-description {
102
+ font-size: 14px;
103
+ max-width: 200px;
104
+ min-width: 100px;
105
+ color: rgba(0, 0, 0, 0.55);
106
+ overflow: hidden;
107
+ text-overflow: ellipsis;
108
+ font-weight: 600;
109
+ }
110
+ }
111
+ }
112
+
113
+ .order-field {
114
+ font-size: 12px;
115
+ font-weight: 500;
116
+ margin-top: 5px;
117
+ display: flex;
118
+ gap: 8px;
119
+
120
+ .field-name {
121
+ color: rgba(0, 0, 0, 0.55);
122
+ flex: 0 0 auto;
123
+ width: 70px;
124
+ white-space: nowrap;
125
+ overflow: hidden;
126
+ text-overflow: ellipsis;
127
+ }
128
+
129
+ .field-value {
130
+ color: #333;
131
+ font-weight: 500;
132
+ white-space: nowrap;
133
+ overflow: hidden;
134
+ text-overflow: ellipsis;
135
+ flex: 1;
136
+ min-width: 0;
137
+ padding-left: 4px;
138
+ }
139
+ }
140
+ }
141
+ </style>
@@ -35,7 +35,7 @@ export default {
35
35
  default: () => ({}),
36
36
  },
37
37
  },
38
- setup(props: Props) {
38
+ setup(props: Props, { emit }) {
39
39
  const image = ref(false);
40
40
  const imageSrc = ref('');
41
41
  const imageList = [];
@@ -45,6 +45,10 @@ export default {
45
45
  image.value = !image.value;
46
46
  imageSrc.value = decodeURIComponent(href);
47
47
  }
48
+ // @ts-ignore
49
+ window.onMarkdownImageLoad = function() {
50
+ emit('heightChanged');
51
+ }
48
52
  return parseMarkdown(props.payload.content);
49
53
  });
50
54
 
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <div class="message-stream">
3
+ <span v-if="isCursorBlinking" class="blinking-cursor">|</span>
3
4
  <pre ref="preRef" :class="['message-marked']" v-html="displayedContent" />
4
5
  </div>
5
6
  <div v-if="image" class="rich-image-previewer" @click="closeImage">
@@ -27,6 +28,7 @@ const props = withDefaults(defineProps<Props>(), {
27
28
  payload: () => '',
28
29
  });
29
30
 
31
+ const isCursorBlinking = ref<boolean>(true);
30
32
  const isStreaming = ref<boolean>(false);
31
33
  const image = ref(false);
32
34
  const imageSrc = ref('');
@@ -40,6 +42,11 @@ const displayedContent = computed(() => {
40
42
  image.value = !image.value;
41
43
  imageSrc.value = decodeURIComponent(href);
42
44
  }
45
+ // @ts-ignore
46
+ window.onMarkdownImageLoad = function() {
47
+ // empty implementation
48
+ // 已经用 ResizeObserver 观测高度,这里不用重复通知高度变化
49
+ }
43
50
  return parseMarkdown(streamContent.value);
44
51
  });
45
52
  const preRef = ref();
@@ -78,11 +85,18 @@ watch(() => props.payload, (newValue: string, oldValue: string) => {
78
85
  const _payloadObject = JSONToObject(props.payload);
79
86
  chunks.value = Array.isArray(_payloadObject.chunks) ? _payloadObject.chunks.join('') : _payloadObject.chunks;
80
87
  isFinished.value = _payloadObject.isFinished === 1;
88
+
89
+ // hide blinking cursor
90
+ if (chunks.value.length > 0) {
91
+ isCursorBlinking.value = false;
92
+ }
93
+
81
94
  if (newValue && !oldValue && isFinished.value) {
82
95
  // disable typeWriter style or history message first load
83
96
  streamContent.value = chunks.value;
97
+ prevChunksLength.value = chunks.value.length;
84
98
  } else {
85
- // 判断长度是为了防御编辑的内容回退的异常 case
99
+ // 判断长度是为了防御编辑的内容回退和内容重复的异常 case
86
100
  if (chunks.value.length > prevChunksLength.value) {
87
101
  const _newChunksToAdd = chunks.value?.slice(prevChunksLength.value);
88
102
  prevChunksLength.value = chunks.value.length;
@@ -136,4 +150,14 @@ const closeImage = () => {
136
150
  white-space: normal;
137
151
  font-size: 14px;
138
152
  }
153
+
154
+ .blinking-cursor {
155
+ animation: blink 0.8s step-end infinite;
156
+ color: #000;
157
+ }
158
+
159
+ @keyframes blink {
160
+ from, to { opacity: 1; }
161
+ 50% { opacity: 0; }
162
+ }
139
163
  </style>
@@ -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;
@@ -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,
@@ -91,11 +91,13 @@ function onMessageListUpdated(newMessageList: IMessageModel[]) {
91
91
  messageList.value = newMessageList || [];
92
92
  const lastMessage = messageList.value?.[messageList.value?.length - 1];
93
93
  isExistLastMessage.value = !!(
94
- lastMessage && lastMessage?.time < currentLastMessageTime?.value
94
+ // 过滤在线消息
95
+ // @ts-ignore
96
+ lastMessage && !lastMessage?._message._onlineOnlyFlag && lastMessage?.time < currentLastMessageTime?.value
95
97
  );
96
98
  }
97
99
 
98
- function onNewMessageListUpdated(newMessageList: IMessageModel[]) {
100
+ function onNewMessageList(newMessageList: IMessageModel[]) {
99
101
  if (Array.isArray(newMessageList) && isScrollButtonVisible.value) {
100
102
  newMessageList.forEach((message: IMessageModel) => {
101
103
  if (
@@ -104,6 +106,9 @@ function onNewMessageListUpdated(newMessageList: IMessageModel[]) {
104
106
  && !message.isDeleted
105
107
  && !message.isRevoked
106
108
  && !isTypingMessage(message)
109
+ // 过滤在线消息
110
+ // @ts-ignore
111
+ && !message._message._onlineOnlyFlag
107
112
  ) {
108
113
  newMessageCount.value += 1;
109
114
  }
@@ -167,9 +172,17 @@ function scrollToMessageListBottom() {
167
172
  emits('scrollToLatestMessage');
168
173
  }
169
174
 
175
+ function hideScrollButton() {
176
+ if (isScrollButtonVisible.value) {
177
+ isScrollButtonVisible.value = false;
178
+ resetNewMessageCount();
179
+ }
180
+ }
181
+
170
182
  defineExpose({
171
183
  judgeScrollOverOneScreen,
172
184
  isScrollButtonVisible,
185
+ hideScrollButton,
173
186
  });
174
187
  </script>
175
188
 
@@ -179,8 +192,6 @@ defineExpose({
179
192
  bottom: 10px;
180
193
  right: 10px;
181
194
  width: auto;
182
- min-width: 92px;
183
- max-width: 115px;
184
195
  height: 28px;
185
196
  background: #fff;
186
197
  border: 1px solid #e0e0e0;
@@ -198,6 +209,7 @@ defineExpose({
198
209
  font-size: 10px;
199
210
  color: #147aff;
200
211
  margin-left: 3px;
212
+ margin-right: 3px;
201
213
  }
202
214
  }
203
215
  </style>
@@ -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;