提交 | 用户 | 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" |
|
26 |
:class="row.readFlag ? 'dot-color' : ''" |
|
27 |
@click.stop="fnRowUpdateRead(row)" |
|
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> |
|
48 |
<CopyOutlined /> |
|
49 |
</div> |
|
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> |
|
81 |
<div class="title-dot" :class="row.readFlag ? 'title-dot-color' : ''"> |
|
82 |
<span style="font-weight: 700">{{ row.senderName }}</span |
|
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" |
|
100 |
:class="row.readFlag ? 'title-dot-color' : ''" |
|
101 |
style="font-weight: 500" |
|
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> |
|
129 |
</vxe-table> |
|
130 |
</div |
|
131 |
></div> |
74a35f
|
132 |
|
63d608
|
133 |
<div v-else style="height: 70vh; display: flex; align-items: center; justify-content: center"> |
H |
134 |
<a-empty /> |
00fe0e
|
135 |
</div> |
ccfd07
|
136 |
<DrawerDetail |
H |
137 |
ref="drawerDetailRef" |
|
138 |
v-model="openDrawerDetail" |
|
139 |
:mailId="rowMailId" |
74a35f
|
140 |
:selectAllRow="selectRow" |
ccfd07
|
141 |
:allList="dataSource" |
H |
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: []; |
00fe0e
|
161 |
} |
12f730
|
162 |
const props = defineProps<Props>(); |
00fe0e
|
163 |
|
12f730
|
164 |
const groupedEmails = ref<GroupedDataItem[]>([]); |
00fe0e
|
165 |
|
12f730
|
166 |
const dataSource = ref([]); |
H |
167 |
watch( |
|
168 |
() => props.pageList, |
|
169 |
(newValue) => { |
63d608
|
170 |
dataSource.value = newValue || []; |
H |
171 |
groupedEmails.value = groupEmailsByDate(newValue || []); |
12f730
|
172 |
}, |
H |
173 |
); |
00fe0e
|
174 |
import dayjs from 'dayjs'; |
H |
175 |
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; |
|
176 |
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; |
|
177 |
import isoWeek from 'dayjs/plugin/isoWeek'; |
|
178 |
dayjs.extend(isSameOrAfter); |
|
179 |
dayjs.extend(isSameOrBefore); |
|
180 |
dayjs.extend(isoWeek); |
|
181 |
|
|
182 |
interface EmailItem { |
|
183 |
id: number; |
|
184 |
subject: string; |
|
185 |
} |
|
186 |
|
|
187 |
// 确保 groupedData 的结构正确且 data 是 EmailItem 类型的数组 |
|
188 |
interface GroupedDataItem { |
|
189 |
key: string; |
|
190 |
data: EmailItem[]; |
|
191 |
name: string; |
|
192 |
} |
|
193 |
|
|
194 |
function groupEmailsByDate(dataSource) { |
|
195 |
const today = dayjs(); |
|
196 |
const yesterday = dayjs().subtract(1, 'day'); |
|
197 |
const startOfWeek = dayjs().startOf('week'); |
|
198 |
const startOfMonth = dayjs().startOf('month'); |
|
199 |
const startOfLastMonth = dayjs().subtract(1, 'month').startOf('month'); |
|
200 |
const endOfLastMonth = dayjs().subtract(1, 'month').endOf('month'); |
|
201 |
|
|
202 |
const groupedData: GroupedDataItem[] = [ |
|
203 |
{ |
|
204 |
data: [], |
|
205 |
name: '今天', |
|
206 |
key: 'today', |
|
207 |
}, |
|
208 |
{ |
|
209 |
data: [], |
|
210 |
name: '昨天', |
|
211 |
key: 'yesterday', |
|
212 |
}, |
|
213 |
{ |
|
214 |
data: [], |
|
215 |
name: '本周', |
|
216 |
key: 'thisWeek', |
|
217 |
}, |
|
218 |
{ |
|
219 |
data: [], |
|
220 |
name: '这个月', |
|
221 |
key: 'thisMonth', |
|
222 |
}, |
|
223 |
{ |
|
224 |
data: [], |
|
225 |
name: '上个月', |
|
226 |
key: 'lastMonth', |
|
227 |
}, |
|
228 |
{ |
|
229 |
data: [], |
|
230 |
name: '更早', |
|
231 |
key: 'earlier', |
|
232 |
}, |
|
233 |
]; |
|
234 |
|
|
235 |
dataSource.forEach((item: any) => { |
|
236 |
try { |
74a35f
|
237 |
const emailDate = dayjs(item.createTime); |
00fe0e
|
238 |
if (emailDate.isSame(today, 'day')) { |
H |
239 |
groupedData[0].data.push(item); |
|
240 |
} else if (emailDate.isSame(yesterday, 'day')) { |
|
241 |
groupedData[1].data.push(item); |
|
242 |
} else if (emailDate.isSameOrAfter(startOfWeek) && emailDate.isBefore(today, 'day')) { |
|
243 |
groupedData[2].data.push(item); |
|
244 |
} else if (emailDate.isSameOrAfter(startOfMonth) && emailDate.isBefore(today, 'day')) { |
|
245 |
groupedData[3].data.push(item); |
|
246 |
} else if ( |
|
247 |
emailDate.isSameOrAfter(startOfLastMonth) && |
|
248 |
emailDate.isSameOrBefore(endOfLastMonth) |
|
249 |
) { |
|
250 |
groupedData[4].data.push(item); |
|
251 |
} else { |
|
252 |
groupedData[5].data.push(item); |
|
253 |
} |
|
254 |
} catch (error) { |
|
255 |
console.error(`Error processing item date: ${item.date}`, error); |
|
256 |
} |
|
257 |
}); |
|
258 |
// 将结果按中文映射进行返回 |
|
259 |
const result = <{ data: any; name: string; key: string }[]>[]; |
|
260 |
groupedData.forEach((group: { data: any; name: string; key: string }) => { |
|
261 |
if (group.data.length > 0) { |
|
262 |
result.push(group); |
|
263 |
} |
|
264 |
}); |
|
265 |
|
|
266 |
return result; |
|
267 |
} |
|
268 |
|
|
269 |
// 右键菜单 |
|
270 |
const tableMenu = { |
|
271 |
className: 'my-menus', |
|
272 |
body: { |
|
273 |
options: [ |
|
274 |
[ |
|
275 |
{ |
|
276 |
code: 'reply', |
|
277 |
name: '回复', |
|
278 |
}, |
|
279 |
{ code: 'replyAll', name: '回复全部' }, |
|
280 |
{ code: 'replyWithAttachment', name: '带附件回复' }, |
|
281 |
{ code: 'replyAllWithAttachment', name: '带附件回复全部' }, |
|
282 |
{ code: 'forward', name: '转发' }, |
|
283 |
{ code: 'forwardAsAttachment', name: '作为附件转发' }, |
|
284 |
{ code: 'distribute', name: '分发' }, |
|
285 |
{ code: 'setRemark', name: '设置备注' }, |
|
286 |
], |
|
287 |
[ |
|
288 |
{ code: 'toHandle', name: '待处理' }, |
|
289 |
{ code: 'markAsUnread', name: '标为未读' }, |
|
290 |
{ code: 'labelAs', name: '标注为' }, |
|
291 |
], |
|
292 |
[ |
|
293 |
{ code: 'newRule', name: '新建收发件规则' }, |
|
294 |
{ code: 'moveTo', name: '移动到' }, |
|
295 |
], |
|
296 |
[ |
|
297 |
{ code: 'exportEmail', name: '导出邮件' }, |
|
298 |
{ code: 'createFollowUp', name: '建为客户跟进' }, |
|
299 |
{ code: 'createSchedule', name: '新建日程' }, |
|
300 |
], |
|
301 |
[ |
|
302 |
{ code: 'markAsSpam', name: '标为垃圾邮件' }, |
|
303 |
{ code: 'delete', name: '删除' }, |
|
304 |
], |
|
305 |
], |
|
306 |
}, |
|
307 |
}; |
|
308 |
|
|
309 |
function contextMenuClickEvent({ menu, row, column }) { |
|
310 |
switch (menu.code) { |
|
311 |
case 'copy': |
|
312 |
if (row && column) { |
|
313 |
} |
|
314 |
break; |
|
315 |
default: |
|
316 |
} |
|
317 |
} |
67287b
|
318 |
const vxeTableRef = ref(); |
H |
319 |
function fnSelectAll(is) { |
|
320 |
vxeTableRef.value.forEach((row) => { |
|
321 |
row.setAllCheckboxRow(is); |
|
322 |
}); |
ccfd07
|
323 |
selectChangeEvent(); |
67287b
|
324 |
} |
00fe0e
|
325 |
|
12f730
|
326 |
function selectChangeEvent() { |
H |
327 |
const isAll = getCheckboxRecords().length === dataSource.value.length; |
|
328 |
const data = { |
|
329 |
isAll, |
|
330 |
records: getCheckboxRecords(), |
|
331 |
}; |
|
332 |
emit('updateSelectAll', data); |
67287b
|
333 |
} |
H |
334 |
function getCheckboxRecords() { |
|
335 |
const list = new Set(); |
|
336 |
|
|
337 |
vxeTableRef.value.forEach((row) => { |
|
338 |
const records = row.getCheckboxRecords(); // 假设该方法返回一个数组 |
|
339 |
if (Array.isArray(records)) { |
|
340 |
// 确保 records 是数组 |
|
341 |
records.forEach((record) => list.add(record)); // 将记录添加到 Set 中 |
|
342 |
} |
|
343 |
}); |
|
344 |
|
|
345 |
return Array.from(list); // 将 Set 转换回数组 |
|
346 |
} |
00fe0e
|
347 |
// 操作row |
H |
348 |
function fnProcessingTime(row) { |
|
349 |
console.log(row); |
|
350 |
} |
|
351 |
function fnTagging(row) { |
|
352 |
console.log(row); |
|
353 |
} |
|
354 |
import DrawerDetail from './drawerDetail.vue'; |
|
355 |
// 详情内容 |
|
356 |
const openDrawerDetail = ref(false); |
ccfd07
|
357 |
const rowMailId = ref(''); |
74a35f
|
358 |
const selectRow = ref([]); |
00fe0e
|
359 |
const cellClickEvent = (event) => { |
74a35f
|
360 |
selectRow.value = []; |
ccfd07
|
361 |
rowMailId.value = event.row.docCode; |
74a35f
|
362 |
selectRow.value.push({ docCode: event.row.docCode }); |
00fe0e
|
363 |
openDrawerDetail.value = true; |
H |
364 |
}; |
67287b
|
365 |
|
12f730
|
366 |
// 更新祖父组件数据 |
H |
367 |
const getDataList = inject('getDataList'); |
|
368 |
console.log(getDataList, '0000004'); |
|
369 |
|
74a35f
|
370 |
import { updateReadApi, updateHandleAPi } from '@/api/email/userList'; |
12f730
|
371 |
// 标志未读/经读 |
H |
372 |
function fnRowUpdateRead(row) { |
|
373 |
const data = { |
|
374 |
status: !row.readFlag, |
|
375 |
list: [row.docCode], |
|
376 |
}; |
|
377 |
pushReadApi(data); |
|
378 |
} |
|
379 |
function pushReadApi(params) { |
|
380 |
updateReadApi(params).then((res) => { |
|
381 |
if (res.code == 0) { |
|
382 |
// |
ccfd07
|
383 |
getDataList({}); |
12f730
|
384 |
} |
H |
385 |
}); |
|
386 |
} |
|
387 |
const emit = defineEmits(['selectAll', 'updateSelectAll']); |
67287b
|
388 |
defineExpose({ |
H |
389 |
fnSelectAll, |
|
390 |
}); |
74a35f
|
391 |
|
H |
392 |
import TooltipAndDropdown from './TooltipAndDropdown .vue'; |
|
393 |
import { formatToDateDay } from '@/utils/dateUtil'; |
00fe0e
|
394 |
</script> |
H |
395 |
<style scoped lang="less"> |
|
396 |
.display-flex { |
|
397 |
display: flex; |
|
398 |
align-items: center; |
|
399 |
justify-content: space-between; |
|
400 |
} |
|
401 |
|
|
402 |
.head { |
|
403 |
display: flex; |
|
404 |
justify-content: space-between; |
|
405 |
width: 100%; |
|
406 |
border-bottom: 1px solid rgb(5 5 5 / 6%); |
|
407 |
|
|
408 |
/* 增加选择器特异性 */ |
|
409 |
& .left { |
|
410 |
width: 20%; |
|
411 |
|
|
412 |
& .left-box { |
|
413 |
display: flex; |
|
414 |
align-items: center; |
|
415 |
justify-content: space-flex-start; |
|
416 |
width: 100%; |
|
417 |
|
|
418 |
& .icon { |
|
419 |
margin-right: 15px; |
|
420 |
font-size: 16px; |
|
421 |
} |
|
422 |
} |
|
423 |
} |
|
424 |
|
|
425 |
& .right { |
|
426 |
display: flex; |
|
427 |
align-items: center; |
|
428 |
} |
|
429 |
} |
|
430 |
|
|
431 |
.left-bt { |
|
432 |
display: flex; |
|
433 |
align-items: center; |
|
434 |
justify-content: center; |
|
435 |
padding-left: 27px; |
|
436 |
background: #fffbe6; |
|
437 |
} |
|
438 |
|
|
439 |
.span-title { |
63d608
|
440 |
width: 100%; |
H |
441 |
padding: 5px; |
00fe0e
|
442 |
color: #000; |
H |
443 |
font-weight: 700; |
63d608
|
444 |
text-align: left; |
00fe0e
|
445 |
} |
H |
446 |
|
|
447 |
.table { |
|
448 |
height: 80vh; |
|
449 |
} |
|
450 |
|
|
451 |
.my-menus { |
|
452 |
background-color: #f8f8f9; |
|
453 |
} |
12f730
|
454 |
// 圆点 |
H |
455 |
.dot { |
|
456 |
display: inline-block; |
|
457 |
width: 8px; |
|
458 |
height: 8px; |
|
459 |
margin-right: 10px; |
|
460 |
border-radius: 50%; |
|
461 |
background-color: #0a6aff; |
|
462 |
} |
|
463 |
|
|
464 |
.dot-color { |
|
465 |
background-color: #d9d9d9; |
|
466 |
color: #d9d9d9; |
|
467 |
} |
|
468 |
|
|
469 |
.title-dot { |
|
470 |
color: #0a6aff; |
|
471 |
} |
|
472 |
|
|
473 |
.title-dot-color { |
|
474 |
color: #999; |
|
475 |
} |
00fe0e
|
476 |
</style> |