cnhis-design-vue 2.1.24 → 2.1.25

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 (126) hide show
  1. package/CHANGELOG.md +2287 -2287
  2. package/es/affix/index.js +8 -8
  3. package/es/age/index.js +10 -10
  4. package/es/alert/index.js +8 -8
  5. package/es/anchor/index.js +8 -8
  6. package/es/auto-complete/index.js +8 -8
  7. package/es/avatar/index.js +8 -8
  8. package/es/back-top/index.js +8 -8
  9. package/es/badge/index.js +8 -8
  10. package/es/base/index.js +8 -8
  11. package/es/big-table/index.js +164 -164
  12. package/es/breadcrumb/index.js +8 -8
  13. package/es/button/index.js +31 -31
  14. package/es/calendar/index.js +8 -8
  15. package/es/captcha/index.js +3 -3
  16. package/es/card/index.js +8 -8
  17. package/es/carousel/index.js +8 -8
  18. package/es/cascader/index.js +8 -8
  19. package/es/checkbox/index.js +9 -9
  20. package/es/col/index.js +8 -8
  21. package/es/collapse/index.js +8 -8
  22. package/es/color-picker/index.js +1 -1
  23. package/es/comment/index.js +8 -8
  24. package/es/config-provider/index.js +8 -8
  25. package/es/date-picker/index.js +8 -8
  26. package/es/descriptions/index.js +8 -8
  27. package/es/divider/index.js +8 -8
  28. package/es/drag-layout/index.js +3 -3
  29. package/es/drawer/index.js +8 -8
  30. package/es/dropdown/index.js +8 -8
  31. package/es/editor/index.js +30 -25
  32. package/es/editor/style.css +1 -1
  33. package/es/empty/index.js +8 -8
  34. package/es/fabric-chart/index.js +65 -49
  35. package/es/form/index.js +8 -8
  36. package/es/form-model/index.js +8 -8
  37. package/es/form-table/index.js +93 -87
  38. package/es/form-table/style.css +1 -1
  39. package/es/index/index.js +803 -764
  40. package/es/index/style.css +1 -1
  41. package/es/input/index.js +9 -9
  42. package/es/input-number/index.js +8 -8
  43. package/es/layout/index.js +8 -8
  44. package/es/list/index.js +8 -8
  45. package/es/locale-provider/index.js +8 -8
  46. package/es/map/index.js +9 -9
  47. package/es/mentions/index.js +8 -8
  48. package/es/menu/index.js +8 -8
  49. package/es/message/index.js +8 -8
  50. package/es/multi-chat/index.js +92 -92
  51. package/es/multi-chat-client/index.js +86 -86
  52. package/es/multi-chat-history/index.js +4 -4
  53. package/es/multi-chat-record/index.js +14 -14
  54. package/es/multi-chat-setting/index.js +27 -27
  55. package/es/multi-chat-sip/index.js +1 -1
  56. package/es/notification/index.js +8 -8
  57. package/es/page-header/index.js +8 -8
  58. package/es/pagination/index.js +8 -8
  59. package/es/popconfirm/index.js +8 -8
  60. package/es/popover/index.js +8 -8
  61. package/es/progress/index.js +8 -8
  62. package/es/radio/index.js +9 -9
  63. package/es/rate/index.js +8 -8
  64. package/es/result/index.js +8 -8
  65. package/es/row/index.js +8 -8
  66. package/es/scale-view/index.js +33 -33
  67. package/es/select/index.js +11 -11
  68. package/es/select-label/index.js +11 -11
  69. package/es/select-person/index.js +20 -20
  70. package/es/skeleton/index.js +8 -8
  71. package/es/slider/index.js +8 -8
  72. package/es/space/index.js +8 -8
  73. package/es/spin/index.js +8 -8
  74. package/es/statistic/index.js +8 -8
  75. package/es/steps/index.js +8 -8
  76. package/es/switch/index.js +8 -8
  77. package/es/table-filter/index.js +212 -200
  78. package/es/table-filter/style.css +1 -1
  79. package/es/tabs/index.js +8 -8
  80. package/es/tag/index.js +9 -9
  81. package/es/time-picker/index.js +8 -8
  82. package/es/timeline/index.js +8 -8
  83. package/es/tooltip/index.js +8 -8
  84. package/es/transfer/index.js +8 -8
  85. package/es/tree/index.js +8 -8
  86. package/es/tree-select/index.js +8 -8
  87. package/es/upload/index.js +8 -8
  88. package/es/verification-code/index.js +2 -2
  89. package/lib/cui.common.js +806 -767
  90. package/lib/cui.umd.js +806 -767
  91. package/lib/cui.umd.min.js +21 -21
  92. package/package.json +107 -107
  93. package/packages/big-table/src/BigTable.vue +3044 -3044
  94. package/packages/big-table/src/assets/style/table-base.less +370 -370
  95. package/packages/big-table/src/components/AutoLayoutButton.vue +270 -270
  96. package/packages/big-table/src/utils/batchEditing.js +610 -610
  97. package/packages/big-table/src/utils/bigTableProps.js +95 -95
  98. package/packages/button/src/ButtonPrint/components/IdentityVerification.vue +181 -181
  99. package/packages/button/src/ButtonPrint/index.vue +739 -739
  100. package/packages/editor/src/Editor.vue +13 -4
  101. package/packages/fabric-chart/src/components/TimeScaleValue.vue +113 -113
  102. package/packages/fabric-chart/src/const/defaultVaule.js +59 -59
  103. package/packages/fabric-chart/src/fabric-chart/FabricPolylines.vue +1079 -1066
  104. package/packages/fabric-chart/src/fabric-chart/FabricScaleValue.vue +135 -135
  105. package/packages/fabric-chart/src/fabric-chart/FabricTextGroup.vue +558 -558
  106. package/packages/fabric-chart/src/fabric-chart2/FabricTop.vue +172 -172
  107. package/packages/form-table/src/FormTable.vue +5 -1
  108. package/packages/multi-chat/chat/chatFooter.vue +1594 -1594
  109. package/packages/multi-chat/chat/chatMain.vue +1466 -1466
  110. package/packages/multi-chat/chat/quickReply.vue +439 -439
  111. package/packages/multi-chat/chat/scrollList.vue +1232 -1232
  112. package/packages/multi-chat/setting/baseInfo/index.vue +1316 -1316
  113. package/packages/multi-chat/store/actions.js +448 -448
  114. package/packages/multi-chat/store/state.js +112 -112
  115. package/packages/scale-view/formitem/r-choice.vue +714 -714
  116. package/packages/scale-view/scaleView.vue +2010 -2010
  117. package/packages/select-person/select-person.vue +1680 -1680
  118. package/packages/table-filter/src/base-search-com/BaseSearch.vue +2468 -2468
  119. package/packages/table-filter/src/components/c-tree-select/tree-select.vue +336 -336
  120. package/packages/table-filter/src/components/multi-select/multi-select.vue +219 -219
  121. package/packages/table-filter/src/components/out-quick-search/out-quick-search.vue +340 -340
  122. package/packages/table-filter/src/components/search-condition/SearchCondition.vue +1825 -1825
  123. package/packages/table-filter/src/const/dataOptions.js +43 -43
  124. package/packages/table-filter/src/mixins/mixins.js +692 -695
  125. package/packages/table-filter/src/quick-search/QuickSearch.vue +2116 -2109
  126. package/src/directive/preventReClick.js +12 -12
@@ -1,1232 +1,1232 @@
1
- <script>
2
- import Grid from '~/grid';
3
- import infiniteScroll from 'vue-infinite-scroll';
4
- import { mapGetters, mapMutations, mapActions } from '../store/helper';
5
- import { getRecallText } from '../utils';
6
- import Avatar from '../components/avatar';
7
- import vexutils, { moment } from '@/utils/vexutils';
8
- import fetch, { qs } from '@/utils/chatFetch';
9
- import { Popover, Spin, Tooltip } from 'ant-design-vue';
10
- import SvgIcon from '@/component/svg/index.vue';
11
-
12
- export default {
13
- inject: ['store', 'i18nText', 'dispatchEvent'],
14
- components: {
15
- [Tooltip.name]: Tooltip,
16
- [Spin.name]: Spin,
17
- [Popover.name]: Popover,
18
- [Grid.name]: Grid,
19
- SvgIcon,
20
- Avatar
21
- },
22
- data() {
23
- return {
24
- scrollList: [],
25
- defaultAvatar: require('../img/default.png'),
26
- busy: false,
27
- pageSize: 20,
28
- pageNum: 1,
29
- infiniteMsgShow: true
30
- };
31
- },
32
- computed: {
33
- ...mapGetters(['userInfo']),
34
- ...mapGetters([
35
- 'assemblyId',
36
- 'classify',
37
- 'isScrollListChange',
38
- 'listChangeItem',
39
- 'currentTab',
40
- 'sessionId',
41
- 'keywords',
42
- 'searchKeywords',
43
- 'enable',
44
- 'panelParams',
45
- 'assemblySetting',
46
- 'clientId',
47
- 'clientParams',
48
- 'serviceId',
49
- 'msgList',
50
- 'queueItem'
51
- ]),
52
- foldTheme() {
53
- return this.theme === 'fold';
54
- },
55
- tableTheme() {
56
- return this.theme === 'table';
57
- },
58
- classifyIcon() {
59
- const { classify, assemblySetting } = this;
60
- if (!classify) return '';
61
- const isListClassify = assemblySetting?.isListClassify === 'Y';
62
- const target = isListClassify ? assemblySetting?.listClassify?.find(item => item.classify === classify) : null;
63
- return target?.icon;
64
- },
65
- isListClassify() {
66
- return this.assemblySetting.isListClassify === 'Y';
67
- },
68
- tabChange() {
69
- let { searchKeywords, classify } = this;
70
- return {
71
- searchKeywords,
72
- classify
73
- };
74
- },
75
- tableColumns() {
76
- const tableLayout = this.assemblySetting?.listLayoutSetting?.tableLayout;
77
- if (tableLayout && tableLayout.length) {
78
- const htmlField = ['${lastContent}'];
79
- return tableLayout.map((item, i) => {
80
- let { title = '', name = '' } = item;
81
- title = title || name;
82
- const w = Math.max((title?.length || 1) * 14 + 22, 60);
83
- const key = name || i;
84
- const data = {
85
- ...item,
86
- title,
87
- // width: w,
88
- field: `_${key}_`,
89
- showHeaderOverflow: true,
90
- showOverflow: true
91
- };
92
- if (htmlField.includes(item.value) || item.mapping?.length > 0) {
93
- data.type = 'html';
94
- }
95
- return data;
96
- });
97
- }
98
- return [];
99
- },
100
- // 根据后台配置信息进行排序
101
- processedScrollList() {
102
- let processedScrollList = [];
103
- let settingList = [];
104
- if (this.tableTheme) {
105
- settingList = this.tableColumns.map(item => [item.field, item]);
106
- } else {
107
- let setting = this.assemblySetting.listLayoutSetting || {};
108
- if (setting.timeSetting) {
109
- setting = Object.assign({}, setting, {
110
- timeSettingTip: setting.timeSetting // 时间类型 tooltip 展示的值和原值不同 额外处理
111
- });
112
- }
113
- settingList = Object.entries(setting);
114
- }
115
- let reg = /\$\{(.+)\}/;
116
- const { currentTab, keywords, classify, isListClassify } = this;
117
- processedScrollList = this.scrollList.reduce((list, item) => {
118
- if (item.kind == 2 || !this.showScrollItem(item, currentTab, keywords, classify, isListClassify)) return list; // <- 通话记录跳过
119
- const layoutData = {};
120
- for (let [key, value] of settingList) {
121
- const isTip = /.+Tip$/.test(key);
122
- let parseValue = (value.value || '').replace(/\$\{([^}]+)\}/g, (_v, $1) => {
123
- let key = $1;
124
- let param;
125
- if (key.startsWith('form.')) {
126
- key = key.split('.')[1];
127
- let params = JSON.parse(item.params || '{}');
128
- param = params[key] || this.getMsgFieldValue(key, item, isTip);
129
- } else {
130
- param = this.getMsgFieldValue(key, item, isTip);
131
- }
132
- if (value.mapping) {
133
- // param = this.getMappingHTML(value.mapping[param]);
134
- param = this.getMappingHTML(value.mapping.find( v => v.value == param) || {});
135
- }
136
- return param || '';
137
- });
138
- layoutData[key] = parseValue;
139
- }
140
- list.push({ ...item, ...layoutData });
141
- return list;
142
- }, []);
143
-
144
- // 排队列表排序(1.等级2.创建时间)
145
- const sortTabs = ['queue', 'session'];
146
- const sortKeys = ['queueSortSetting', 'sessionSortSetting'];
147
- const sortIndex = sortTabs.indexOf(currentTab);
148
- if (sortIndex >= 0) {
149
- // 获取对应sortsetting, listSortSetting兼容旧数据
150
- const sortSetting = this.assemblySetting[sortKeys[sortIndex]] || this.assemblySetting.listSortSetting;
151
- if (sortSetting) {
152
- let { isParams, descOrAsc, orderBy } = sortSetting;
153
- orderBy = orderBy ? orderBy.replace(reg, '$1') : orderBy; // 去除 ${}
154
- let hasLevel = [],
155
- noLevel = [];
156
- processedScrollList.forEach(item => {
157
- let value;
158
- if (isParams) {
159
- value = JSON.parse(item.params || '{}')[orderBy];
160
- } else {
161
- value = item[orderBy];
162
- }
163
- vexutils.isUndefined(value) ? noLevel.push(item) : hasLevel.push(item);
164
- });
165
- hasLevel.sort((a, b) => {
166
- let aLevel = JSON.parse(a.params || '{}')[orderBy] || a[orderBy];
167
- let bLevel = JSON.parse(b.params || '{}')[orderBy] || b[orderBy];
168
- if (aLevel === bLevel) {
169
- return Number(a.createdTime) - Number(b.createdTime);
170
- }
171
- // return descOrAsc === 'desc' ? aLevel - bLevel : bLevel - aLevel;
172
- return descOrAsc === 'desc' ? bLevel - aLevel : aLevel - bLevel;
173
- });
174
- noLevel.sort((a, b) => Number(a.createdTime) - Number(b.createdTime));
175
- processedScrollList = [...hasLevel, ...noLevel];
176
- } else {
177
- processedScrollList.sort((a, b) => Number(a.createdTime) - Number(b.createdTime));
178
- }
179
- }
180
- return processedScrollList;
181
- }
182
- },
183
- props: {
184
- // 日期过滤条件
185
- dateParams: {
186
- type: Array,
187
- default: () => []
188
- },
189
- theme: {
190
- type: String,
191
- default: ''
192
- },
193
- // 默认会话id
194
- defSessionId: {
195
- type: [String, Number]
196
- },
197
- type: {
198
- type: String
199
- }
200
- },
201
- methods: {
202
- ...mapMutations([
203
- 'setSessionId',
204
- 'setIsScrollListChange',
205
- 'setListChangeItem',
206
- 'setCurrentTab',
207
- 'setClientId',
208
- 'setOnChating',
209
- 'setClientParams',
210
- 'setQueueItem',
211
- 'setScrollTo',
212
- 'setSessionHistoryList',
213
- 'setSessionEnd',
214
- 'setQueueStatistic',
215
- 'setSessionStatistic',
216
- 'offlineSessionStatistic',
217
- 'appendList',
218
- 'setSessionType',
219
- 'setOfflineClassifyCount',
220
- 'setScrollQueueId',
221
- 'setLastClassify',
222
- 'setLastCurrentTab',
223
- 'setFooterMessage',
224
- 'setCurScrollItem',
225
- 'setDisableOperate',
226
- 'setClassify',
227
- 'setOfflineSessionStatistic',
228
- 'setClosedSession'
229
- ]),
230
- ...mapMutations({
231
- clearMsgList: 'setMsgList'
232
- }),
233
- ...mapActions(['setMsgList', 'setChatTimer']),
234
- isActive(item) {
235
- let res = this.sessionId === item.id || (item.username && item.username === this.clientId);
236
- return res;
237
- },
238
- formatBrief(item) {
239
- let { lastContentRecallFlag } = item;
240
- if (lastContentRecallFlag == 1) {
241
- const content = JSON.parse(item.lastContent);
242
- const flag = this.serviceId == item.fromId;
243
- return getRecallText(this, { content }, flag);
244
- }
245
- let lastItem;
246
- try {
247
- lastItem = JSON.parse(item.lastContent || '{}');
248
- } catch {
249
- console.log(item.lastContent.value);
250
- }
251
- if (!lastItem) return '';
252
- let { type, content } = lastItem;
253
- if (type === 1) {
254
- return `[${this.i18nText('1.9.181')}]`;
255
- } else if (type === 0) {
256
- return content;
257
- } else if (type === 2) {
258
- // return `[${this.i18nText("1.9.388")}]`;
259
- return '[视频通话]';
260
- } else if (type === 5) {
261
- return '[语音通话]';
262
- } else if (type === 6) {
263
- return '[语音消息]';
264
- } else if (type === 3) {
265
- return JSON.parse(content)?.type == 2 ? `[${this.i18nText('1.9.389')}]` : `[${this.i18nText('1.9.390')}]`;
266
- } else if (type === 4) {
267
- let title = content.title ? content.title : JSON.parse(content).title;
268
- return `[${title}]`;
269
- } else {
270
- return '';
271
- }
272
- },
273
- showScrollItem(item, currentTab, keywords, classify, isListClassify) {
274
- if (currentTab == 'end') return true;
275
- return (!keywords || (item.name || item.toName || item.title).includes(keywords)) && (item.classify === classify || !isListClassify);
276
- },
277
- handleInfiniteOnLoad() {
278
- if (this.busy) return;
279
- if (this.currentTab !== 'end') {
280
- this.busy = true;
281
- this.infiniteMsgShow = false;
282
- return;
283
- }
284
- this.busy = true;
285
- this.infiniteMsgShow = true;
286
- // 发送请求有时间间隔第一个滚动时间结束后才发送第二个请求
287
- this.pageNum++; // 滚动之后加载第二页
288
- this.getScrollList(true);
289
- },
290
- async getScrollList(flag) {
291
- switch (this.currentTab) {
292
- case 'queue':
293
- this.getQueueUsers();
294
- break;
295
- case 'session':
296
- await this.getSessionList();
297
- this.handleSelectItem();
298
- break;
299
- case 'end':
300
- // this.getSessionList(flag);
301
- this.getData(1, flag);
302
- }
303
- },
304
- /* 获取会话列表 */
305
- getSessionList() {
306
- let params = {
307
- ...this.getDateParams(),
308
- assemblyId: this.assemblyId,
309
- onlineType: this.currentTab === 'session' ? 1 : 0
310
- };
311
- if (this.isListClassify) {
312
- this.assemblySetting.listClassify.forEach(item => {
313
- this.$set(item, 'count', '');
314
- });
315
- }
316
- return fetch
317
- .get('/chat/service/getMySessionList', {
318
- params
319
- })
320
- .then(({ data }) => {
321
- if (data.result === 'SUCCESS') {
322
- this.scrollList = data.map.list;
323
- if (this.isListClassify) {
324
- this.assemblySetting.listClassify.forEach(item => {
325
- let count = data.map.list.filter(i => i.classify === item.classify).length;
326
- this.$set(item, 'count', count);
327
- });
328
- }
329
- if (data.map.listStatistic) {
330
- if (this.currentTab === 'end') {
331
- this.setOfflineSessionStatistic(data.map.listStatistic);
332
- } else {
333
- this.setSessionStatistic(data.map.listStatistic);
334
- }
335
- }
336
- } else {
337
- this.$message.warning(data.resultMsg);
338
- }
339
- });
340
- },
341
- async getData(type, flag) {
342
- try {
343
- let res = await this.handleDataRequest(type);
344
- let { data = {} } = res;
345
- if (data.result === 'SUCCESS') {
346
- let { list = [] } = data.map;
347
- if (flag) {
348
- if (!list || !list.length) {
349
- this.busy = true;
350
- this.infiniteMsgShow = false;
351
- } else {
352
- this.busy = false;
353
- this.scrollList = this.scrollList.concat(list);
354
- }
355
- return;
356
- } else {
357
- this.infiniteMsgShow = false;
358
- this.busy = false;
359
- this.scrollList = list;
360
- }
361
- if (this.isListClassify) {
362
- this.assemblySetting.listClassify.forEach(item => {
363
- let count = list.filter(i => i.classify === item.classify).length;
364
- this.$set(item, 'count', count);
365
- });
366
- }
367
- let { classifyStatistic = {} } = data.map || {};
368
- if (Object.keys(classifyStatistic).length) {
369
- this.setOfflineClassifyCount(classifyStatistic);
370
- }
371
- } else {
372
- this.infiniteMsgShow = false;
373
- this.$message.warning(data.resultMsg);
374
- }
375
- this.handleSelectItem();
376
- } catch (err) {
377
- this.infiniteMsgShow = false;
378
- console.log(err);
379
- }
380
- },
381
- getDateParams() {
382
- let params = {};
383
- const dateParams = this.dateParams;
384
- if (dateParams?.length) {
385
- const fmt = 'YYYY-MM-DD';
386
- params.startDate = dateParams[0].format(fmt) + ' 00:00:00';
387
- params.endDate = dateParams[1].format(fmt) + ' 23:59:59';
388
- }
389
- return params;
390
- },
391
- handleDataRequest(type) {
392
- let url = type == 1 ? '/chat/service/getMySessionList' : '/chat/service/getQueueUsers';
393
- let params = {
394
- ...this.getDateParams(),
395
- assemblyId: this.assemblyId,
396
- pageSize: this.pageSize,
397
- pageNum: this.pageNum
398
- };
399
- if (type == 1) {
400
- params.onlineType = this.currentTab === 'session' ? 1 : 0;
401
- }
402
- if (this.currentTab == 'end') {
403
- params.keyword = this.searchKeywords;
404
- params.classify = this.classify;
405
- }
406
- if (this.isListClassify) {
407
- this.assemblySetting.listClassify.forEach(item => {
408
- this.$set(item, 'count', '');
409
- });
410
- }
411
- return fetch.get(url, {
412
- params
413
- });
414
- },
415
- /* 获取排队列表 */
416
- getQueueUsers() {
417
- if (this.isListClassify) {
418
- this.assemblySetting.listClassify.forEach(item => {
419
- this.$set(item, 'count', '');
420
- });
421
- }
422
- // let params = { assemblyId: this.assemblyId };
423
- let params = {
424
- ...this.getDateParams(),
425
- assemblyId: this.assemblyId,
426
- };
427
- fetch
428
- .get('/chat/service/getQueueUsers', {
429
- params
430
- })
431
- .then(({ data }) => {
432
- if (data.result === 'SUCCESS') {
433
- this.scrollList = data.map.list;
434
- if (this.isListClassify) {
435
- this.assemblySetting.listClassify.forEach(item => {
436
- let count = data.map.list.filter(i => i.classify === item.classify).length;
437
- this.$set(item, 'count', count);
438
- });
439
- }
440
- if (data.map.listStatistic) {
441
- this.setQueueStatistic(data.map.listStatistic);
442
- }
443
- } else {
444
- this.$message.warning(data.resultMsg);
445
- }
446
- });
447
- },
448
- /* 点击列表项 */
449
- handleClick(item) {
450
- this.setLastClassify(this.classify);
451
- this.setLastCurrentTab(this.currentTab);
452
- this.setCurScrollItem(item);
453
- this.setClosedSession(this.type === 'end');
454
- switch (this.type) {
455
- case 'queue':
456
- this.accessChat(item);
457
- break;
458
- case 'session':
459
- this.getMsgList(item);
460
- break;
461
- case 'end':
462
- if (item.msgCount > 0) {
463
- this.cleanSessionCount(item);
464
- }
465
- this.getEndMsgList(item);
466
- }
467
- this.dispatchEvent('click_sessionListUser', JSON.parse(JSON.stringify(item)));
468
- },
469
- handleTableCellClick({ row }) {
470
- this.handleClick(row);
471
- },
472
- tableRowClass({ row }) {
473
- return this.isActive(row) ? 'active' : '';
474
- },
475
- handleTableScroll(args) {
476
- let target;
477
- if (this.currentTab == 'end' && !this.busy && (target = args.$event.target) && target.scrollHeight - target.clientHeight - args.scrollTop <= 10) {
478
- this.handleInfiniteOnLoad();
479
- }
480
- },
481
- cleanSessionCount(item) {
482
- let params = {
483
- sessionId: item.id,
484
- orgId: this.userInfo?.sysParams?.orgId,
485
- assemblyId: this.assemblyId
486
- };
487
- fetch.post('/chat/service/cleanSessionCount', qs.stringify(params)).then(({ data }) => {
488
- if (data.result === 'SUCCESS') {
489
- item.msgCount = 0;
490
- }
491
- });
492
- },
493
- /* 查看结束历史消息 */
494
- getEndMsgList(item) {
495
- let { id, toId } = item;
496
- this.setClientId(toId);
497
- this.setSessionId(id);
498
- this.clearMsgList();
499
- this.setMsgList({ validate: this.getMsgValidateFn(toId) });
500
- this.setOnChating(false);
501
- this.setChatTimer();
502
- let clientParams = JSON.parse(item.params || '{}');
503
- this.setClientParams(clientParams);
504
- // this.getSessionHistoryList();
505
- this.setQueueItem(null);
506
- },
507
- /* 客服点击会话列表进入会话, id:会话id, toId:客户id */
508
- getMsgList(item) {
509
- let { id, toId, params: cParams } = item;
510
- let lastSessionId = this.sessionId;
511
- this.setQueueItem(null);
512
- item.msgCount = 0;
513
- let params = {
514
- sessionId: id,
515
- lastSessionId,
516
- assemblyId: this.assemblyId
517
- };
518
- // 心跳
519
- fetch.post(
520
- '/chat/service/sessionHeart',
521
- qs.stringify({
522
- assemblyId: this.assemblyId,
523
- sessionId: id
524
- })
525
- );
526
- this.setDisableOperate(true);
527
- fetch
528
- .post('/chat/service/accessSession', qs.stringify(params))
529
- .then(({ data }) => {
530
- this.setScrollQueueId('');
531
- this.setSessionId(id);
532
- if (data.result === 'SUCCESS') {
533
- let { countdown, clientParams, sessionType, countdownDesc } = data.map;
534
- this.setClientId(toId);
535
- this.clearMsgList();
536
- this.setMsgList({ validate: this.getMsgValidateFn(toId) });
537
- this.setOnChating(true);
538
- this.setChatTimer({ countdown, countdownDesc });
539
- this.setSessionEnd(false);
540
- this.setClientParams(clientParams || JSON.parse(cParams || '{}'));
541
- this.getSessionHistoryList();
542
- this.setSessionType(sessionType);
543
- this.setFooterMessage('');
544
- } else {
545
- this.$message.warning(data.resultMsg);
546
- }
547
- })
548
- .finally(() => {
549
- this.setDisableOperate(false);
550
- });
551
- },
552
- getMsgValidateFn(id) {
553
- return () => id === this.clientId;
554
- },
555
- scrollToItem(sessionId) {
556
- if (!sessionId) return;
557
- this.$nextTick(() => {
558
- const target = this.$el.querySelector(`#item_id_${sessionId}`);
559
- if (!target) return;
560
- target.scrollIntoView(true);
561
- });
562
- },
563
- getSessionHistoryList() {
564
- if (this.assemblySetting?.type == 'staff') return;
565
- this.setSessionHistoryList([]);
566
- if (this.assemblySetting.recordScopeSetting === 'CURRENT') return;
567
- fetch
568
- .get('/chat/service/getPastSessionList', {
569
- params: { sessionId: this.sessionId }
570
- })
571
- .then(({ data }) => {
572
- if (data.result === 'SUCCESS') {
573
- let list = data.list.map(item => {
574
- if (item.fromId === this.serviceId) {
575
- item.onChating = true;
576
- }
577
- return item;
578
- });
579
- this.setSessionHistoryList(list);
580
- }
581
- });
582
- },
583
- /* 客服点击排队列表进入会话 */
584
- accessChat(item) {
585
- this.getQueueMsgList(item);
586
- },
587
- /* 排队消息 */
588
- getQueueMsgList(item) {
589
- let { bid: userId, userType, username, params: clientParams } = item;
590
- let params = {
591
- assemblyId: this.assemblyId,
592
- userId,
593
- userType,
594
- relateId: JSON.parse(clientParams || '{}').relateId
595
- };
596
- fetch.get('/chat/service/getQueueUser', { params }).then(({ data }) => {
597
- if (data.result === 'SUCCESS') {
598
- const obj = data.obj || {};
599
- this.currentTab === 'queue' && this.dispatchEvent('click_queueListUser', JSON.parse(JSON.stringify(obj.messages || [])));
600
- let messages = (obj.messages || []).map(item => {
601
- item.fromName = obj.name;
602
- item.content = JSON.parse(JSON.stringify(item));
603
- return item;
604
- });
605
- let robotMessages = this.handleRobotMessages(obj.robotMessages, obj.name);
606
- this.setScrollQueueId(obj.bid);
607
- this.setClientParams(JSON.parse(clientParams || '{}'));
608
- this.setClientId(username);
609
- this.setSessionId(null);
610
- this.setSessionHistoryList([]);
611
- this.clearMsgList([...robotMessages, ...messages]);
612
- this.setQueueItem(item);
613
- this.setChatTimer();
614
- this.$nextTick(() => {
615
- this.setScrollTo('bottom');
616
- });
617
- }
618
- });
619
- },
620
- handleRobotMessages(robotMessages, name) {
621
- if (!robotMessages || robotMessages.length === 0) return [];
622
- return robotMessages.map(item => {
623
- let content = item.content;
624
- if (item.content && vexutils.isJSON(item.content)) {
625
- content = JSON.parse(item.content);
626
- }
627
- item.content = content;
628
- item.fromName = name;
629
- return item;
630
- });
631
- },
632
- // queue: 排队列表, session: 会话列表, end: 已结束列表
633
- updateScrollList() {
634
- const v = this.listChangeItem;
635
- if (!v) return;
636
- // 存在队列id 更新排队列表
637
- if (v.queueId) {
638
- this.updateQueueList();
639
- } else if (v.sessionId) {
640
- // 存在会话id 更新排队列表
641
- this.updateSessionList();
642
- } else {
643
- this.updateEndList();
644
- }
645
- },
646
- /* 更新咨询结束列表 */
647
- updateEndList() {
648
- let { lastTime, lastContent, opt, sessionId, username, name } = this.listChangeItem;
649
- if (opt === 2) {
650
- this.scrollList.unshift({
651
- toId: username,
652
- lastTime,
653
- lastContent,
654
- toName: name,
655
- id: sessionId
656
- });
657
- }
658
- },
659
- /* 更新会话列表 */
660
- updateSessionList() {
661
- let { msgCount = undefined, lastContent, opt, from, to, sendTime, sessionId, lastTime, lastContentRecallFlag = undefined } = this.listChangeItem;
662
- lastContent = vexutils.isJSON(lastContent) ? JSON.parse(lastContent) : lastContent;
663
- // 0: 新增, 1: 更新, 2: 删除
664
- if (opt === 0) {
665
- this.scrollList.unshift(
666
- Object.assign(
667
- {},
668
- this.listChangeItem,
669
- {
670
- lastContent: JSON.stringify(lastContent)
671
- },
672
- { id: sessionId, lastTime: sendTime, fromId: from, toId: to }
673
- )
674
- );
675
- } else if (opt === 1) {
676
- let realIndex = this.scrollList.findIndex(item => item.id === sessionId);
677
- if (realIndex === -1) return;
678
- let res = {
679
- lastTime: sendTime || lastTime,
680
- sendTime,
681
- lastContentRecallFlag
682
- };
683
- if (lastContentRecallFlag != 1) {
684
- if (msgCount !== undefined) {
685
- res.msgCount = msgCount;
686
- }
687
- res.lastContent = JSON.stringify(lastContent);
688
- }
689
- let item = Object.assign({}, this.scrollList[realIndex], res);
690
- this.scrollList.splice(realIndex, 1, item);
691
- } else if (opt === 2) {
692
- let realIndex = this.scrollList.findIndex(item => item.id === sessionId);
693
- if (realIndex === -1) return;
694
- this.scrollList.splice(realIndex, 1);
695
- }
696
- },
697
- /* 更新排队列表 */
698
- updateQueueList() {
699
- let { sendTime, sendName, receiveName, opt, name, username, bid, msgCount, messages = [], createdTime, userType, classify, params, lastTime, lastContent } = this.listChangeItem;
700
- if (messages.length === 0) {
701
- messages = [{ createdTime }];
702
- }
703
- lastTime = lastTime || sendTime;
704
- lastContent = vexutils.isPlainObject(lastContent) ? JSON.stringify(lastContent) : lastContent;
705
- let current_item = {
706
- sendName,
707
- receiveName,
708
- msgCount,
709
- username,
710
- bid,
711
- name,
712
- messages,
713
- createdTime: new Date(createdTime).getTime(),
714
- userType,
715
- classify,
716
- params,
717
- lastTime: new Date(lastTime).getTime(),
718
- lastContent
719
- };
720
- // 0: 新增, 1: 更新, 2: 删除
721
- if (opt === 0) {
722
- this.scrollList.unshift(current_item);
723
- } else if (opt === 2) {
724
- let realIndex = this.scrollList.findIndex(item => item.username == username);
725
- this.scrollList.splice(realIndex, 1);
726
- } else if (opt === 1) {
727
- let realIndex = this.scrollList.findIndex(item => item.username == username);
728
- this.scrollList.splice(realIndex, 1, current_item);
729
- // 排队消息更新
730
- if (this.queueItem && username === this.queueItem.username) {
731
- this.clearMsgList([
732
- ...this.msgList,
733
- {
734
- fromName: name,
735
- content: Object.assign(JSON.parse(lastContent), { lastTime })
736
- }
737
- ]);
738
- this.appendList([
739
- {
740
- fromName: name,
741
- content: Object.assign(JSON.parse(lastContent), { lastTime })
742
- }
743
- ]);
744
- }
745
- }
746
- },
747
- getMappingHTML(value) {
748
- if (!vexutils.isObject(value)) return value;
749
- const { icon = '', desc, color } = value;
750
- let str = '';
751
- if (icon) {
752
- str += `<svg aria-hidden="true" style="font-size 20px;width: 1em;height: 1em;" class={svg-icon}><use xlinkhref={#icon-${icon}}></use></svg>`;
753
- }
754
- if (desc) {
755
- str += `<span style="color:#fff;padding:0 4px;${color ? 'background:' + color : ''}">${desc}</span>`;
756
- }
757
- return str;
758
- },
759
- // 获取聊天字段对应的值(部分字段需要格式化处理)
760
- getMsgFieldValue(key, item, isTip = false) {
761
- let val = item[key];
762
- switch (key) {
763
- case 'createdTime':
764
- case 'lastTime':
765
- val = this.formatDate(val || item.createdTime, isTip);
766
- break;
767
- case 'lastContent':
768
- val = this.formatBrief(item);
769
- break;
770
- }
771
- return val;
772
- },
773
- handleSelectItem() {
774
- let sessionId = this.defSessionId;
775
- if (!sessionId || this._selected) return;
776
- let matchItem = this.processedScrollList.find(item => item.id === sessionId);
777
- if (!matchItem) return;
778
- this._selected = 1;
779
- this.setClassify(matchItem.classify);
780
- this.handleClick(matchItem);
781
- this.$nextTick(() => {
782
- const target = this.$el.querySelector(`#item_id_${sessionId}`);
783
- if (!target) return;
784
- target.scrollIntoView(true);
785
- });
786
- },
787
- formatDate(date, showDetail) {
788
- let input;
789
- if (!isNaN(Number(date))) {
790
- input = moment(Number(date));
791
- } else {
792
- input = moment(new Date(date));
793
- }
794
- if (showDetail) {
795
- return input.format('YYYY年M月D日 HH:mm:ss');
796
- }
797
- // let input = moment(new Date(date));
798
- let now = moment();
799
- let today = moment(now.toArray().slice(0, 3));
800
- let yestoday = moment(today).subtract(1, 'days');
801
- let thisYear = moment(now.toArray().slice(0, 1));
802
- let inToday = today.isBefore(input);
803
- let inYestoday = input.isBetween(yestoday, today);
804
- let inThisYear = thisYear.isBefore(input);
805
- if (inToday) {
806
- return '今天' + input.format('HH:mm');
807
- } else if (inYestoday) {
808
- return '昨天' + input.format('HH:mm');
809
- } else if (inThisYear) {
810
- return input.format('M月D日');
811
- } else {
812
- return input.format('YYYY年M月D日');
813
- }
814
- // return vexutils.formatDate(date);
815
- }
816
- },
817
- directives: {
818
- infiniteScroll
819
- },
820
- filters: {
821
- formatName(item) {
822
- if (item.title) return item.title;
823
- // if (item.type == 2) {
824
- // return item.toName;
825
- // }
826
- const name = JSON.parse(item.params || {})?.name || "";
827
- return item.titleSetting || item.name || name;
828
- },
829
- formatTime(item) {
830
- return item.timeSetting || item.createdTime;
831
- },
832
- formatCount(item) {
833
- return item.msgCount || 0;
834
- }
835
- },
836
- watch: {
837
- /* 列表更新 */
838
- isScrollListChange: {
839
- handler(isScrollListChange) {
840
- if (isScrollListChange) {
841
- this.updateScrollList();
842
- this.setIsScrollListChange(false);
843
- this.setListChangeItem(null);
844
- // 分类
845
- if (this.assemblySetting.isListClassify !== 'Y') return;
846
- this.assemblySetting.listClassify.forEach(item => {
847
- let count = this.scrollList.filter(i => i.classify === item.classify).length;
848
- this.$set(item, 'count', count);
849
- });
850
- }
851
- }
852
- },
853
- dateParams() {
854
- if (this.enable) {
855
- if (this.currentTab === 'end') {
856
- this.pageNum = 1;
857
- this.getData(1);
858
- } else {
859
- this.getScrollList();
860
- }
861
- }
862
- },
863
- /* 组件更新 */
864
- currentTab: {
865
- immediate: true,
866
- handler(val) {
867
- if (this.enable) {
868
- this.pageNum = 1;
869
- if (val == 'end') return;
870
- this.getScrollList();
871
- }
872
- }
873
- },
874
- tabChange: {
875
- immediate: true,
876
- deep: true,
877
- handler(val) {
878
- if (!val || !Object.keys(val).length) return;
879
- if (this.currentTab != 'end') return;
880
- this.pageNum = 1;
881
- this.getData(1);
882
- }
883
- }
884
- },
885
- render(_h) {
886
- if (this.tableTheme) {
887
- return (
888
- <c-grid
889
- class="chat-scroll-session__table"
890
- resizable
891
- max-height="100%"
892
- auto-resize
893
- data={this.processedScrollList}
894
- columns={this.tableColumns}
895
- scroll-y={{ enabled: false }}
896
- scroll-x={{ enabled: false }}
897
- row-class-name={this.tableRowClass}
898
- on-cell-click={this.handleTableCellClick}
899
- on-scroll={this.handleTableScroll}
900
- ></c-grid>
901
- );
902
- }
903
- const { theme, currentTab, processedScrollList, handleInfiniteOnLoad, busy, infiniteMsgShow, foldTheme, classifyIcon } = this;
904
- const isEnd = currentTab === 'end';
905
- const isNoData = !processedScrollList?.length;
906
- const filters = this.$options.filters;
907
- const directives = [
908
- {
909
- name: 'infiniteScroll',
910
- value: handleInfiniteOnLoad
911
- }
912
- ];
913
- function getAvatar(item) {
914
- return <Avatar className="avatar" src={item.portrait} name={item.name || item.toName || item.title || item.titleSetting} len={2} />;
915
- }
916
- function getContent(item, filters, fold, classifyIcon) {
917
- const { formatName, formatCount } = filters;
918
- const name = formatName(item);
919
- const sendName = item.sendName;
920
- const receiveName = item.receiveName;
921
- const msgCount = item.msgCount;
922
- const statusSetting = item.statusSetting;
923
- return (
924
- <div class={{ 'chat-scroll-session__item': true, fold: fold }}>
925
- <div class="chat-scroll-session__name-time">
926
- <div class="chat-scroll-session__name-zhuanjie">
927
- <a-tooltip placement="topLeft">
928
- <template slot="title">{name}</template>
929
- <span class="chat-scroll-session__name">{name}</span>
930
- </a-tooltip>
931
- {fold && classifyIcon ? <svg-icon icon-class={classifyIcon} style="margin-left:4px"></svg-icon> : null}
932
- {sendName ? (
933
- <a-tooltip placement="topLeft">
934
- <template slot="title">{sendName + '转接给了你'}</template>
935
- <span class="chat-scroll-session__zhuanjie">
936
- <svg-icon icon-class="liaotian-zhuanchu" style="fontSize: 16px"></svg-icon>
937
- {sendName}
938
- </span>
939
- </a-tooltip>
940
- ) : null}
941
- {receiveName ? (
942
- <a-tooltip placement="topLeft">
943
- <template slot="title">{'你转接给了' + receiveName}</template>
944
- <span class="chat-scroll-session__zhuanchu">
945
- <svg-icon icon-class="liaotian-zhuanru" style="fontSize: 16px"></svg-icon>
946
- {receiveName}
947
- </span>
948
- </a-tooltip>
949
- ) : null}
950
- </div>
951
- <a-tooltip placement="topRight" title={item.timeSettingTip}>
952
- <span class="chat-scroll-session__time">{item.timeSetting}</span>
953
- </a-tooltip>
954
- </div>
955
- <div class="chat-scroll-session__remark">{item.remarkSetting}</div>
956
- <div class="chat-scroll-session__brief-count">
957
- <div class="chat-scroll-session__brief" domPropsInnerHTML={item.contentSetting}></div>
958
- {msgCount ? <span class="chat-scroll-session__count">{msgCount > 99 ? <a-tooltip title={msgCount}>99+</a-tooltip> : formatCount(item)}</span> : null}
959
- {statusSetting ? <span class="chat-scroll-session__level" domPropsInnerHTML={statusSetting}></span> : null}
960
- </div>
961
- </div>
962
- );
963
- }
964
-
965
- return (
966
- <div class={[theme, 'chat-scroll-container']}>
967
- <ul class="chat-scroll-session__list">
968
- {processedScrollList.map((item, index) => {
969
- const msgCount = item.msgCount;
970
- return (
971
- <li class={{ 'scroll-item': true, active: this.isActive(item) }} key={item.id || item.bid || index} onClick={() => this.handleClick(item)} id={'item_id_' + item.id}>
972
- {foldTheme
973
- ? [
974
- <a-popover overlayClassName="chat-list-popover--fold" placement="right" destroyTooltipOnHide={true}>
975
- <template slot="content">{getContent(item, filters, foldTheme, classifyIcon)}</template>
976
- {getAvatar(item)}
977
- </a-popover>,
978
- msgCount ? <div class="chat-scroll-session__num">{msgCount > 99 ? '99+' : msgCount}</div> : null
979
- ]
980
- : [getAvatar(item), getContent(item, filters, foldTheme, classifyIcon)]}
981
- </li>
982
- );
983
- })}
984
- </ul>
985
-
986
- {isEnd ? (
987
- <div {...{ directives }} infinite-scroll-immediate-check="immediate" infinite-scroll-disabled={busy} infinite-scroll-distance={10}>
988
- <div class="infinite-wrap" style={infiniteMsgShow ? '' : 'display:none'}>
989
- <a-spin size="small" />
990
- {foldTheme ? '' : '加载中'}
991
- </div>
992
- </div>
993
- ) : null}
994
-
995
- {isNoData && ((isEnd && !infiniteMsgShow) || !isEnd) ? (
996
- <div class={[theme, 'no-data']}>
997
- <img src={require('../img/no-data2.png')} />
998
- <span>{this.i18nText('1.1.6.11')}</span>
999
- </div>
1000
- ) : null}
1001
- </div>
1002
- );
1003
- }
1004
- };
1005
- </script>
1006
-
1007
- <style lang="less">
1008
- .no-data {
1009
- flex: 1;
1010
- display: flex;
1011
- flex-direction: column;
1012
- justify-content: center;
1013
- align-items: center;
1014
- & > img {
1015
- width: 160px;
1016
- }
1017
- &.fold > img {
1018
- width: 80%;
1019
- }
1020
- span {
1021
- display: inline-block;
1022
- font-size: 14px;
1023
- }
1024
- &.fold span {
1025
- font-size: 12px;
1026
- }
1027
- }
1028
- .chat-scroll-container {
1029
- height: 100%;
1030
- overflow-y: auto;
1031
- &::-webkit-scrollbar {
1032
- width: 5px;
1033
- }
1034
- .infinite-wrap {
1035
- padding: 16px;
1036
- text-align: center;
1037
- font-size: 14px;
1038
- color: #000000;
1039
- line-height: 20px;
1040
- .ant-spin {
1041
- margin-right: 8px;
1042
- }
1043
- }
1044
- }
1045
- .chat-scroll-session__list {
1046
- margin-bottom: -1px;
1047
- overflow: hidden;
1048
- .scroll-item {
1049
- position: relative;
1050
- display: flex;
1051
- align-items: center;
1052
- height: 82px;
1053
- padding: 15px 8px;
1054
- background: #fff;
1055
- border-bottom: 1px solid #d5d5d5;
1056
- &:hover {
1057
- background-color: #f4f4f4;
1058
- }
1059
- &.active {
1060
- background: #ebebeb;
1061
- .chat-scroll-session__name-time .chat-scroll-session__time {
1062
- color: #666;
1063
- }
1064
- }
1065
- .avatar {
1066
- margin-right: 20px;
1067
- }
1068
- }
1069
- }
1070
-
1071
- .chat-scroll-container.fold .chat-scroll-session__list {
1072
- .scroll-item {
1073
- border-bottom: 0;
1074
- justify-content: center;
1075
- background-color: transparent;
1076
- padding: 8px;
1077
- height: auto;
1078
- }
1079
- .avatar {
1080
- margin-right: 0;
1081
- }
1082
- }
1083
- .chat-list-popover--fold {
1084
- .ant-popover-content {
1085
- max-width: 300px;
1086
- }
1087
- .ant-popover-arrow {
1088
- display: none;
1089
- }
1090
- }
1091
-
1092
- .chat-scroll-session__item {
1093
- flex: 1;
1094
- width: 0;
1095
- &.fold {
1096
- width: 192px;
1097
- .chat-scroll-session__time {
1098
- font-size: 12px;
1099
- }
1100
- }
1101
- }
1102
- .chat-scroll-session__name-time {
1103
- display: flex;
1104
- justify-content: space-between;
1105
- align-items: center;
1106
- margin-bottom: 4px;
1107
- .chat-scroll-session__name {
1108
- cursor: pointer;
1109
- font-size: 16px;
1110
- color: #212121;
1111
- height: 26px;
1112
- font-weight: 600;
1113
- line-height: 26px;
1114
- flex: 1;
1115
- text-align: left;
1116
- white-space: nowrap;
1117
- text-overflow: ellipsis;
1118
- overflow: hidden;
1119
- word-break: break-all;
1120
- text-align: left;
1121
- }
1122
- .chat-scroll-session__time {
1123
- padding-left: 4px;
1124
- cursor: pointer;
1125
- color: #b3b3b3;
1126
- text-align: right;
1127
- white-space: nowrap;
1128
- text-overflow: ellipsis;
1129
- overflow: hidden;
1130
- word-break: break-all;
1131
- }
1132
- .chat-scroll-session__name-zhuanjie {
1133
- display: flex;
1134
- justify-content: flex-start;
1135
- align-items: center;
1136
- max-width: 65%;
1137
- }
1138
- .chat-scroll-session__zhuanchu,
1139
- .chat-scroll-session__zhuanjie {
1140
- line-height: 16px;
1141
- height: 18px;
1142
- display: flex;
1143
- align-items: center;
1144
- margin-left: 8px;
1145
- border: 1px solid #37d39f;
1146
- color: #37d39f;
1147
- border-radius: 2px;
1148
- font-size: 12px;
1149
- padding: 0 4px 0 2px;
1150
- border-radius: 2px;
1151
- }
1152
- .chat-scroll-session__zhuanchu {
1153
- color: #2d7aff;
1154
- border: 1px solid #2d7aff;
1155
- }
1156
- }
1157
- .chat-scroll-session__remark {
1158
- width: 100%;
1159
- text-overflow: ellipsis;
1160
- overflow: hidden;
1161
- white-space: nowrap;
1162
- }
1163
- .chat-scroll-session__brief-count {
1164
- display: flex;
1165
- justify-content: space-between;
1166
- align-items: center;
1167
- overflow: hidden;
1168
- height: 20px;
1169
- .chat-scroll-session__brief {
1170
- flex: 1;
1171
- width: 0;
1172
- height: 20px;
1173
- font-size: 14px;
1174
- color: rgba(33, 33, 33, 0.6);
1175
- line-height: 20px;
1176
- text-align: left;
1177
- white-space: nowrap;
1178
- text-overflow: ellipsis;
1179
- overflow: hidden;
1180
- word-break: break-all;
1181
- padding-right: 8px;
1182
- > div {
1183
- white-space: nowrap;
1184
- text-overflow: ellipsis;
1185
- overflow: hidden;
1186
- word-break: break-all;
1187
- }
1188
- }
1189
- .chat-scroll-session__level {
1190
- flex: 0 0 60px;
1191
- display: flex;
1192
- white-space: nowrap;
1193
- overflow: hidden;
1194
- }
1195
- }
1196
- .chat-scroll-session__num,
1197
- .chat-scroll-session__count {
1198
- display: block;
1199
- min-width: 28px;
1200
- height: 28px;
1201
- line-height: 28px;
1202
- text-align: center;
1203
- border-radius: 14px;
1204
- background: #e54949;
1205
- padding: 0 2px;
1206
- color: #fff;
1207
- font-size: 20px;
1208
- transform: scale(0.5);
1209
- }
1210
- .chat-scroll-session__num {
1211
- position: absolute;
1212
- right: 0;
1213
- top: 8px;
1214
- margin-right: 12px;
1215
- }
1216
- .chat-scroll-session__table {
1217
- .vxe-table--render-default .vxe-body--column.col--ellipsis,
1218
- .vxe-table--render-default.vxe-editable .vxe-body--column,
1219
- .vxe-table--render-default .vxe-footer--column.col--ellipsis,
1220
- .vxe-table--render-default .vxe-header--column.col--ellipsis {
1221
- height: 40px;
1222
- }
1223
- .vxe-table--render-default.border--default .vxe-table--header-wrapper,
1224
- .vxe-table--render-default.border--full .vxe-table--header-wrapper,
1225
- .vxe-table--render-default.border--outer .vxe-table--header-wrapper {
1226
- background-color: #fff;
1227
- }
1228
- .vxe-body--row.active {
1229
- background-color: #f2f2f2;
1230
- }
1231
- }
1232
- </style>
1
+ <script>
2
+ import Grid from '~/grid';
3
+ import infiniteScroll from 'vue-infinite-scroll';
4
+ import { mapGetters, mapMutations, mapActions } from '../store/helper';
5
+ import { getRecallText } from '../utils';
6
+ import Avatar from '../components/avatar';
7
+ import vexutils, { moment } from '@/utils/vexutils';
8
+ import fetch, { qs } from '@/utils/chatFetch';
9
+ import { Popover, Spin, Tooltip } from 'ant-design-vue';
10
+ import SvgIcon from '@/component/svg/index.vue';
11
+
12
+ export default {
13
+ inject: ['store', 'i18nText', 'dispatchEvent'],
14
+ components: {
15
+ [Tooltip.name]: Tooltip,
16
+ [Spin.name]: Spin,
17
+ [Popover.name]: Popover,
18
+ [Grid.name]: Grid,
19
+ SvgIcon,
20
+ Avatar
21
+ },
22
+ data() {
23
+ return {
24
+ scrollList: [],
25
+ defaultAvatar: require('../img/default.png'),
26
+ busy: false,
27
+ pageSize: 20,
28
+ pageNum: 1,
29
+ infiniteMsgShow: true
30
+ };
31
+ },
32
+ computed: {
33
+ ...mapGetters(['userInfo']),
34
+ ...mapGetters([
35
+ 'assemblyId',
36
+ 'classify',
37
+ 'isScrollListChange',
38
+ 'listChangeItem',
39
+ 'currentTab',
40
+ 'sessionId',
41
+ 'keywords',
42
+ 'searchKeywords',
43
+ 'enable',
44
+ 'panelParams',
45
+ 'assemblySetting',
46
+ 'clientId',
47
+ 'clientParams',
48
+ 'serviceId',
49
+ 'msgList',
50
+ 'queueItem'
51
+ ]),
52
+ foldTheme() {
53
+ return this.theme === 'fold';
54
+ },
55
+ tableTheme() {
56
+ return this.theme === 'table';
57
+ },
58
+ classifyIcon() {
59
+ const { classify, assemblySetting } = this;
60
+ if (!classify) return '';
61
+ const isListClassify = assemblySetting?.isListClassify === 'Y';
62
+ const target = isListClassify ? assemblySetting?.listClassify?.find(item => item.classify === classify) : null;
63
+ return target?.icon;
64
+ },
65
+ isListClassify() {
66
+ return this.assemblySetting.isListClassify === 'Y';
67
+ },
68
+ tabChange() {
69
+ let { searchKeywords, classify } = this;
70
+ return {
71
+ searchKeywords,
72
+ classify
73
+ };
74
+ },
75
+ tableColumns() {
76
+ const tableLayout = this.assemblySetting?.listLayoutSetting?.tableLayout;
77
+ if (tableLayout && tableLayout.length) {
78
+ const htmlField = ['${lastContent}'];
79
+ return tableLayout.map((item, i) => {
80
+ let { title = '', name = '' } = item;
81
+ title = title || name;
82
+ const w = Math.max((title?.length || 1) * 14 + 22, 60);
83
+ const key = name || i;
84
+ const data = {
85
+ ...item,
86
+ title,
87
+ // width: w,
88
+ field: `_${key}_`,
89
+ showHeaderOverflow: true,
90
+ showOverflow: true
91
+ };
92
+ if (htmlField.includes(item.value) || item.mapping?.length > 0) {
93
+ data.type = 'html';
94
+ }
95
+ return data;
96
+ });
97
+ }
98
+ return [];
99
+ },
100
+ // 根据后台配置信息进行排序
101
+ processedScrollList() {
102
+ let processedScrollList = [];
103
+ let settingList = [];
104
+ if (this.tableTheme) {
105
+ settingList = this.tableColumns.map(item => [item.field, item]);
106
+ } else {
107
+ let setting = this.assemblySetting.listLayoutSetting || {};
108
+ if (setting.timeSetting) {
109
+ setting = Object.assign({}, setting, {
110
+ timeSettingTip: setting.timeSetting // 时间类型 tooltip 展示的值和原值不同 额外处理
111
+ });
112
+ }
113
+ settingList = Object.entries(setting);
114
+ }
115
+ let reg = /\$\{(.+)\}/;
116
+ const { currentTab, keywords, classify, isListClassify } = this;
117
+ processedScrollList = this.scrollList.reduce((list, item) => {
118
+ if (item.kind == 2 || !this.showScrollItem(item, currentTab, keywords, classify, isListClassify)) return list; // <- 通话记录跳过
119
+ const layoutData = {};
120
+ for (let [key, value] of settingList) {
121
+ const isTip = /.+Tip$/.test(key);
122
+ let parseValue = (value.value || '').replace(/\$\{([^}]+)\}/g, (_v, $1) => {
123
+ let key = $1;
124
+ let param;
125
+ if (key.startsWith('form.')) {
126
+ key = key.split('.')[1];
127
+ let params = JSON.parse(item.params || '{}');
128
+ param = params[key] || this.getMsgFieldValue(key, item, isTip);
129
+ } else {
130
+ param = this.getMsgFieldValue(key, item, isTip);
131
+ }
132
+ if (value.mapping) {
133
+ // param = this.getMappingHTML(value.mapping[param]);
134
+ param = this.getMappingHTML(value.mapping.find( v => v.value == param) || {});
135
+ }
136
+ return param || '';
137
+ });
138
+ layoutData[key] = parseValue;
139
+ }
140
+ list.push({ ...item, ...layoutData });
141
+ return list;
142
+ }, []);
143
+
144
+ // 排队列表排序(1.等级2.创建时间)
145
+ const sortTabs = ['queue', 'session'];
146
+ const sortKeys = ['queueSortSetting', 'sessionSortSetting'];
147
+ const sortIndex = sortTabs.indexOf(currentTab);
148
+ if (sortIndex >= 0) {
149
+ // 获取对应sortsetting, listSortSetting兼容旧数据
150
+ const sortSetting = this.assemblySetting[sortKeys[sortIndex]] || this.assemblySetting.listSortSetting;
151
+ if (sortSetting) {
152
+ let { isParams, descOrAsc, orderBy } = sortSetting;
153
+ orderBy = orderBy ? orderBy.replace(reg, '$1') : orderBy; // 去除 ${}
154
+ let hasLevel = [],
155
+ noLevel = [];
156
+ processedScrollList.forEach(item => {
157
+ let value;
158
+ if (isParams) {
159
+ value = JSON.parse(item.params || '{}')[orderBy];
160
+ } else {
161
+ value = item[orderBy];
162
+ }
163
+ vexutils.isUndefined(value) ? noLevel.push(item) : hasLevel.push(item);
164
+ });
165
+ hasLevel.sort((a, b) => {
166
+ let aLevel = JSON.parse(a.params || '{}')[orderBy] || a[orderBy];
167
+ let bLevel = JSON.parse(b.params || '{}')[orderBy] || b[orderBy];
168
+ if (aLevel === bLevel) {
169
+ return Number(a.createdTime) - Number(b.createdTime);
170
+ }
171
+ // return descOrAsc === 'desc' ? aLevel - bLevel : bLevel - aLevel;
172
+ return descOrAsc === 'desc' ? bLevel - aLevel : aLevel - bLevel;
173
+ });
174
+ noLevel.sort((a, b) => Number(a.createdTime) - Number(b.createdTime));
175
+ processedScrollList = [...hasLevel, ...noLevel];
176
+ } else {
177
+ processedScrollList.sort((a, b) => Number(a.createdTime) - Number(b.createdTime));
178
+ }
179
+ }
180
+ return processedScrollList;
181
+ }
182
+ },
183
+ props: {
184
+ // 日期过滤条件
185
+ dateParams: {
186
+ type: Array,
187
+ default: () => []
188
+ },
189
+ theme: {
190
+ type: String,
191
+ default: ''
192
+ },
193
+ // 默认会话id
194
+ defSessionId: {
195
+ type: [String, Number]
196
+ },
197
+ type: {
198
+ type: String
199
+ }
200
+ },
201
+ methods: {
202
+ ...mapMutations([
203
+ 'setSessionId',
204
+ 'setIsScrollListChange',
205
+ 'setListChangeItem',
206
+ 'setCurrentTab',
207
+ 'setClientId',
208
+ 'setOnChating',
209
+ 'setClientParams',
210
+ 'setQueueItem',
211
+ 'setScrollTo',
212
+ 'setSessionHistoryList',
213
+ 'setSessionEnd',
214
+ 'setQueueStatistic',
215
+ 'setSessionStatistic',
216
+ 'offlineSessionStatistic',
217
+ 'appendList',
218
+ 'setSessionType',
219
+ 'setOfflineClassifyCount',
220
+ 'setScrollQueueId',
221
+ 'setLastClassify',
222
+ 'setLastCurrentTab',
223
+ 'setFooterMessage',
224
+ 'setCurScrollItem',
225
+ 'setDisableOperate',
226
+ 'setClassify',
227
+ 'setOfflineSessionStatistic',
228
+ 'setClosedSession'
229
+ ]),
230
+ ...mapMutations({
231
+ clearMsgList: 'setMsgList'
232
+ }),
233
+ ...mapActions(['setMsgList', 'setChatTimer']),
234
+ isActive(item) {
235
+ let res = this.sessionId === item.id || (item.username && item.username === this.clientId);
236
+ return res;
237
+ },
238
+ formatBrief(item) {
239
+ let { lastContentRecallFlag } = item;
240
+ if (lastContentRecallFlag == 1) {
241
+ const content = JSON.parse(item.lastContent);
242
+ const flag = this.serviceId == item.fromId;
243
+ return getRecallText(this, { content }, flag);
244
+ }
245
+ let lastItem;
246
+ try {
247
+ lastItem = JSON.parse(item.lastContent || '{}');
248
+ } catch {
249
+ console.log(item.lastContent.value);
250
+ }
251
+ if (!lastItem) return '';
252
+ let { type, content } = lastItem;
253
+ if (type === 1) {
254
+ return `[${this.i18nText('1.9.181')}]`;
255
+ } else if (type === 0) {
256
+ return content;
257
+ } else if (type === 2) {
258
+ // return `[${this.i18nText("1.9.388")}]`;
259
+ return '[视频通话]';
260
+ } else if (type === 5) {
261
+ return '[语音通话]';
262
+ } else if (type === 6) {
263
+ return '[语音消息]';
264
+ } else if (type === 3) {
265
+ return JSON.parse(content)?.type == 2 ? `[${this.i18nText('1.9.389')}]` : `[${this.i18nText('1.9.390')}]`;
266
+ } else if (type === 4) {
267
+ let title = content.title ? content.title : JSON.parse(content).title;
268
+ return `[${title}]`;
269
+ } else {
270
+ return '';
271
+ }
272
+ },
273
+ showScrollItem(item, currentTab, keywords, classify, isListClassify) {
274
+ if (currentTab == 'end') return true;
275
+ return (!keywords || (item.name || item.toName || item.title).includes(keywords)) && (item.classify === classify || !isListClassify);
276
+ },
277
+ handleInfiniteOnLoad() {
278
+ if (this.busy) return;
279
+ if (this.currentTab !== 'end') {
280
+ this.busy = true;
281
+ this.infiniteMsgShow = false;
282
+ return;
283
+ }
284
+ this.busy = true;
285
+ this.infiniteMsgShow = true;
286
+ // 发送请求有时间间隔第一个滚动时间结束后才发送第二个请求
287
+ this.pageNum++; // 滚动之后加载第二页
288
+ this.getScrollList(true);
289
+ },
290
+ async getScrollList(flag) {
291
+ switch (this.currentTab) {
292
+ case 'queue':
293
+ this.getQueueUsers();
294
+ break;
295
+ case 'session':
296
+ await this.getSessionList();
297
+ this.handleSelectItem();
298
+ break;
299
+ case 'end':
300
+ // this.getSessionList(flag);
301
+ this.getData(1, flag);
302
+ }
303
+ },
304
+ /* 获取会话列表 */
305
+ getSessionList() {
306
+ let params = {
307
+ ...this.getDateParams(),
308
+ assemblyId: this.assemblyId,
309
+ onlineType: this.currentTab === 'session' ? 1 : 0
310
+ };
311
+ if (this.isListClassify) {
312
+ this.assemblySetting.listClassify.forEach(item => {
313
+ this.$set(item, 'count', '');
314
+ });
315
+ }
316
+ return fetch
317
+ .get('/chat/service/getMySessionList', {
318
+ params
319
+ })
320
+ .then(({ data }) => {
321
+ if (data.result === 'SUCCESS') {
322
+ this.scrollList = data.map.list;
323
+ if (this.isListClassify) {
324
+ this.assemblySetting.listClassify.forEach(item => {
325
+ let count = data.map.list.filter(i => i.classify === item.classify).length;
326
+ this.$set(item, 'count', count);
327
+ });
328
+ }
329
+ if (data.map.listStatistic) {
330
+ if (this.currentTab === 'end') {
331
+ this.setOfflineSessionStatistic(data.map.listStatistic);
332
+ } else {
333
+ this.setSessionStatistic(data.map.listStatistic);
334
+ }
335
+ }
336
+ } else {
337
+ this.$message.warning(data.resultMsg);
338
+ }
339
+ });
340
+ },
341
+ async getData(type, flag) {
342
+ try {
343
+ let res = await this.handleDataRequest(type);
344
+ let { data = {} } = res;
345
+ if (data.result === 'SUCCESS') {
346
+ let { list = [] } = data.map;
347
+ if (flag) {
348
+ if (!list || !list.length) {
349
+ this.busy = true;
350
+ this.infiniteMsgShow = false;
351
+ } else {
352
+ this.busy = false;
353
+ this.scrollList = this.scrollList.concat(list);
354
+ }
355
+ return;
356
+ } else {
357
+ this.infiniteMsgShow = false;
358
+ this.busy = false;
359
+ this.scrollList = list;
360
+ }
361
+ if (this.isListClassify) {
362
+ this.assemblySetting.listClassify.forEach(item => {
363
+ let count = list.filter(i => i.classify === item.classify).length;
364
+ this.$set(item, 'count', count);
365
+ });
366
+ }
367
+ let { classifyStatistic = {} } = data.map || {};
368
+ if (Object.keys(classifyStatistic).length) {
369
+ this.setOfflineClassifyCount(classifyStatistic);
370
+ }
371
+ } else {
372
+ this.infiniteMsgShow = false;
373
+ this.$message.warning(data.resultMsg);
374
+ }
375
+ this.handleSelectItem();
376
+ } catch (err) {
377
+ this.infiniteMsgShow = false;
378
+ console.log(err);
379
+ }
380
+ },
381
+ getDateParams() {
382
+ let params = {};
383
+ const dateParams = this.dateParams;
384
+ if (dateParams?.length) {
385
+ const fmt = 'YYYY-MM-DD';
386
+ params.startDate = dateParams[0].format(fmt) + ' 00:00:00';
387
+ params.endDate = dateParams[1].format(fmt) + ' 23:59:59';
388
+ }
389
+ return params;
390
+ },
391
+ handleDataRequest(type) {
392
+ let url = type == 1 ? '/chat/service/getMySessionList' : '/chat/service/getQueueUsers';
393
+ let params = {
394
+ ...this.getDateParams(),
395
+ assemblyId: this.assemblyId,
396
+ pageSize: this.pageSize,
397
+ pageNum: this.pageNum
398
+ };
399
+ if (type == 1) {
400
+ params.onlineType = this.currentTab === 'session' ? 1 : 0;
401
+ }
402
+ if (this.currentTab == 'end') {
403
+ params.keyword = this.searchKeywords;
404
+ params.classify = this.classify;
405
+ }
406
+ if (this.isListClassify) {
407
+ this.assemblySetting.listClassify.forEach(item => {
408
+ this.$set(item, 'count', '');
409
+ });
410
+ }
411
+ return fetch.get(url, {
412
+ params
413
+ });
414
+ },
415
+ /* 获取排队列表 */
416
+ getQueueUsers() {
417
+ if (this.isListClassify) {
418
+ this.assemblySetting.listClassify.forEach(item => {
419
+ this.$set(item, 'count', '');
420
+ });
421
+ }
422
+ // let params = { assemblyId: this.assemblyId };
423
+ let params = {
424
+ ...this.getDateParams(),
425
+ assemblyId: this.assemblyId,
426
+ };
427
+ fetch
428
+ .get('/chat/service/getQueueUsers', {
429
+ params
430
+ })
431
+ .then(({ data }) => {
432
+ if (data.result === 'SUCCESS') {
433
+ this.scrollList = data.map.list;
434
+ if (this.isListClassify) {
435
+ this.assemblySetting.listClassify.forEach(item => {
436
+ let count = data.map.list.filter(i => i.classify === item.classify).length;
437
+ this.$set(item, 'count', count);
438
+ });
439
+ }
440
+ if (data.map.listStatistic) {
441
+ this.setQueueStatistic(data.map.listStatistic);
442
+ }
443
+ } else {
444
+ this.$message.warning(data.resultMsg);
445
+ }
446
+ });
447
+ },
448
+ /* 点击列表项 */
449
+ handleClick(item) {
450
+ this.setLastClassify(this.classify);
451
+ this.setLastCurrentTab(this.currentTab);
452
+ this.setCurScrollItem(item);
453
+ this.setClosedSession(this.type === 'end');
454
+ switch (this.type) {
455
+ case 'queue':
456
+ this.accessChat(item);
457
+ break;
458
+ case 'session':
459
+ this.getMsgList(item);
460
+ break;
461
+ case 'end':
462
+ if (item.msgCount > 0) {
463
+ this.cleanSessionCount(item);
464
+ }
465
+ this.getEndMsgList(item);
466
+ }
467
+ this.dispatchEvent('click_sessionListUser', JSON.parse(JSON.stringify(item)));
468
+ },
469
+ handleTableCellClick({ row }) {
470
+ this.handleClick(row);
471
+ },
472
+ tableRowClass({ row }) {
473
+ return this.isActive(row) ? 'active' : '';
474
+ },
475
+ handleTableScroll(args) {
476
+ let target;
477
+ if (this.currentTab == 'end' && !this.busy && (target = args.$event.target) && target.scrollHeight - target.clientHeight - args.scrollTop <= 10) {
478
+ this.handleInfiniteOnLoad();
479
+ }
480
+ },
481
+ cleanSessionCount(item) {
482
+ let params = {
483
+ sessionId: item.id,
484
+ orgId: this.userInfo?.sysParams?.orgId,
485
+ assemblyId: this.assemblyId
486
+ };
487
+ fetch.post('/chat/service/cleanSessionCount', qs.stringify(params)).then(({ data }) => {
488
+ if (data.result === 'SUCCESS') {
489
+ item.msgCount = 0;
490
+ }
491
+ });
492
+ },
493
+ /* 查看结束历史消息 */
494
+ getEndMsgList(item) {
495
+ let { id, toId } = item;
496
+ this.setClientId(toId);
497
+ this.setSessionId(id);
498
+ this.clearMsgList();
499
+ this.setMsgList({ validate: this.getMsgValidateFn(toId) });
500
+ this.setOnChating(false);
501
+ this.setChatTimer();
502
+ let clientParams = JSON.parse(item.params || '{}');
503
+ this.setClientParams(clientParams);
504
+ // this.getSessionHistoryList();
505
+ this.setQueueItem(null);
506
+ },
507
+ /* 客服点击会话列表进入会话, id:会话id, toId:客户id */
508
+ getMsgList(item) {
509
+ let { id, toId, params: cParams } = item;
510
+ let lastSessionId = this.sessionId;
511
+ this.setQueueItem(null);
512
+ item.msgCount = 0;
513
+ let params = {
514
+ sessionId: id,
515
+ lastSessionId,
516
+ assemblyId: this.assemblyId
517
+ };
518
+ // 心跳
519
+ fetch.post(
520
+ '/chat/service/sessionHeart',
521
+ qs.stringify({
522
+ assemblyId: this.assemblyId,
523
+ sessionId: id
524
+ })
525
+ );
526
+ this.setDisableOperate(true);
527
+ fetch
528
+ .post('/chat/service/accessSession', qs.stringify(params))
529
+ .then(({ data }) => {
530
+ this.setScrollQueueId('');
531
+ this.setSessionId(id);
532
+ if (data.result === 'SUCCESS') {
533
+ let { countdown, clientParams, sessionType, countdownDesc } = data.map;
534
+ this.setClientId(toId);
535
+ this.clearMsgList();
536
+ this.setMsgList({ validate: this.getMsgValidateFn(toId) });
537
+ this.setOnChating(true);
538
+ this.setChatTimer({ countdown, countdownDesc });
539
+ this.setSessionEnd(false);
540
+ this.setClientParams(clientParams || JSON.parse(cParams || '{}'));
541
+ this.getSessionHistoryList();
542
+ this.setSessionType(sessionType);
543
+ this.setFooterMessage('');
544
+ } else {
545
+ this.$message.warning(data.resultMsg);
546
+ }
547
+ })
548
+ .finally(() => {
549
+ this.setDisableOperate(false);
550
+ });
551
+ },
552
+ getMsgValidateFn(id) {
553
+ return () => id === this.clientId;
554
+ },
555
+ scrollToItem(sessionId) {
556
+ if (!sessionId) return;
557
+ this.$nextTick(() => {
558
+ const target = this.$el.querySelector(`#item_id_${sessionId}`);
559
+ if (!target) return;
560
+ target.scrollIntoView(true);
561
+ });
562
+ },
563
+ getSessionHistoryList() {
564
+ if (this.assemblySetting?.type == 'staff') return;
565
+ this.setSessionHistoryList([]);
566
+ if (this.assemblySetting.recordScopeSetting === 'CURRENT') return;
567
+ fetch
568
+ .get('/chat/service/getPastSessionList', {
569
+ params: { sessionId: this.sessionId }
570
+ })
571
+ .then(({ data }) => {
572
+ if (data.result === 'SUCCESS') {
573
+ let list = data.list.map(item => {
574
+ if (item.fromId === this.serviceId) {
575
+ item.onChating = true;
576
+ }
577
+ return item;
578
+ });
579
+ this.setSessionHistoryList(list);
580
+ }
581
+ });
582
+ },
583
+ /* 客服点击排队列表进入会话 */
584
+ accessChat(item) {
585
+ this.getQueueMsgList(item);
586
+ },
587
+ /* 排队消息 */
588
+ getQueueMsgList(item) {
589
+ let { bid: userId, userType, username, params: clientParams } = item;
590
+ let params = {
591
+ assemblyId: this.assemblyId,
592
+ userId,
593
+ userType,
594
+ relateId: JSON.parse(clientParams || '{}').relateId
595
+ };
596
+ fetch.get('/chat/service/getQueueUser', { params }).then(({ data }) => {
597
+ if (data.result === 'SUCCESS') {
598
+ const obj = data.obj || {};
599
+ this.currentTab === 'queue' && this.dispatchEvent('click_queueListUser', JSON.parse(JSON.stringify(obj.messages || [])));
600
+ let messages = (obj.messages || []).map(item => {
601
+ item.fromName = obj.name;
602
+ item.content = JSON.parse(JSON.stringify(item));
603
+ return item;
604
+ });
605
+ let robotMessages = this.handleRobotMessages(obj.robotMessages, obj.name);
606
+ this.setScrollQueueId(obj.bid);
607
+ this.setClientParams(JSON.parse(clientParams || '{}'));
608
+ this.setClientId(username);
609
+ this.setSessionId(null);
610
+ this.setSessionHistoryList([]);
611
+ this.clearMsgList([...robotMessages, ...messages]);
612
+ this.setQueueItem(item);
613
+ this.setChatTimer();
614
+ this.$nextTick(() => {
615
+ this.setScrollTo('bottom');
616
+ });
617
+ }
618
+ });
619
+ },
620
+ handleRobotMessages(robotMessages, name) {
621
+ if (!robotMessages || robotMessages.length === 0) return [];
622
+ return robotMessages.map(item => {
623
+ let content = item.content;
624
+ if (item.content && vexutils.isJSON(item.content)) {
625
+ content = JSON.parse(item.content);
626
+ }
627
+ item.content = content;
628
+ item.fromName = name;
629
+ return item;
630
+ });
631
+ },
632
+ // queue: 排队列表, session: 会话列表, end: 已结束列表
633
+ updateScrollList() {
634
+ const v = this.listChangeItem;
635
+ if (!v) return;
636
+ // 存在队列id 更新排队列表
637
+ if (v.queueId) {
638
+ this.updateQueueList();
639
+ } else if (v.sessionId) {
640
+ // 存在会话id 更新排队列表
641
+ this.updateSessionList();
642
+ } else {
643
+ this.updateEndList();
644
+ }
645
+ },
646
+ /* 更新咨询结束列表 */
647
+ updateEndList() {
648
+ let { lastTime, lastContent, opt, sessionId, username, name } = this.listChangeItem;
649
+ if (opt === 2) {
650
+ this.scrollList.unshift({
651
+ toId: username,
652
+ lastTime,
653
+ lastContent,
654
+ toName: name,
655
+ id: sessionId
656
+ });
657
+ }
658
+ },
659
+ /* 更新会话列表 */
660
+ updateSessionList() {
661
+ let { msgCount = undefined, lastContent, opt, from, to, sendTime, sessionId, lastTime, lastContentRecallFlag = undefined } = this.listChangeItem;
662
+ lastContent = vexutils.isJSON(lastContent) ? JSON.parse(lastContent) : lastContent;
663
+ // 0: 新增, 1: 更新, 2: 删除
664
+ if (opt === 0) {
665
+ this.scrollList.unshift(
666
+ Object.assign(
667
+ {},
668
+ this.listChangeItem,
669
+ {
670
+ lastContent: JSON.stringify(lastContent)
671
+ },
672
+ { id: sessionId, lastTime: sendTime, fromId: from, toId: to }
673
+ )
674
+ );
675
+ } else if (opt === 1) {
676
+ let realIndex = this.scrollList.findIndex(item => item.id === sessionId);
677
+ if (realIndex === -1) return;
678
+ let res = {
679
+ lastTime: sendTime || lastTime,
680
+ sendTime,
681
+ lastContentRecallFlag
682
+ };
683
+ if (lastContentRecallFlag != 1) {
684
+ if (msgCount !== undefined) {
685
+ res.msgCount = msgCount;
686
+ }
687
+ res.lastContent = JSON.stringify(lastContent);
688
+ }
689
+ let item = Object.assign({}, this.scrollList[realIndex], res);
690
+ this.scrollList.splice(realIndex, 1, item);
691
+ } else if (opt === 2) {
692
+ let realIndex = this.scrollList.findIndex(item => item.id === sessionId);
693
+ if (realIndex === -1) return;
694
+ this.scrollList.splice(realIndex, 1);
695
+ }
696
+ },
697
+ /* 更新排队列表 */
698
+ updateQueueList() {
699
+ let { sendTime, sendName, receiveName, opt, name, username, bid, msgCount, messages = [], createdTime, userType, classify, params, lastTime, lastContent } = this.listChangeItem;
700
+ if (messages.length === 0) {
701
+ messages = [{ createdTime }];
702
+ }
703
+ lastTime = lastTime || sendTime;
704
+ lastContent = vexutils.isPlainObject(lastContent) ? JSON.stringify(lastContent) : lastContent;
705
+ let current_item = {
706
+ sendName,
707
+ receiveName,
708
+ msgCount,
709
+ username,
710
+ bid,
711
+ name,
712
+ messages,
713
+ createdTime: new Date(createdTime).getTime(),
714
+ userType,
715
+ classify,
716
+ params,
717
+ lastTime: new Date(lastTime).getTime(),
718
+ lastContent
719
+ };
720
+ // 0: 新增, 1: 更新, 2: 删除
721
+ if (opt === 0) {
722
+ this.scrollList.unshift(current_item);
723
+ } else if (opt === 2) {
724
+ let realIndex = this.scrollList.findIndex(item => item.username == username);
725
+ this.scrollList.splice(realIndex, 1);
726
+ } else if (opt === 1) {
727
+ let realIndex = this.scrollList.findIndex(item => item.username == username);
728
+ this.scrollList.splice(realIndex, 1, current_item);
729
+ // 排队消息更新
730
+ if (this.queueItem && username === this.queueItem.username) {
731
+ this.clearMsgList([
732
+ ...this.msgList,
733
+ {
734
+ fromName: name,
735
+ content: Object.assign(JSON.parse(lastContent), { lastTime })
736
+ }
737
+ ]);
738
+ this.appendList([
739
+ {
740
+ fromName: name,
741
+ content: Object.assign(JSON.parse(lastContent), { lastTime })
742
+ }
743
+ ]);
744
+ }
745
+ }
746
+ },
747
+ getMappingHTML(value) {
748
+ if (!vexutils.isObject(value)) return value;
749
+ const { icon = '', desc, color } = value;
750
+ let str = '';
751
+ if (icon) {
752
+ str += `<svg aria-hidden="true" style="font-size 20px;width: 1em;height: 1em;" class={svg-icon}><use xlinkhref={#icon-${icon}}></use></svg>`;
753
+ }
754
+ if (desc) {
755
+ str += `<span style="color:#fff;padding:0 4px;${color ? 'background:' + color : ''}">${desc}</span>`;
756
+ }
757
+ return str;
758
+ },
759
+ // 获取聊天字段对应的值(部分字段需要格式化处理)
760
+ getMsgFieldValue(key, item, isTip = false) {
761
+ let val = item[key];
762
+ switch (key) {
763
+ case 'createdTime':
764
+ case 'lastTime':
765
+ val = this.formatDate(val || item.createdTime, isTip);
766
+ break;
767
+ case 'lastContent':
768
+ val = this.formatBrief(item);
769
+ break;
770
+ }
771
+ return val;
772
+ },
773
+ handleSelectItem() {
774
+ let sessionId = this.defSessionId;
775
+ if (!sessionId || this._selected) return;
776
+ let matchItem = this.processedScrollList.find(item => item.id === sessionId);
777
+ if (!matchItem) return;
778
+ this._selected = 1;
779
+ this.setClassify(matchItem.classify);
780
+ this.handleClick(matchItem);
781
+ this.$nextTick(() => {
782
+ const target = this.$el.querySelector(`#item_id_${sessionId}`);
783
+ if (!target) return;
784
+ target.scrollIntoView(true);
785
+ });
786
+ },
787
+ formatDate(date, showDetail) {
788
+ let input;
789
+ if (!isNaN(Number(date))) {
790
+ input = moment(Number(date));
791
+ } else {
792
+ input = moment(new Date(date));
793
+ }
794
+ if (showDetail) {
795
+ return input.format('YYYY年M月D日 HH:mm:ss');
796
+ }
797
+ // let input = moment(new Date(date));
798
+ let now = moment();
799
+ let today = moment(now.toArray().slice(0, 3));
800
+ let yestoday = moment(today).subtract(1, 'days');
801
+ let thisYear = moment(now.toArray().slice(0, 1));
802
+ let inToday = today.isBefore(input);
803
+ let inYestoday = input.isBetween(yestoday, today);
804
+ let inThisYear = thisYear.isBefore(input);
805
+ if (inToday) {
806
+ return '今天' + input.format('HH:mm');
807
+ } else if (inYestoday) {
808
+ return '昨天' + input.format('HH:mm');
809
+ } else if (inThisYear) {
810
+ return input.format('M月D日');
811
+ } else {
812
+ return input.format('YYYY年M月D日');
813
+ }
814
+ // return vexutils.formatDate(date);
815
+ }
816
+ },
817
+ directives: {
818
+ infiniteScroll
819
+ },
820
+ filters: {
821
+ formatName(item) {
822
+ if (item.title) return item.title;
823
+ // if (item.type == 2) {
824
+ // return item.toName;
825
+ // }
826
+ const name = JSON.parse(item.params || {})?.name || "";
827
+ return item.titleSetting || item.name || name;
828
+ },
829
+ formatTime(item) {
830
+ return item.timeSetting || item.createdTime;
831
+ },
832
+ formatCount(item) {
833
+ return item.msgCount || 0;
834
+ }
835
+ },
836
+ watch: {
837
+ /* 列表更新 */
838
+ isScrollListChange: {
839
+ handler(isScrollListChange) {
840
+ if (isScrollListChange) {
841
+ this.updateScrollList();
842
+ this.setIsScrollListChange(false);
843
+ this.setListChangeItem(null);
844
+ // 分类
845
+ if (this.assemblySetting.isListClassify !== 'Y') return;
846
+ this.assemblySetting.listClassify.forEach(item => {
847
+ let count = this.scrollList.filter(i => i.classify === item.classify).length;
848
+ this.$set(item, 'count', count);
849
+ });
850
+ }
851
+ }
852
+ },
853
+ dateParams() {
854
+ if (this.enable) {
855
+ if (this.currentTab === 'end') {
856
+ this.pageNum = 1;
857
+ this.getData(1);
858
+ } else {
859
+ this.getScrollList();
860
+ }
861
+ }
862
+ },
863
+ /* 组件更新 */
864
+ currentTab: {
865
+ immediate: true,
866
+ handler(val) {
867
+ if (this.enable) {
868
+ this.pageNum = 1;
869
+ if (val == 'end') return;
870
+ this.getScrollList();
871
+ }
872
+ }
873
+ },
874
+ tabChange: {
875
+ immediate: true,
876
+ deep: true,
877
+ handler(val) {
878
+ if (!val || !Object.keys(val).length) return;
879
+ if (this.currentTab != 'end') return;
880
+ this.pageNum = 1;
881
+ this.getData(1);
882
+ }
883
+ }
884
+ },
885
+ render(_h) {
886
+ if (this.tableTheme) {
887
+ return (
888
+ <c-grid
889
+ class="chat-scroll-session__table"
890
+ resizable
891
+ max-height="100%"
892
+ auto-resize
893
+ data={this.processedScrollList}
894
+ columns={this.tableColumns}
895
+ scroll-y={{ enabled: false }}
896
+ scroll-x={{ enabled: false }}
897
+ row-class-name={this.tableRowClass}
898
+ on-cell-click={this.handleTableCellClick}
899
+ on-scroll={this.handleTableScroll}
900
+ ></c-grid>
901
+ );
902
+ }
903
+ const { theme, currentTab, processedScrollList, handleInfiniteOnLoad, busy, infiniteMsgShow, foldTheme, classifyIcon } = this;
904
+ const isEnd = currentTab === 'end';
905
+ const isNoData = !processedScrollList?.length;
906
+ const filters = this.$options.filters;
907
+ const directives = [
908
+ {
909
+ name: 'infiniteScroll',
910
+ value: handleInfiniteOnLoad
911
+ }
912
+ ];
913
+ function getAvatar(item) {
914
+ return <Avatar className="avatar" src={item.portrait} name={item.name || item.toName || item.title || item.titleSetting} len={2} />;
915
+ }
916
+ function getContent(item, filters, fold, classifyIcon) {
917
+ const { formatName, formatCount } = filters;
918
+ const name = formatName(item);
919
+ const sendName = item.sendName;
920
+ const receiveName = item.receiveName;
921
+ const msgCount = item.msgCount;
922
+ const statusSetting = item.statusSetting;
923
+ return (
924
+ <div class={{ 'chat-scroll-session__item': true, fold: fold }}>
925
+ <div class="chat-scroll-session__name-time">
926
+ <div class="chat-scroll-session__name-zhuanjie">
927
+ <a-tooltip placement="topLeft">
928
+ <template slot="title">{name}</template>
929
+ <span class="chat-scroll-session__name">{name}</span>
930
+ </a-tooltip>
931
+ {fold && classifyIcon ? <svg-icon icon-class={classifyIcon} style="margin-left:4px"></svg-icon> : null}
932
+ {sendName ? (
933
+ <a-tooltip placement="topLeft">
934
+ <template slot="title">{sendName + '转接给了你'}</template>
935
+ <span class="chat-scroll-session__zhuanjie">
936
+ <svg-icon icon-class="liaotian-zhuanchu" style="fontSize: 16px"></svg-icon>
937
+ {sendName}
938
+ </span>
939
+ </a-tooltip>
940
+ ) : null}
941
+ {receiveName ? (
942
+ <a-tooltip placement="topLeft">
943
+ <template slot="title">{'你转接给了' + receiveName}</template>
944
+ <span class="chat-scroll-session__zhuanchu">
945
+ <svg-icon icon-class="liaotian-zhuanru" style="fontSize: 16px"></svg-icon>
946
+ {receiveName}
947
+ </span>
948
+ </a-tooltip>
949
+ ) : null}
950
+ </div>
951
+ <a-tooltip placement="topRight" title={item.timeSettingTip}>
952
+ <span class="chat-scroll-session__time">{item.timeSetting}</span>
953
+ </a-tooltip>
954
+ </div>
955
+ <div class="chat-scroll-session__remark">{item.remarkSetting}</div>
956
+ <div class="chat-scroll-session__brief-count">
957
+ <div class="chat-scroll-session__brief" domPropsInnerHTML={item.contentSetting}></div>
958
+ {msgCount ? <span class="chat-scroll-session__count">{msgCount > 99 ? <a-tooltip title={msgCount}>99+</a-tooltip> : formatCount(item)}</span> : null}
959
+ {statusSetting ? <span class="chat-scroll-session__level" domPropsInnerHTML={statusSetting}></span> : null}
960
+ </div>
961
+ </div>
962
+ );
963
+ }
964
+
965
+ return (
966
+ <div class={[theme, 'chat-scroll-container']}>
967
+ <ul class="chat-scroll-session__list">
968
+ {processedScrollList.map((item, index) => {
969
+ const msgCount = item.msgCount;
970
+ return (
971
+ <li class={{ 'scroll-item': true, active: this.isActive(item) }} key={item.id || item.bid || index} onClick={() => this.handleClick(item)} id={'item_id_' + item.id}>
972
+ {foldTheme
973
+ ? [
974
+ <a-popover overlayClassName="chat-list-popover--fold" placement="right" destroyTooltipOnHide={true}>
975
+ <template slot="content">{getContent(item, filters, foldTheme, classifyIcon)}</template>
976
+ {getAvatar(item)}
977
+ </a-popover>,
978
+ msgCount ? <div class="chat-scroll-session__num">{msgCount > 99 ? '99+' : msgCount}</div> : null
979
+ ]
980
+ : [getAvatar(item), getContent(item, filters, foldTheme, classifyIcon)]}
981
+ </li>
982
+ );
983
+ })}
984
+ </ul>
985
+
986
+ {isEnd ? (
987
+ <div {...{ directives }} infinite-scroll-immediate-check="immediate" infinite-scroll-disabled={busy} infinite-scroll-distance={10}>
988
+ <div class="infinite-wrap" style={infiniteMsgShow ? '' : 'display:none'}>
989
+ <a-spin size="small" />
990
+ {foldTheme ? '' : '加载中'}
991
+ </div>
992
+ </div>
993
+ ) : null}
994
+
995
+ {isNoData && ((isEnd && !infiniteMsgShow) || !isEnd) ? (
996
+ <div class={[theme, 'no-data']}>
997
+ <img src={require('../img/no-data2.png')} />
998
+ <span>{this.i18nText('1.1.6.11')}</span>
999
+ </div>
1000
+ ) : null}
1001
+ </div>
1002
+ );
1003
+ }
1004
+ };
1005
+ </script>
1006
+
1007
+ <style lang="less">
1008
+ .no-data {
1009
+ flex: 1;
1010
+ display: flex;
1011
+ flex-direction: column;
1012
+ justify-content: center;
1013
+ align-items: center;
1014
+ & > img {
1015
+ width: 160px;
1016
+ }
1017
+ &.fold > img {
1018
+ width: 80%;
1019
+ }
1020
+ span {
1021
+ display: inline-block;
1022
+ font-size: 14px;
1023
+ }
1024
+ &.fold span {
1025
+ font-size: 12px;
1026
+ }
1027
+ }
1028
+ .chat-scroll-container {
1029
+ height: 100%;
1030
+ overflow-y: auto;
1031
+ &::-webkit-scrollbar {
1032
+ width: 5px;
1033
+ }
1034
+ .infinite-wrap {
1035
+ padding: 16px;
1036
+ text-align: center;
1037
+ font-size: 14px;
1038
+ color: #000000;
1039
+ line-height: 20px;
1040
+ .ant-spin {
1041
+ margin-right: 8px;
1042
+ }
1043
+ }
1044
+ }
1045
+ .chat-scroll-session__list {
1046
+ margin-bottom: -1px;
1047
+ overflow: hidden;
1048
+ .scroll-item {
1049
+ position: relative;
1050
+ display: flex;
1051
+ align-items: center;
1052
+ height: 82px;
1053
+ padding: 15px 8px;
1054
+ background: #fff;
1055
+ border-bottom: 1px solid #d5d5d5;
1056
+ &:hover {
1057
+ background-color: #f4f4f4;
1058
+ }
1059
+ &.active {
1060
+ background: #ebebeb;
1061
+ .chat-scroll-session__name-time .chat-scroll-session__time {
1062
+ color: #666;
1063
+ }
1064
+ }
1065
+ .avatar {
1066
+ margin-right: 20px;
1067
+ }
1068
+ }
1069
+ }
1070
+
1071
+ .chat-scroll-container.fold .chat-scroll-session__list {
1072
+ .scroll-item {
1073
+ border-bottom: 0;
1074
+ justify-content: center;
1075
+ background-color: transparent;
1076
+ padding: 8px;
1077
+ height: auto;
1078
+ }
1079
+ .avatar {
1080
+ margin-right: 0;
1081
+ }
1082
+ }
1083
+ .chat-list-popover--fold {
1084
+ .ant-popover-content {
1085
+ max-width: 300px;
1086
+ }
1087
+ .ant-popover-arrow {
1088
+ display: none;
1089
+ }
1090
+ }
1091
+
1092
+ .chat-scroll-session__item {
1093
+ flex: 1;
1094
+ width: 0;
1095
+ &.fold {
1096
+ width: 192px;
1097
+ .chat-scroll-session__time {
1098
+ font-size: 12px;
1099
+ }
1100
+ }
1101
+ }
1102
+ .chat-scroll-session__name-time {
1103
+ display: flex;
1104
+ justify-content: space-between;
1105
+ align-items: center;
1106
+ margin-bottom: 4px;
1107
+ .chat-scroll-session__name {
1108
+ cursor: pointer;
1109
+ font-size: 16px;
1110
+ color: #212121;
1111
+ height: 26px;
1112
+ font-weight: 600;
1113
+ line-height: 26px;
1114
+ flex: 1;
1115
+ text-align: left;
1116
+ white-space: nowrap;
1117
+ text-overflow: ellipsis;
1118
+ overflow: hidden;
1119
+ word-break: break-all;
1120
+ text-align: left;
1121
+ }
1122
+ .chat-scroll-session__time {
1123
+ padding-left: 4px;
1124
+ cursor: pointer;
1125
+ color: #b3b3b3;
1126
+ text-align: right;
1127
+ white-space: nowrap;
1128
+ text-overflow: ellipsis;
1129
+ overflow: hidden;
1130
+ word-break: break-all;
1131
+ }
1132
+ .chat-scroll-session__name-zhuanjie {
1133
+ display: flex;
1134
+ justify-content: flex-start;
1135
+ align-items: center;
1136
+ max-width: 65%;
1137
+ }
1138
+ .chat-scroll-session__zhuanchu,
1139
+ .chat-scroll-session__zhuanjie {
1140
+ line-height: 16px;
1141
+ height: 18px;
1142
+ display: flex;
1143
+ align-items: center;
1144
+ margin-left: 8px;
1145
+ border: 1px solid #37d39f;
1146
+ color: #37d39f;
1147
+ border-radius: 2px;
1148
+ font-size: 12px;
1149
+ padding: 0 4px 0 2px;
1150
+ border-radius: 2px;
1151
+ }
1152
+ .chat-scroll-session__zhuanchu {
1153
+ color: #2d7aff;
1154
+ border: 1px solid #2d7aff;
1155
+ }
1156
+ }
1157
+ .chat-scroll-session__remark {
1158
+ width: 100%;
1159
+ text-overflow: ellipsis;
1160
+ overflow: hidden;
1161
+ white-space: nowrap;
1162
+ }
1163
+ .chat-scroll-session__brief-count {
1164
+ display: flex;
1165
+ justify-content: space-between;
1166
+ align-items: center;
1167
+ overflow: hidden;
1168
+ height: 20px;
1169
+ .chat-scroll-session__brief {
1170
+ flex: 1;
1171
+ width: 0;
1172
+ height: 20px;
1173
+ font-size: 14px;
1174
+ color: rgba(33, 33, 33, 0.6);
1175
+ line-height: 20px;
1176
+ text-align: left;
1177
+ white-space: nowrap;
1178
+ text-overflow: ellipsis;
1179
+ overflow: hidden;
1180
+ word-break: break-all;
1181
+ padding-right: 8px;
1182
+ > div {
1183
+ white-space: nowrap;
1184
+ text-overflow: ellipsis;
1185
+ overflow: hidden;
1186
+ word-break: break-all;
1187
+ }
1188
+ }
1189
+ .chat-scroll-session__level {
1190
+ flex: 0 0 60px;
1191
+ display: flex;
1192
+ white-space: nowrap;
1193
+ overflow: hidden;
1194
+ }
1195
+ }
1196
+ .chat-scroll-session__num,
1197
+ .chat-scroll-session__count {
1198
+ display: block;
1199
+ min-width: 28px;
1200
+ height: 28px;
1201
+ line-height: 28px;
1202
+ text-align: center;
1203
+ border-radius: 14px;
1204
+ background: #e54949;
1205
+ padding: 0 2px;
1206
+ color: #fff;
1207
+ font-size: 20px;
1208
+ transform: scale(0.5);
1209
+ }
1210
+ .chat-scroll-session__num {
1211
+ position: absolute;
1212
+ right: 0;
1213
+ top: 8px;
1214
+ margin-right: 12px;
1215
+ }
1216
+ .chat-scroll-session__table {
1217
+ .vxe-table--render-default .vxe-body--column.col--ellipsis,
1218
+ .vxe-table--render-default.vxe-editable .vxe-body--column,
1219
+ .vxe-table--render-default .vxe-footer--column.col--ellipsis,
1220
+ .vxe-table--render-default .vxe-header--column.col--ellipsis {
1221
+ height: 40px;
1222
+ }
1223
+ .vxe-table--render-default.border--default .vxe-table--header-wrapper,
1224
+ .vxe-table--render-default.border--full .vxe-table--header-wrapper,
1225
+ .vxe-table--render-default.border--outer .vxe-table--header-wrapper {
1226
+ background-color: #fff;
1227
+ }
1228
+ .vxe-body--row.active {
1229
+ background-color: #f2f2f2;
1230
+ }
1231
+ }
1232
+ </style>