Files
TakeoutSaaS.TenantUI/apps/web-antd/src/views/member/message-reach/composables/message-reach-page/helpers.ts

265 lines
6.4 KiB
TypeScript

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<MemberMessageReachStatus, string> =
{
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';
}