feat: 完成会员消息触达模块页面与交互
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user