<template>
|
<div style="overflow: auto">
|
<div v-if="groupedEmails.length != 0">
|
<div v-for="(item, index) in groupedEmails" :key="index">
|
<div class="span-title">{{ `${item.name}(${item.data.length})` }}</div>
|
<vxe-table
|
ref="vxeTableRef"
|
style="margin: 10px 0"
|
:showHeader="false"
|
:data="item.data"
|
size="small"
|
min-height="40px"
|
:row-config="{ isCurrent: true, isHover: true }"
|
:menu-config="{ enabled: true }"
|
@cell-click="cellClickEvent"
|
@checkbox-change="selectChangeEvent"
|
@cell-menu="onCellContextMenu"
|
>
|
<vxe-column type="checkbox" width="30"></vxe-column>
|
<vxe-column field="sender" title="发件人" data-index="sender" min-width="300px">
|
<template #default="{ row }">
|
<div style="display: flex; align-items: center">
|
<div
|
v-if="row.mailType != 0"
|
class="dot"
|
:class="fnIsItHighlighted(row) ? 'dot-color' : ''"
|
@click.stop="fnRowUpdateRead(row)"
|
></div>
|
<a-tooltip placement="bottom">
|
<template #title>
|
<span>陌生人</span>
|
</template>
|
<a-avatar size="small" style="margin-right: 8px" :src="row.avatar" />
|
</a-tooltip>
|
|
<a-popover placement="bottom">
|
<template #content>
|
<div
|
class="p-2"
|
style="
|
display: flex;
|
align-items: center;
|
border-bottom: 1px solid rgb(5 5 5 / 6%);
|
"
|
>
|
<a-avatar size="small" style="margin-right: 8px" :src="row.avatar" />
|
<span style="color: #000; font-weight: 700">{{ row.sender }}</span>
|
<CopyOutlined @click="copyText(row.sender)" />
|
</div>
|
<div class="display-flex p-2">
|
<a-button type="link" size="small">新建客户</a-button>
|
<a-dropdown>
|
<a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent>
|
<DownOutlined />
|
</a>
|
<template #overlay>
|
<a-menu>
|
<a-menu-item>
|
<a href="javascript:;">添加到已有客户</a>
|
</a-menu-item>
|
</a-menu>
|
</template>
|
</a-dropdown>
|
<a-button type="link" size="small">添加为线索</a-button>
|
|
<a-dropdown style="margin-right: 5px">
|
<a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent>
|
<DownOutlined />
|
</a>
|
<template #overlay>
|
<a-menu>
|
<a-menu-item>
|
<a href="javascript:;">添加到通讯录</a>
|
</a-menu-item>
|
</a-menu>
|
</template>
|
</a-dropdown>
|
<a-button type="link" size="small">往来邮件</a-button></div
|
>
|
</template>
|
<div :class="fnIsItHighlighted(row) ? 'title-dot-color' : ''">
|
<span style="font-weight: 700">{{ row.senderName }}</span
|
><span style="padding: 0 8px">|</span>
|
<span style="font-weight: 500">{{ row.sender }}</span>
|
</div>
|
</a-popover>
|
</div>
|
</template>
|
</vxe-column>
|
<vxe-column
|
show-overflow
|
field="icon"
|
title="表题"
|
data-index="icon"
|
width="100"
|
align="right"
|
>
|
<template #default="{ row }">
|
<a-tooltip placement="bottom">
|
<template #title>
|
<span>有附件</span>
|
</template>
|
<span v-show="row.attachmentList?.length > 0">
|
<PaperClipOutlined />
|
</span>
|
</a-tooltip>
|
</template>
|
</vxe-column>
|
|
<vxe-column
|
show-overflow
|
field="subject"
|
title="表题"
|
data-index="subject"
|
min-width="250"
|
>
|
<template #default="{ row }">
|
<span
|
class="title-dot"
|
:class="fnIsItHighlighted(row) ? 'title-dot-color' : ''"
|
style="font-weight: 500"
|
>{{ row.subject || '(无主题)' }}</span
|
>
|
-
|
<span style="color: #999">{{ row.subject }}</span>
|
</template>
|
</vxe-column>
|
<vxe-column field="action" title="Action" width="190">
|
<template #default="{ row, rowIndex }">
|
<span style="display: flex; justify-content: space-around">
|
<span>{{
|
row.mailType !== 0
|
? formatToDateDay(row.receiveTime || row.createTime)
|
: formatToDateDay(row.createTime)
|
}}</span>
|
|
<TooltipAndDropdown
|
:tooltipTitle="'待处理邮件'"
|
:initialDropdownOpen="false"
|
:initialTooltipOpen="false"
|
:showTooltip="!!row.handleTime"
|
:row="row"
|
:docCodeS="[row.docCode]"
|
/>
|
<span style="margin-left: 5px"><PushpinOutlined @click.stop="fnTagging" /></span>
|
</span>
|
</template>
|
</vxe-column>
|
</vxe-table>
|
<ContextMenu
|
v-if="showMenu"
|
:style="menuStyle"
|
:selected-cell="selectedCell"
|
@close-menu="showMenu = false"
|
/> </div
|
></div>
|
|
<div v-else style="display: flex; align-items: center; justify-content: center; height: 70vh">
|
<a-empty />
|
</div>
|
<DrawerDetail
|
ref="drawerDetailRef"
|
v-model="openDrawerDetail"
|
:mailId="rowMailId"
|
:selectAllRow="selectRow"
|
:allList="dataSource"
|
:isDrafts="isDrafts"
|
/>
|
<a-dropdown :trigger="['click']" placement="bottomLeft" ref="dropdownRefs"> </a-dropdown>
|
</div>
|
</template>
|
|
<script lang="ts" setup>
|
name: 'ListPageTable';
|
import {
|
FieldTimeOutlined,
|
PushpinOutlined,
|
CopyOutlined,
|
DownOutlined,
|
PaperClipOutlined,
|
} from '@ant-design/icons-vue';
|
|
import { ref, watch, defineProps, defineEmits, computed, defineExpose, inject } from 'vue';
|
|
// 定义属性
|
interface Props {
|
pageList: [];
|
isDrafts?: boolean;
|
}
|
const props = defineProps<Props>();
|
|
const groupedEmails = ref<GroupedDataItem[]>([]);
|
|
const dataSource = ref([]);
|
watch(
|
() => props.pageList,
|
(newValue) => {
|
dataSource.value = newValue || [];
|
groupedEmails.value = groupEmailsByDate(newValue || []);
|
},
|
);
|
import dayjs from 'dayjs';
|
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
import isoWeek from 'dayjs/plugin/isoWeek';
|
dayjs.extend(isSameOrAfter);
|
dayjs.extend(isSameOrBefore);
|
dayjs.extend(isoWeek);
|
|
interface EmailItem {
|
id: number;
|
subject: string;
|
}
|
|
// 确保 groupedData 的结构正确且 data 是 EmailItem 类型的数组
|
interface GroupedDataItem {
|
key: string;
|
data: EmailItem[];
|
name: string;
|
}
|
|
function groupEmailsByDate(dataSource) {
|
const today = dayjs();
|
const yesterday = dayjs().subtract(1, 'day');
|
const startOfWeek = dayjs().startOf('week');
|
const startOfMonth = dayjs().startOf('month');
|
const startOfLastMonth = dayjs().subtract(1, 'month').startOf('month');
|
const endOfLastMonth = dayjs().subtract(1, 'month').endOf('month');
|
|
const groupedData: GroupedDataItem[] = [
|
{
|
data: [],
|
name: '今天',
|
key: 'today',
|
},
|
{
|
data: [],
|
name: '昨天',
|
key: 'yesterday',
|
},
|
{
|
data: [],
|
name: '本周',
|
key: 'thisWeek',
|
},
|
{
|
data: [],
|
name: '这个月',
|
key: 'thisMonth',
|
},
|
{
|
data: [],
|
name: '上个月',
|
key: 'lastMonth',
|
},
|
{
|
data: [],
|
name: '更早',
|
key: 'earlier',
|
},
|
];
|
|
dataSource.forEach((item: any) => {
|
try {
|
const emailDate = dayjs(item.createTime);
|
if (emailDate.isSame(today, 'day')) {
|
groupedData[0].data.push(item);
|
} else if (emailDate.isSame(yesterday, 'day')) {
|
groupedData[1].data.push(item);
|
} else if (emailDate.isSameOrAfter(startOfWeek) && emailDate.isBefore(today, 'day')) {
|
groupedData[2].data.push(item);
|
} else if (emailDate.isSameOrAfter(startOfMonth) && emailDate.isBefore(today, 'day')) {
|
groupedData[3].data.push(item);
|
} else if (
|
emailDate.isSameOrAfter(startOfLastMonth) &&
|
emailDate.isSameOrBefore(endOfLastMonth)
|
) {
|
groupedData[4].data.push(item);
|
} else {
|
groupedData[5].data.push(item);
|
}
|
} catch (error) {
|
console.error(`Error processing item date: ${item.date}`, error);
|
}
|
});
|
// 将结果按中文映射进行返回
|
const result = <{ data: any; name: string; key: string }[]>[];
|
groupedData.forEach((group: { data: any; name: string; key: string }) => {
|
if (group.data.length > 0) {
|
result.push(group);
|
}
|
});
|
|
return result;
|
}
|
|
// 右键菜单
|
import ContextMenu from '@/views/email/components/ContextMenu/index.vue';
|
const showMenu = ref(false);
|
const menuStyle = ref({});
|
const selectedCell = ref(null);
|
const onCellContextMenu = ({
|
type,
|
row,
|
rowIndex,
|
$rowIndex,
|
column,
|
columnIndex,
|
$columnIndex,
|
$event,
|
}) => {
|
$event.preventDefault(); // 阻止默认的浏览器右键菜单?
|
selectedCell.value = { row, column, columnIndex }; // 保存当前选中的单元格数据
|
|
// 计算菜单初始位置
|
let menuX = $event.clientX;
|
let menuY = $event.clientY;
|
|
// 获取菜单的宽度和高度
|
const menuWidth = 200; // 假设菜单宽度为200px,可以根据实际情况调整
|
const menuHeight = 800; // 假设菜单高度为150px,可以根据实际情况调整
|
// 获取窗口宽度和高度
|
const windowWidth = window.innerWidth;
|
const windowHeight = window.innerHeight;
|
|
// 检查菜单是否超出窗口的右边界,如果是,则向左调整
|
if (menuX + menuWidth > windowWidth) {
|
menuX = windowWidth - menuWidth;
|
}
|
|
// 检查菜单是否超出窗口的下边界,如果是,则向上调整
|
if (menuY + menuHeight > windowHeight) {
|
menuY = windowHeight - menuHeight;
|
}
|
menuStyle.value = {
|
position: 'fixed',
|
top: `${menuY}px`,
|
left: `${menuX}px`,
|
};
|
showMenu.value = true;
|
};
|
|
// 监听全局点击事件,点击页面其他地方关闭菜单
|
document.addEventListener('click', () => {
|
showMenu.value = false;
|
});
|
|
const vxeTableRef = ref();
|
function fnIsItHighlighted(row) {
|
if (props.isDrafts) {
|
return row.readFlag && props.isDrafts;
|
} else {
|
return row.readFlag;
|
}
|
}
|
function fnSelectAll(is) {
|
try {
|
if (!vxeTableRef.value) {
|
return;
|
}
|
vxeTableRef.value.forEach((row) => {
|
row.setAllCheckboxRow(is);
|
});
|
selectChangeEvent();
|
} catch (error) {
|
console.log(error);
|
}
|
}
|
|
function selectChangeEvent() {
|
const isAll = getCheckboxRecords().length === dataSource.value.length;
|
const data = {
|
isAll,
|
records: getCheckboxRecords(),
|
};
|
emit('updateSelectAll', data);
|
}
|
function getCheckboxRecords() {
|
const list = new Set();
|
|
vxeTableRef.value.forEach((row) => {
|
const records = row.getCheckboxRecords(); // 假设该方法返回一个数组
|
if (Array.isArray(records)) {
|
// 确保 records 是数组
|
records.forEach((record) => list.add(record)); // 将记录添加到 Set 中
|
}
|
});
|
|
return Array.from(list); // 将 Set 转换回数组
|
}
|
// 操作row
|
function fnProcessingTime(row) {
|
console.log(row);
|
}
|
function fnTagging(row) {
|
console.log(row);
|
}
|
import DrawerDetail from './drawerDetail.vue';
|
// 详情内容
|
const openDrawerDetail = ref(false);
|
const rowMailId = ref('');
|
const selectRow = ref([]);
|
const cellClickEvent = (event) => {
|
selectRow.value = [];
|
rowMailId.value = event.row.docCode;
|
selectRow.value.push({ docCode: event.row.docCode });
|
fnRowUpdateRead(event.row);
|
openDrawerDetail.value = true;
|
};
|
|
// 更新祖父组件数据
|
const getDataList = inject('getDataList');
|
|
import { updateReadApi, updateHandleAPi } from '@/api/email/userList';
|
// 标志未读/经读
|
function fnRowUpdateRead(row) {
|
const data = {
|
status: !row.readFlag,
|
list: [row.docCode],
|
};
|
pushReadApi(data);
|
}
|
function pushReadApi(params) {
|
updateReadApi(params).then((res) => {
|
if (res.code == 0) {
|
//
|
getDataList({});
|
}
|
});
|
}
|
import { useMessage } from '@/hooks/web/useMessage';
|
|
const { createMessage } = useMessage();
|
const copyText = async (value) => {
|
try {
|
await navigator.clipboard.writeText(value);
|
setTimeout(() => {
|
createMessage.success('复制成功');
|
}, 500);
|
} catch (err) {
|
console.error('复制失败: ', err);
|
}
|
};
|
const emit = defineEmits(['selectAll', 'updateSelectAll']);
|
defineExpose({
|
fnSelectAll,
|
});
|
|
import TooltipAndDropdown from './TooltipAndDropdown .vue';
|
import { formatToDateDay } from '@/utils/dateUtil';
|
</script>
|
<style scoped lang="less">
|
.display-flex {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
}
|
|
.head {
|
display: flex;
|
justify-content: space-between;
|
width: 100%;
|
border-bottom: 1px solid rgb(5 5 5 / 6%);
|
|
/* 增加选择器特异性 */
|
& .left {
|
width: 20%;
|
|
& .left-box {
|
display: flex;
|
align-items: center;
|
justify-content: space-flex-start;
|
width: 100%;
|
|
& .icon {
|
margin-right: 15px;
|
font-size: 16px;
|
}
|
}
|
}
|
|
& .right {
|
display: flex;
|
align-items: center;
|
}
|
}
|
|
.left-bt {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
padding-left: 27px;
|
background: #fffbe6;
|
}
|
|
.span-title {
|
width: 100%;
|
height: 30px;
|
padding: 5px;
|
color: #000;
|
font-weight: 700;
|
line-height: 30px;
|
text-align: left;
|
}
|
|
.table {
|
height: 80vh;
|
}
|
|
.my-menus {
|
background-color: #f8f8f9;
|
}
|
// 圆点
|
.dot {
|
display: inline-block;
|
width: 8px;
|
height: 8px;
|
margin-right: 10px;
|
border-radius: 50%;
|
background-color: #0a6aff;
|
}
|
|
.dot-color {
|
background-color: #d9d9d9;
|
color: #d9d9d9;
|
}
|
|
.title-dot {
|
color: #0a6aff;
|
}
|
|
.title-dot-color {
|
color: #999;
|
}
|
</style>
|