huangyinfeng
6 天以前 a9a03d64cf190188d3db04d14970fc0908b03491
提交 | 用户 | age
00fe0e 1 <template>
63d608 2   <div style="overflow: auto">
H 3     <div v-if="groupedEmails.length != 0">
4       <div v-for="(item, index) in groupedEmails" :key="index">
5         <div class="span-title">{{ `${item.name}(${item.data.length})` }}</div>
6         <vxe-table
7           ref="vxeTableRef"
8           style="margin: 10px 0"
9           :showHeader="false"
10           :data="item.data"
11           size="small"
12           min-height="40px"
13           :row-config="{ isCurrent: true, isHover: true }"
14           :menu-config="tableMenu"
15           @menu-click="contextMenuClickEvent"
16           @cell-click="cellClickEvent"
17           @checkbox-change="selectChangeEvent"
18         >
19           <vxe-column type="checkbox" width="30"></vxe-column>
20           <vxe-column field="sender" title="发件人" data-index="sender" min-width="300px">
21             <template #default="{ row }">
22               <div style="display: flex; align-items: center">
23                 <div
24                   v-if="row.mailType != 0"
25                   class="dot"
a9a03d 26                   :class="fnIsItHighlighted(row) ? 'dot-color' : ''"
63d608 27                   @click.stop="fnRowUpdateRead(row)"
H 28                 ></div>
29                 <a-tooltip placement="bottom">
30                   <template #title>
31                     <span>陌生人</span>
32                   </template>
33                   <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" />
34                 </a-tooltip>
12f730 35
63d608 36                 <a-popover placement="bottom">
H 37                   <template #content>
38                     <div
39                       class="p-2"
40                       style="
41                         display: flex;
42                         align-items: center;
43                         border-bottom: 1px solid rgb(5 5 5 / 6%);
44                       "
45                     >
46                       <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" />
47                       <span style="color: #000; font-weight: 700">{{ row.sender }}</span>
a9a03d 48                       <CopyOutlined @click="copyText(row.sender)" />
63d608 49                     </div>
H 50                     <div class="display-flex p-2">
51                       <a-button type="link" size="small">新建客户</a-button>
52                       <a-dropdown>
53                         <a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent>
54                           <DownOutlined />
55                         </a>
56                         <template #overlay>
57                           <a-menu>
58                             <a-menu-item>
59                               <a href="javascript:;">添加到已有客户</a>
60                             </a-menu-item>
61                           </a-menu>
62                         </template>
63                       </a-dropdown>
64                       <a-button type="link" size="small">添加为线索</a-button>
65
66                       <a-dropdown style="margin-right: 5px">
67                         <a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent>
68                           <DownOutlined />
69                         </a>
70                         <template #overlay>
71                           <a-menu>
72                             <a-menu-item>
73                               <a href="javascript:;">添加到通讯录</a>
74                             </a-menu-item>
75                           </a-menu>
76                         </template>
77                       </a-dropdown>
78                       <a-button type="link" size="small">往来邮件</a-button></div
79                     >
80                   </template>
a9a03d 81                   <div class="title-dot" :class="fnIsItHighlighted(row) ? 'title-dot-color' : ''">
63d608 82                     <span style="font-weight: 700">{{ row.senderName }}</span
H 83                     ><span style="padding: 0 8px">|</span>
84                     <span style="font-weight: 500">{{ row.sender }}</span>
00fe0e 85                   </div>
63d608 86                 </a-popover>
H 87               </div>
88             </template>
89           </vxe-column>
90           <vxe-column
91             show-overflow
92             field="subject"
93             title="表题"
94             data-index="subject"
95             min-width="250"
96           >
97             <template #default="{ row }">
98               <span
99                 class="title-dot"
a9a03d 100                 :class="fnIsItHighlighted(row) ? 'title-dot-color' : ''"
63d608 101                 style="font-weight: 500"
H 102                 >{{ row.subject || '(无主题)' }}</span
103               >
104               -
105               <span style="color: #999">{{ row.subject }}</span>
106             </template>
107           </vxe-column>
108           <vxe-column field="action" title="Action" width="190">
109             <template #default="{ row, rowIndex }">
110               <span style="display: flex; justify-content: space-around">
111                 <span>{{
112                   row.mailType !== 0
113                     ? formatToDateDay(row.receiveTime)
114                     : formatToDateDay(row.createTime)
115                 }}</span>
00fe0e 116
63d608 117                 <TooltipAndDropdown
H 118                   :tooltipTitle="'待处理邮件'"
119                   :initialDropdownOpen="false"
120                   :initialTooltipOpen="false"
121                   :showTooltip="!!row.handleTime"
122                   :row="row"
123                   :docCodeS="[row.docCode]"
124                 />
125                 <span style="margin-left: 5px"><PushpinOutlined @click.stop="fnTagging" /></span>
126               </span>
127             </template>
128           </vxe-column>
a9a03d 129         </vxe-table> </div
63d608 130     ></div>
74a35f 131
a9a03d 132     <div v-else style="display: flex; align-items: center; justify-content: center; height: 70vh">
63d608 133       <a-empty />
00fe0e 134     </div>
ccfd07 135     <DrawerDetail
H 136       ref="drawerDetailRef"
137       v-model="openDrawerDetail"
138       :mailId="rowMailId"
74a35f 139       :selectAllRow="selectRow"
ccfd07 140       :allList="dataSource"
a9a03d 141       :isDrafts="isDrafts"
ccfd07 142     />
74a35f 143     <a-dropdown :trigger="['click']" placement="bottomLeft" ref="dropdownRefs"> </a-dropdown>
00fe0e 144   </div>
H 145 </template>
146
147 <script lang="ts" setup>
148   name: 'ListPageTable';
149   import {
150     FieldTimeOutlined,
151     PushpinOutlined,
152     CopyOutlined,
153     DownOutlined,
154   } from '@ant-design/icons-vue';
155
12f730 156   import { ref, watch, defineProps, defineEmits, computed, defineExpose, inject } from 'vue';
00fe0e 157
H 158   // 定义属性
159   interface Props {
12f730 160     pageList: [];
a9a03d 161     isDrafts?: boolean;
00fe0e 162   }
12f730 163   const props = defineProps<Props>();
00fe0e 164
12f730 165   const groupedEmails = ref<GroupedDataItem[]>([]);
00fe0e 166
12f730 167   const dataSource = ref([]);
H 168   watch(
169     () => props.pageList,
170     (newValue) => {
63d608 171       dataSource.value = newValue || [];
H 172       groupedEmails.value = groupEmailsByDate(newValue || []);
12f730 173     },
H 174   );
00fe0e 175   import dayjs from 'dayjs';
H 176   import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
177   import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
178   import isoWeek from 'dayjs/plugin/isoWeek';
179   dayjs.extend(isSameOrAfter);
180   dayjs.extend(isSameOrBefore);
181   dayjs.extend(isoWeek);
182
183   interface EmailItem {
184     id: number;
185     subject: string;
186   }
187
188   // 确保 groupedData 的结构正确且 data 是 EmailItem 类型的数组
189   interface GroupedDataItem {
190     key: string;
191     data: EmailItem[];
192     name: string;
193   }
194
195   function groupEmailsByDate(dataSource) {
196     const today = dayjs();
197     const yesterday = dayjs().subtract(1, 'day');
198     const startOfWeek = dayjs().startOf('week');
199     const startOfMonth = dayjs().startOf('month');
200     const startOfLastMonth = dayjs().subtract(1, 'month').startOf('month');
201     const endOfLastMonth = dayjs().subtract(1, 'month').endOf('month');
202
203     const groupedData: GroupedDataItem[] = [
204       {
205         data: [],
206         name: '今天',
207         key: 'today',
208       },
209       {
210         data: [],
211         name: '昨天',
212         key: 'yesterday',
213       },
214       {
215         data: [],
216         name: '本周',
217         key: 'thisWeek',
218       },
219       {
220         data: [],
221         name: '这个月',
222         key: 'thisMonth',
223       },
224       {
225         data: [],
226         name: '上个月',
227         key: 'lastMonth',
228       },
229       {
230         data: [],
231         name: '更早',
232         key: 'earlier',
233       },
234     ];
235
236     dataSource.forEach((item: any) => {
237       try {
74a35f 238         const emailDate = dayjs(item.createTime);
00fe0e 239         if (emailDate.isSame(today, 'day')) {
H 240           groupedData[0].data.push(item);
241         } else if (emailDate.isSame(yesterday, 'day')) {
242           groupedData[1].data.push(item);
243         } else if (emailDate.isSameOrAfter(startOfWeek) && emailDate.isBefore(today, 'day')) {
244           groupedData[2].data.push(item);
245         } else if (emailDate.isSameOrAfter(startOfMonth) && emailDate.isBefore(today, 'day')) {
246           groupedData[3].data.push(item);
247         } else if (
248           emailDate.isSameOrAfter(startOfLastMonth) &&
249           emailDate.isSameOrBefore(endOfLastMonth)
250         ) {
251           groupedData[4].data.push(item);
252         } else {
253           groupedData[5].data.push(item);
254         }
255       } catch (error) {
256         console.error(`Error processing item date: ${item.date}`, error);
257       }
258     });
259     // 将结果按中文映射进行返回
260     const result = <{ data: any; name: string; key: string }[]>[];
261     groupedData.forEach((group: { data: any; name: string; key: string }) => {
262       if (group.data.length > 0) {
263         result.push(group);
264       }
265     });
266
267     return result;
268   }
269
270   // 右键菜单
271   const tableMenu = {
272     className: 'my-menus',
273     body: {
274       options: [
275         [
276           {
277             code: 'reply',
278             name: '回复',
279           },
280           { code: 'replyAll', name: '回复全部' },
281           { code: 'replyWithAttachment', name: '带附件回复' },
282           { code: 'replyAllWithAttachment', name: '带附件回复全部' },
283           { code: 'forward', name: '转发' },
284           { code: 'forwardAsAttachment', name: '作为附件转发' },
285           { code: 'distribute', name: '分发' },
286           { code: 'setRemark', name: '设置备注' },
287         ],
288         [
289           { code: 'toHandle', name: '待处理' },
290           { code: 'markAsUnread', name: '标为未读' },
291           { code: 'labelAs', name: '标注为' },
292         ],
293         [
294           { code: 'newRule', name: '新建收发件规则' },
295           { code: 'moveTo', name: '移动到' },
296         ],
297         [
298           { code: 'exportEmail', name: '导出邮件' },
299           { code: 'createFollowUp', name: '建为客户跟进' },
300           { code: 'createSchedule', name: '新建日程' },
301         ],
302         [
303           { code: 'markAsSpam', name: '标为垃圾邮件' },
304           { code: 'delete', name: '删除' },
305         ],
306       ],
307     },
308   };
309
310   function contextMenuClickEvent({ menu, row, column }) {
311     switch (menu.code) {
312       case 'copy':
313         if (row && column) {
314         }
315         break;
316       default:
317     }
318   }
67287b 319   const vxeTableRef = ref();
a9a03d 320   function fnIsItHighlighted(row) {
H 321     return row.readFlag && props.isDrafts;
322   }
67287b 323   function fnSelectAll(is) {
a9a03d 324     try {
H 325       if (!vxeTableRef.value) {
326         return;
327       }
328       vxeTableRef.value.forEach((row) => {
329         row.setAllCheckboxRow(is);
330       });
331       selectChangeEvent();
332     } catch (error) {
333       console.log(error);
334     }
67287b 335   }
00fe0e 336
12f730 337   function selectChangeEvent() {
H 338     const isAll = getCheckboxRecords().length === dataSource.value.length;
339     const data = {
340       isAll,
341       records: getCheckboxRecords(),
342     };
343     emit('updateSelectAll', data);
67287b 344   }
H 345   function getCheckboxRecords() {
346     const list = new Set();
347
348     vxeTableRef.value.forEach((row) => {
349       const records = row.getCheckboxRecords(); // 假设该方法返回一个数组
350       if (Array.isArray(records)) {
351         // 确保 records 是数组
352         records.forEach((record) => list.add(record)); // 将记录添加到 Set 中
353       }
354     });
355
356     return Array.from(list); // 将 Set 转换回数组
357   }
00fe0e 358   // 操作row
H 359   function fnProcessingTime(row) {
360     console.log(row);
361   }
362   function fnTagging(row) {
363     console.log(row);
364   }
365   import DrawerDetail from './drawerDetail.vue';
366   // 详情内容
367   const openDrawerDetail = ref(false);
ccfd07 368   const rowMailId = ref('');
74a35f 369   const selectRow = ref([]);
00fe0e 370   const cellClickEvent = (event) => {
74a35f 371     selectRow.value = [];
ccfd07 372     rowMailId.value = event.row.docCode;
74a35f 373     selectRow.value.push({ docCode: event.row.docCode });
a9a03d 374     fnRowUpdateRead(event.row);
00fe0e 375     openDrawerDetail.value = true;
H 376   };
67287b 377
12f730 378   // 更新祖父组件数据
H 379   const getDataList = inject('getDataList');
380
74a35f 381   import { updateReadApi, updateHandleAPi } from '@/api/email/userList';
12f730 382   // 标志未读/经读
H 383   function fnRowUpdateRead(row) {
384     const data = {
385       status: !row.readFlag,
386       list: [row.docCode],
387     };
388     pushReadApi(data);
389   }
390   function pushReadApi(params) {
391     updateReadApi(params).then((res) => {
392       if (res.code == 0) {
393         //
ccfd07 394         getDataList({});
12f730 395       }
H 396     });
397   }
a9a03d 398   import { useMessage } from '@/hooks/web/useMessage';
H 399
400   const { createMessage } = useMessage();
401   const copyText = async (value) => {
402     try {
403       await navigator.clipboard.writeText(value);
404       setTimeout(() => {
405         createMessage.success('复制成功');
406       }, 500);
407     } catch (err) {
408       console.error('复制失败: ', err);
409     }
410   };
12f730 411   const emit = defineEmits(['selectAll', 'updateSelectAll']);
67287b 412   defineExpose({
H 413     fnSelectAll,
414   });
74a35f 415
H 416   import TooltipAndDropdown from './TooltipAndDropdown .vue';
417   import { formatToDateDay } from '@/utils/dateUtil';
00fe0e 418 </script>
H 419 <style scoped lang="less">
420   .display-flex {
421     display: flex;
422     align-items: center;
423     justify-content: space-between;
424   }
425
426   .head {
427     display: flex;
428     justify-content: space-between;
429     width: 100%;
430     border-bottom: 1px solid rgb(5 5 5 / 6%);
431
432     /* 增加选择器特异性 */
433     & .left {
434       width: 20%;
435
436       & .left-box {
437         display: flex;
438         align-items: center;
439         justify-content: space-flex-start;
440         width: 100%;
441
442         & .icon {
443           margin-right: 15px;
444           font-size: 16px;
445         }
446       }
447     }
448
449     & .right {
450       display: flex;
451       align-items: center;
452     }
453   }
454
455   .left-bt {
456     display: flex;
457     align-items: center;
458     justify-content: center;
459     padding-left: 27px;
460     background: #fffbe6;
461   }
462
463   .span-title {
63d608 464     width: 100%;
H 465     padding: 5px;
00fe0e 466     color: #000;
H 467     font-weight: 700;
63d608 468     text-align: left;
00fe0e 469   }
H 470
471   .table {
472     height: 80vh;
473   }
474
475   .my-menus {
476     background-color: #f8f8f9;
477   }
12f730 478   // 圆点
H 479   .dot {
480     display: inline-block;
481     width: 8px;
482     height: 8px;
483     margin-right: 10px;
484     border-radius: 50%;
485     background-color: #0a6aff;
486   }
487
488   .dot-color {
489     background-color: #d9d9d9;
490     color: #d9d9d9;
491   }
492
493   .title-dot {
494     color: #0a6aff;
495   }
496
497   .title-dot-color {
498     color: #999;
499   }
00fe0e 500 </style>