222 lines
5.7 KiB
Vue
222 lines
5.7 KiB
Vue
<script setup lang="ts">
|
|
import type { TableColumnType } from 'ant-design-vue';
|
|
|
|
import type {
|
|
MemberMessageReachChannel,
|
|
MemberMessageReachListItemDto,
|
|
MemberMessageReachStatus,
|
|
} from '#/api/member/message-reach';
|
|
import type { MessageReachPager } from '#/views/member/message-reach/types';
|
|
|
|
import { Button, Pagination, Table, Tag } from 'ant-design-vue';
|
|
|
|
import {
|
|
formatInteger,
|
|
formatPercent,
|
|
MESSAGE_CHANNEL_COLOR_MAP,
|
|
MESSAGE_CHANNEL_TEXT_MAP,
|
|
MESSAGE_STATUS_COLOR_MAP,
|
|
MESSAGE_STATUS_TEXT_MAP,
|
|
resolveMessageTime,
|
|
} from '../composables/message-reach-page/helpers';
|
|
|
|
defineProps<{
|
|
canManage: boolean;
|
|
loading: boolean;
|
|
pager: MessageReachPager;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(event: 'delete', messageId: string): void;
|
|
(event: 'detail', messageId: string): void;
|
|
(event: 'edit', messageId: string): void;
|
|
(event: 'pageChange', page: number, pageSize: number): void;
|
|
}>();
|
|
|
|
const columns: TableColumnType<MemberMessageReachListItemDto>[] = [
|
|
{
|
|
title: '消息标题',
|
|
dataIndex: 'title',
|
|
key: 'title',
|
|
width: 280,
|
|
},
|
|
{
|
|
title: '推送渠道',
|
|
dataIndex: 'channels',
|
|
key: 'channels',
|
|
width: 180,
|
|
},
|
|
{
|
|
title: '目标人群',
|
|
dataIndex: 'audienceText',
|
|
key: 'audienceText',
|
|
width: 170,
|
|
},
|
|
{
|
|
title: '触达人数',
|
|
dataIndex: 'estimatedReachCount',
|
|
key: 'estimatedReachCount',
|
|
width: 120,
|
|
},
|
|
{
|
|
title: '发送时间',
|
|
key: 'sendTime',
|
|
width: 180,
|
|
},
|
|
{
|
|
title: '状态',
|
|
dataIndex: 'status',
|
|
key: 'status',
|
|
width: 120,
|
|
},
|
|
{
|
|
title: '操作',
|
|
key: 'action',
|
|
fixed: 'right',
|
|
width: 180,
|
|
},
|
|
];
|
|
|
|
function showMetricRow(record: Record<string, any>) {
|
|
return (
|
|
Number(record.openRate || 0) > 0 || Number(record.conversionRate || 0) > 0
|
|
);
|
|
}
|
|
|
|
function resolveChannelColor(channel: unknown) {
|
|
return (
|
|
MESSAGE_CHANNEL_COLOR_MAP[channel as MemberMessageReachChannel] || 'default'
|
|
);
|
|
}
|
|
|
|
function resolveChannelText(channel: unknown) {
|
|
return (
|
|
MESSAGE_CHANNEL_TEXT_MAP[channel as MemberMessageReachChannel] || '未知渠道'
|
|
);
|
|
}
|
|
|
|
function resolveStatusColor(status: unknown) {
|
|
return MESSAGE_STATUS_COLOR_MAP[status as MemberMessageReachStatus];
|
|
}
|
|
|
|
function resolveStatusText(status: unknown) {
|
|
return MESSAGE_STATUS_TEXT_MAP[status as MemberMessageReachStatus];
|
|
}
|
|
|
|
function canEdit(status: unknown) {
|
|
const currentStatus = status as MemberMessageReachStatus;
|
|
return currentStatus === 'draft' || currentStatus === 'failed';
|
|
}
|
|
|
|
function canDelete(status: unknown) {
|
|
const currentStatus = status as MemberMessageReachStatus;
|
|
return (
|
|
currentStatus === 'draft' ||
|
|
currentStatus === 'failed' ||
|
|
currentStatus === 'pending'
|
|
);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="mmr-table-wrap">
|
|
<Table
|
|
:columns="columns"
|
|
:data-source="pager.items"
|
|
:loading="loading"
|
|
:pagination="false"
|
|
row-key="messageId"
|
|
:scroll="{ x: 1080 }"
|
|
class="mmr-table"
|
|
>
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.key === 'title'">
|
|
<div class="mmr-message-title">{{ record.title }}</div>
|
|
<div v-if="showMetricRow(record)" class="mmr-message-metric">
|
|
打开率 {{ formatPercent(record.openRate) }} | 转化率
|
|
{{ formatPercent(record.conversionRate) }}
|
|
</div>
|
|
</template>
|
|
|
|
<template v-else-if="column.key === 'channels'">
|
|
<div class="mmr-channel-list">
|
|
<Tag
|
|
v-for="item in record.channels"
|
|
:key="`${record.messageId}-${item}`"
|
|
:color="resolveChannelColor(item)"
|
|
>
|
|
{{ resolveChannelText(item) }}
|
|
</Tag>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-else-if="column.key === 'estimatedReachCount'">
|
|
<span>
|
|
{{
|
|
record.estimatedReachCount > 0
|
|
? formatInteger(record.estimatedReachCount)
|
|
: '—'
|
|
}}
|
|
</span>
|
|
</template>
|
|
|
|
<template v-else-if="column.key === 'sendTime'">
|
|
<span class="mmr-time-text">
|
|
{{ resolveMessageTime(record.sentAt, record.scheduledAt) }}
|
|
</span>
|
|
</template>
|
|
|
|
<template v-else-if="column.key === 'status'">
|
|
<Tag :color="resolveStatusColor(record.status)">
|
|
{{ resolveStatusText(record.status) }}
|
|
</Tag>
|
|
</template>
|
|
|
|
<template v-else-if="column.key === 'action'">
|
|
<div class="mmr-table-actions">
|
|
<Button
|
|
type="link"
|
|
size="small"
|
|
class="mmr-action-link"
|
|
@click="emit('detail', record.messageId)"
|
|
>
|
|
详情
|
|
</Button>
|
|
<Button
|
|
v-if="canManage && canEdit(record.status)"
|
|
type="link"
|
|
size="small"
|
|
class="mmr-action-link"
|
|
@click="emit('edit', record.messageId)"
|
|
>
|
|
编辑
|
|
</Button>
|
|
<Button
|
|
v-if="canManage && canDelete(record.status)"
|
|
type="link"
|
|
size="small"
|
|
danger
|
|
class="mmr-action-link"
|
|
@click="emit('delete', record.messageId)"
|
|
>
|
|
删除
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
</Table>
|
|
|
|
<div class="mmr-pagination">
|
|
<Pagination
|
|
:current="pager.page"
|
|
:page-size="pager.pageSize"
|
|
:total="pager.totalCount"
|
|
show-size-changer
|
|
:show-total="(value) => `共 ${value} 条`"
|
|
:page-size-options="['10', '20', '50']"
|
|
@change="(page, pageSize) => emit('pageChange', page, pageSize)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|