huangyinfeng
4 天以前 db42d08c39ae6129e2b95cd24c0d57c6769282e5
提交 | 用户 | 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 }"
db42d0 14           :menu-config="{ enabled: true }"
63d608 15           @cell-click="cellClickEvent"
H 16           @checkbox-change="selectChangeEvent"
db42d0 17           @cell-menu="onCellContextMenu"
63d608 18         >
H 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>
db42d0 81                   <div :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>
db42d0 90           <vxe-column
H 91             show-overflow
92             field="icon"
93             title="表题"
94             data-index="icon"
95             width="100"
96             align="right"
97           >
98             <template #default="{ row }">
99               <a-tooltip placement="bottom">
100                 <template #title>
101                   <span>有附件</span>
102                 </template>
103                 <span v-show="row.attachmentList?.length > 0">
104                   <PaperClipOutlined />
105                 </span>
106               </a-tooltip>
107             </template>
108           </vxe-column>
109
63d608 110           <vxe-column
H 111             show-overflow
112             field="subject"
113             title="表题"
114             data-index="subject"
115             min-width="250"
116           >
117             <template #default="{ row }">
118               <span
119                 class="title-dot"
a9a03d 120                 :class="fnIsItHighlighted(row) ? 'title-dot-color' : ''"
63d608 121                 style="font-weight: 500"
H 122                 >{{ row.subject || '(无主题)' }}</span
123               >
124               -
125               <span style="color: #999">{{ row.subject }}</span>
126             </template>
127           </vxe-column>
128           <vxe-column field="action" title="Action" width="190">
129             <template #default="{ row, rowIndex }">
130               <span style="display: flex; justify-content: space-around">
131                 <span>{{
132                   row.mailType !== 0
db42d0 133                     ? formatToDateDay(row.receiveTime || row.createTime)
63d608 134                     : formatToDateDay(row.createTime)
H 135                 }}</span>
00fe0e 136
63d608 137                 <TooltipAndDropdown
H 138                   :tooltipTitle="'待处理邮件'"
139                   :initialDropdownOpen="false"
140                   :initialTooltipOpen="false"
141                   :showTooltip="!!row.handleTime"
142                   :row="row"
143                   :docCodeS="[row.docCode]"
144                 />
145                 <span style="margin-left: 5px"><PushpinOutlined @click.stop="fnTagging" /></span>
146               </span>
147             </template>
148           </vxe-column>
db42d0 149         </vxe-table>
H 150         <ContextMenu
151           v-if="showMenu"
152           :style="menuStyle"
153           :selected-cell="selectedCell"
154           @close-menu="showMenu = false"
155         /> </div
63d608 156     ></div>
74a35f 157
a9a03d 158     <div v-else style="display: flex; align-items: center; justify-content: center; height: 70vh">
63d608 159       <a-empty />
00fe0e 160     </div>
ccfd07 161     <DrawerDetail
H 162       ref="drawerDetailRef"
163       v-model="openDrawerDetail"
164       :mailId="rowMailId"
74a35f 165       :selectAllRow="selectRow"
ccfd07 166       :allList="dataSource"
a9a03d 167       :isDrafts="isDrafts"
ccfd07 168     />
74a35f 169     <a-dropdown :trigger="['click']" placement="bottomLeft" ref="dropdownRefs"> </a-dropdown>
00fe0e 170   </div>
H 171 </template>
172
173 <script lang="ts" setup>
174   name: 'ListPageTable';
175   import {
176     FieldTimeOutlined,
177     PushpinOutlined,
178     CopyOutlined,
179     DownOutlined,
db42d0 180     PaperClipOutlined,
00fe0e 181   } from '@ant-design/icons-vue';
H 182
12f730 183   import { ref, watch, defineProps, defineEmits, computed, defineExpose, inject } from 'vue';
00fe0e 184
H 185   // 定义属性
186   interface Props {
12f730 187     pageList: [];
a9a03d 188     isDrafts?: boolean;
00fe0e 189   }
12f730 190   const props = defineProps<Props>();
00fe0e 191
12f730 192   const groupedEmails = ref<GroupedDataItem[]>([]);
00fe0e 193
12f730 194   const dataSource = ref([]);
H 195   watch(
196     () => props.pageList,
197     (newValue) => {
63d608 198       dataSource.value = newValue || [];
H 199       groupedEmails.value = groupEmailsByDate(newValue || []);
12f730 200     },
H 201   );
00fe0e 202   import dayjs from 'dayjs';
H 203   import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
204   import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
205   import isoWeek from 'dayjs/plugin/isoWeek';
206   dayjs.extend(isSameOrAfter);
207   dayjs.extend(isSameOrBefore);
208   dayjs.extend(isoWeek);
209
210   interface EmailItem {
211     id: number;
212     subject: string;
213   }
214
215   // 确保 groupedData 的结构正确且 data 是 EmailItem 类型的数组
216   interface GroupedDataItem {
217     key: string;
218     data: EmailItem[];
219     name: string;
220   }
221
222   function groupEmailsByDate(dataSource) {
223     const today = dayjs();
224     const yesterday = dayjs().subtract(1, 'day');
225     const startOfWeek = dayjs().startOf('week');
226     const startOfMonth = dayjs().startOf('month');
227     const startOfLastMonth = dayjs().subtract(1, 'month').startOf('month');
228     const endOfLastMonth = dayjs().subtract(1, 'month').endOf('month');
229
230     const groupedData: GroupedDataItem[] = [
231       {
232         data: [],
233         name: '今天',
234         key: 'today',
235       },
236       {
237         data: [],
238         name: '昨天',
239         key: 'yesterday',
240       },
241       {
242         data: [],
243         name: '本周',
244         key: 'thisWeek',
245       },
246       {
247         data: [],
248         name: '这个月',
249         key: 'thisMonth',
250       },
251       {
252         data: [],
253         name: '上个月',
254         key: 'lastMonth',
255       },
256       {
257         data: [],
258         name: '更早',
259         key: 'earlier',
260       },
261     ];
262
263     dataSource.forEach((item: any) => {
264       try {
74a35f 265         const emailDate = dayjs(item.createTime);
00fe0e 266         if (emailDate.isSame(today, 'day')) {
H 267           groupedData[0].data.push(item);
268         } else if (emailDate.isSame(yesterday, 'day')) {
269           groupedData[1].data.push(item);
270         } else if (emailDate.isSameOrAfter(startOfWeek) && emailDate.isBefore(today, 'day')) {
271           groupedData[2].data.push(item);
272         } else if (emailDate.isSameOrAfter(startOfMonth) && emailDate.isBefore(today, 'day')) {
273           groupedData[3].data.push(item);
274         } else if (
275           emailDate.isSameOrAfter(startOfLastMonth) &&
276           emailDate.isSameOrBefore(endOfLastMonth)
277         ) {
278           groupedData[4].data.push(item);
279         } else {
280           groupedData[5].data.push(item);
281         }
282       } catch (error) {
283         console.error(`Error processing item date: ${item.date}`, error);
284       }
285     });
286     // 将结果按中文映射进行返回
287     const result = <{ data: any; name: string; key: string }[]>[];
288     groupedData.forEach((group: { data: any; name: string; key: string }) => {
289       if (group.data.length > 0) {
290         result.push(group);
291       }
292     });
293
294     return result;
295   }
296
297   // 右键菜单
db42d0 298   import ContextMenu from '@/views/email/components/ContextMenu/index.vue';
H 299   const showMenu = ref(false);
300   const menuStyle = ref({});
301   const selectedCell = ref(null);
302   const onCellContextMenu = ({
303     type,
304     row,
305     rowIndex,
306     $rowIndex,
307     column,
308     columnIndex,
309     $columnIndex,
310     $event,
311   }) => {
312     $event.preventDefault(); // 阻止默认的浏览器右键菜单?
313     selectedCell.value = { row, column, columnIndex }; // 保存当前选中的单元格数据
314
315     // 计算菜单初始位置
316     let menuX = $event.clientX;
317     let menuY = $event.clientY;
318
319     // 获取菜单的宽度和高度
320     const menuWidth = 200; // 假设菜单宽度为200px,可以根据实际情况调整
321     const menuHeight = 800; // 假设菜单高度为150px,可以根据实际情况调整
322     // 获取窗口宽度和高度
323     const windowWidth = window.innerWidth;
324     const windowHeight = window.innerHeight;
325
326     // 检查菜单是否超出窗口的右边界,如果是,则向左调整
327     if (menuX + menuWidth > windowWidth) {
328       menuX = windowWidth - menuWidth;
329     }
330
331     // 检查菜单是否超出窗口的下边界,如果是,则向上调整
332     if (menuY + menuHeight > windowHeight) {
333       menuY = windowHeight - menuHeight;
334     }
335     menuStyle.value = {
336       position: 'fixed',
337       top: `${menuY}px`,
338       left: `${menuX}px`,
339     };
340     showMenu.value = true;
00fe0e 341   };
H 342
db42d0 343   // 监听全局点击事件,点击页面其他地方关闭菜单
H 344   document.addEventListener('click', () => {
345     showMenu.value = false;
346   });
347
67287b 348   const vxeTableRef = ref();
a9a03d 349   function fnIsItHighlighted(row) {
db42d0 350     if (props.isDrafts) {
H 351       return row.readFlag && props.isDrafts;
352     } else {
353       return row.readFlag;
354     }
a9a03d 355   }
67287b 356   function fnSelectAll(is) {
a9a03d 357     try {
H 358       if (!vxeTableRef.value) {
359         return;
360       }
361       vxeTableRef.value.forEach((row) => {
362         row.setAllCheckboxRow(is);
363       });
364       selectChangeEvent();
365     } catch (error) {
366       console.log(error);
367     }
67287b 368   }
00fe0e 369
12f730 370   function selectChangeEvent() {
H 371     const isAll = getCheckboxRecords().length === dataSource.value.length;
372     const data = {
373       isAll,
374       records: getCheckboxRecords(),
375     };
376     emit('updateSelectAll', data);
67287b 377   }
H 378   function getCheckboxRecords() {
379     const list = new Set();
380
381     vxeTableRef.value.forEach((row) => {
382       const records = row.getCheckboxRecords(); // 假设该方法返回一个数组
383       if (Array.isArray(records)) {
384         // 确保 records 是数组
385         records.forEach((record) => list.add(record)); // 将记录添加到 Set 中
386       }
387     });
388
389     return Array.from(list); // 将 Set 转换回数组
390   }
00fe0e 391   // 操作row
H 392   function fnProcessingTime(row) {
393     console.log(row);
394   }
395   function fnTagging(row) {
396     console.log(row);
397   }
398   import DrawerDetail from './drawerDetail.vue';
399   // 详情内容
400   const openDrawerDetail = ref(false);
ccfd07 401   const rowMailId = ref('');
74a35f 402   const selectRow = ref([]);
00fe0e 403   const cellClickEvent = (event) => {
74a35f 404     selectRow.value = [];
ccfd07 405     rowMailId.value = event.row.docCode;
74a35f 406     selectRow.value.push({ docCode: event.row.docCode });
a9a03d 407     fnRowUpdateRead(event.row);
00fe0e 408     openDrawerDetail.value = true;
H 409   };
67287b 410
12f730 411   // 更新祖父组件数据
H 412   const getDataList = inject('getDataList');
413
74a35f 414   import { updateReadApi, updateHandleAPi } from '@/api/email/userList';
12f730 415   // 标志未读/经读
H 416   function fnRowUpdateRead(row) {
417     const data = {
418       status: !row.readFlag,
419       list: [row.docCode],
420     };
421     pushReadApi(data);
422   }
423   function pushReadApi(params) {
424     updateReadApi(params).then((res) => {
425       if (res.code == 0) {
426         //
ccfd07 427         getDataList({});
12f730 428       }
H 429     });
430   }
a9a03d 431   import { useMessage } from '@/hooks/web/useMessage';
H 432
433   const { createMessage } = useMessage();
434   const copyText = async (value) => {
435     try {
436       await navigator.clipboard.writeText(value);
437       setTimeout(() => {
438         createMessage.success('复制成功');
439       }, 500);
440     } catch (err) {
441       console.error('复制失败: ', err);
442     }
443   };
12f730 444   const emit = defineEmits(['selectAll', 'updateSelectAll']);
67287b 445   defineExpose({
H 446     fnSelectAll,
447   });
74a35f 448
H 449   import TooltipAndDropdown from './TooltipAndDropdown .vue';
450   import { formatToDateDay } from '@/utils/dateUtil';
00fe0e 451 </script>
H 452 <style scoped lang="less">
453   .display-flex {
454     display: flex;
455     align-items: center;
456     justify-content: space-between;
457   }
458
459   .head {
460     display: flex;
461     justify-content: space-between;
462     width: 100%;
463     border-bottom: 1px solid rgb(5 5 5 / 6%);
464
465     /* 增加选择器特异性 */
466     & .left {
467       width: 20%;
468
469       & .left-box {
470         display: flex;
471         align-items: center;
472         justify-content: space-flex-start;
473         width: 100%;
474
475         & .icon {
476           margin-right: 15px;
477           font-size: 16px;
478         }
479       }
480     }
481
482     & .right {
483       display: flex;
484       align-items: center;
485     }
486   }
487
488   .left-bt {
489     display: flex;
490     align-items: center;
491     justify-content: center;
492     padding-left: 27px;
493     background: #fffbe6;
494   }
495
496   .span-title {
63d608 497     width: 100%;
db42d0 498     height: 30px;
63d608 499     padding: 5px;
00fe0e 500     color: #000;
H 501     font-weight: 700;
db42d0 502     line-height: 30px;
63d608 503     text-align: left;
00fe0e 504   }
H 505
506   .table {
507     height: 80vh;
508   }
509
510   .my-menus {
511     background-color: #f8f8f9;
512   }
12f730 513   // 圆点
H 514   .dot {
515     display: inline-block;
516     width: 8px;
517     height: 8px;
518     margin-right: 10px;
519     border-radius: 50%;
520     background-color: #0a6aff;
521   }
522
523   .dot-color {
524     background-color: #d9d9d9;
525     color: #d9d9d9;
526   }
527
528   .title-dot {
529     color: #0a6aff;
530   }
531
532   .title-dot-color {
533     color: #999;
534   }
00fe0e 535 </style>