import type { MemberMessageReachChannel, MemberMessageReachDetailDto, MemberMessageReachStatus, MemberMessageScheduleType, MemberMessageTemplateCategory, SaveMemberMessageReachPayload, SaveMemberMessageTemplatePayload, } from '#/api/member/message-reach'; import type { MessageReachEditorForm, MessageReachFilterForm, MessageTemplateEditorForm, MessageTemplateFilterForm, } from '#/views/member/message-reach/types'; import dayjs from 'dayjs'; /** 状态文案。 */ export const MESSAGE_STATUS_TEXT_MAP: Record = { draft: '草稿', pending: '待发送', sending: '发送中', sent: '已发送', failed: '发送失败', }; /** 状态标签色。 */ export const MESSAGE_STATUS_COLOR_MAP: Record< MemberMessageReachStatus, string > = { draft: 'default', pending: 'processing', sending: 'warning', sent: 'success', failed: 'error', }; /** 渠道文案。 */ export const MESSAGE_CHANNEL_TEXT_MAP: Record< MemberMessageReachChannel, string > = { inapp: '站内信', sms: '短信', 'wechat-mini': '微信模板', }; /** 渠道标签色。 */ export const MESSAGE_CHANNEL_COLOR_MAP: Record< MemberMessageReachChannel, string > = { inapp: 'blue', sms: 'green', 'wechat-mini': 'orange', }; /** 模板分类文案。 */ export const MESSAGE_TEMPLATE_CATEGORY_TEXT_MAP: Record< MemberMessageTemplateCategory, string > = { marketing: '营销', notice: '通知', recall: '召回', }; /** 模板分类颜色。 */ export const MESSAGE_TEMPLATE_CATEGORY_COLOR_MAP: Record< MemberMessageTemplateCategory, string > = { marketing: 'magenta', notice: 'blue', recall: 'red', }; /** 收件状态文案。 */ export const MESSAGE_RECIPIENT_STATUS_TEXT_MAP: Record< 'failed' | 'pending' | 'sent', string > = { pending: '待发送', sent: '已发送', failed: '发送失败', }; /** 收件状态颜色。 */ export const MESSAGE_RECIPIENT_STATUS_COLOR_MAP: Record< 'failed' | 'pending' | 'sent', string > = { pending: 'processing', sent: 'success', failed: 'error', }; /** 格式化百分比。 */ export function formatPercent(value: null | number | undefined) { const amount = Number(value ?? 0); if (!Number.isFinite(amount)) { return '0%'; } const digits = amount % 1 === 0 ? 0 : 1; return `${amount.toFixed(digits)}%`; } /** 格式化数量。 */ export function formatInteger(value: null | number | undefined) { const amount = Number(value ?? 0); if (!Number.isFinite(amount)) { return '0'; } return Math.round(amount).toLocaleString('zh-CN'); } /** 格式化时间。 */ export function formatDateTime(value?: string) { if (!value) { return '—'; } const parsed = dayjs(value); if (!parsed.isValid()) { return value; } return parsed.format('YYYY-MM-DD HH:mm'); } /** 解析消息发送时间。 */ export function resolveMessageTime( sentAt?: string, scheduledAt?: string, ): string { if (sentAt) { return formatDateTime(sentAt); } if (scheduledAt) { return formatDateTime(scheduledAt); } return '—'; } /** 将列表筛选映射为查询参数。 */ export function mapMessageFilterToQuery(form: MessageReachFilterForm) { const keyword = form.keyword.trim(); return { status: form.status || undefined, channel: form.channel || undefined, keyword: keyword || undefined, }; } /** 将模板筛选映射为查询参数。 */ export function mapTemplateFilterToQuery(form: MessageTemplateFilterForm) { const keyword = form.keyword.trim(); return { category: form.category || undefined, keyword: keyword || undefined, }; } /** 将详情映射为编辑表单。 */ export function mapDetailToEditorForm( detail: MemberMessageReachDetailDto, form: MessageReachEditorForm, ) { form.messageId = detail.messageId; form.templateId = detail.templateId; form.title = detail.title; form.content = detail.content; form.channels = [...detail.channels]; form.audienceType = detail.audienceType; form.audienceTags = [...detail.audienceTags]; form.scheduleType = detail.scheduleType; form.scheduledAt = detail.scheduleType === 'scheduled' && detail.scheduledAt ? dayjs(detail.scheduledAt) : null; } /** 重置消息编辑表单。 */ export function resetMessageEditorForm(form: MessageReachEditorForm) { form.messageId = ''; form.templateId = undefined; form.title = ''; form.content = ''; form.channels = ['inapp']; form.audienceType = 'all'; form.audienceTags = []; form.scheduleType = 'immediate'; form.scheduledAt = null; } /** 重置模板编辑表单。 */ export function resetTemplateEditorForm(form: MessageTemplateEditorForm) { form.templateId = ''; form.name = ''; form.category = 'notice'; form.content = ''; } /** 消息编辑表单转保存请求。 */ export function mapMessageEditorFormToSavePayload( form: MessageReachEditorForm, submitAction: 'draft' | 'send', ): SaveMemberMessageReachPayload { const payload: SaveMemberMessageReachPayload = { messageId: form.messageId || undefined, templateId: form.templateId || undefined, title: form.title.trim(), content: form.content.trim(), channels: [...form.channels], audienceType: form.audienceType, audienceTags: [...form.audienceTags], scheduleType: form.scheduleType, scheduledAt: form.scheduleType === 'scheduled' && form.scheduledAt ? form.scheduledAt.toISOString() : undefined, submitAction, }; return payload; } /** 模板编辑表单转保存请求。 */ export function mapTemplateEditorFormToSavePayload( form: MessageTemplateEditorForm, ): SaveMemberMessageTemplatePayload { return { templateId: form.templateId || undefined, name: form.name.trim(), category: form.category, content: form.content.trim(), }; } /** 切换渠道。 */ export function toggleChannel( channels: MemberMessageReachChannel[], channel: MemberMessageReachChannel, ) { if (channels.includes(channel)) { return channels.filter((item) => item !== channel); } return [...channels, channel]; } /** 切换标签。 */ export function toggleTag(tags: string[], tag: string) { if (tags.includes(tag)) { return tags.filter((item) => item !== tag); } return [...tags, tag]; } /** 解析时间类型。 */ export function resolveScheduleType(value: string): MemberMessageScheduleType { return value === 'scheduled' ? 'scheduled' : 'immediate'; }