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

265 lines
7.2 KiB
TypeScript

import type { Dayjs } from 'dayjs';
import type { Ref } from 'vue';
import type {
MemberMessageTemplateDto,
SaveMemberMessageReachPayload,
} from '#/api/member/message-reach';
import type {
MemberMessageReachTabKey,
MessageReachDetailViewModel,
MessageReachEditorForm,
} from '#/views/member/message-reach/types';
import { message, Modal } from 'ant-design-vue';
import {
deleteMemberMessageReachApi,
saveMemberMessageReachApi,
} from '#/api/member/message-reach';
import {
mapDetailToEditorForm,
mapMessageEditorFormToSavePayload,
resetMessageEditorForm,
toggleTag,
} from './helpers';
interface CreateMessageActionsOptions {
activeTab: Ref<MemberMessageReachTabKey>;
audienceEstimateCount: Ref<number>;
canManage: Ref<boolean>;
detail: Ref<MessageReachDetailViewModel | null>;
detailDrawerMessageId: Ref<string>;
form: MessageReachEditorForm;
isDetailDrawerOpen: Ref<boolean>;
isMessageDrawerOpen: Ref<boolean>;
isMessageSubmitting: Ref<boolean>;
messageDrawerMode: Ref<'create' | 'edit'>;
loadMessageDetail: (
messageId: string,
) => Promise<MessageReachDetailViewModel | null>;
loadMessageList: () => Promise<void>;
loadStats: () => Promise<void>;
}
export function createMessageActions(options: CreateMessageActionsOptions) {
function setMessageDrawerOpen(value: boolean) {
options.isMessageDrawerOpen.value = value;
}
function setMessageTitle(value: string) {
options.form.title = value;
}
function setMessageContent(value: string) {
options.form.content = value;
}
function setMessageChannel(channel: 'inapp' | 'sms' | 'wechat-mini') {
if (
options.form.channels.length === 1 &&
options.form.channels[0] === channel
) {
return;
}
options.form.channels = [channel];
}
function setAudienceType(value: 'all' | 'tag') {
options.form.audienceType = value;
if (value === 'all') {
options.form.audienceTags = [];
}
}
function toggleAudienceTag(value: string) {
options.form.audienceTags = toggleTag(options.form.audienceTags, value);
}
function setScheduleType(value: 'immediate' | 'scheduled') {
options.form.scheduleType = value;
if (value === 'immediate') {
options.form.scheduledAt = null;
}
}
function setScheduledAt(value: Dayjs | null) {
options.form.scheduledAt = value;
}
function switchToTemplateTab() {
options.activeTab.value = 'template';
options.isMessageDrawerOpen.value = false;
}
async function openCreateMessageDrawer() {
if (!options.canManage.value) {
return;
}
resetMessageEditorForm(options.form);
options.audienceEstimateCount.value = 0;
options.messageDrawerMode.value = 'create';
options.isMessageDrawerOpen.value = true;
}
async function openEditMessageDrawer(messageId: string) {
if (!options.canManage.value) {
return;
}
const detail = await options.loadMessageDetail(messageId);
if (!detail) {
return;
}
mapDetailToEditorForm(detail, options.form);
options.audienceEstimateCount.value = detail.estimatedReachCount;
options.messageDrawerMode.value = 'edit';
options.isMessageDrawerOpen.value = true;
}
async function openDetailDrawer(messageId: string) {
options.detailDrawerMessageId.value = messageId;
options.isDetailDrawerOpen.value = true;
await options.loadMessageDetail(messageId);
}
async function refreshDetailIfNeeded(messageId: string) {
if (!options.isDetailDrawerOpen.value) {
return;
}
if (options.detailDrawerMessageId.value !== messageId) {
return;
}
await options.loadMessageDetail(messageId);
}
function useTemplateToCreateMessage(template: MemberMessageTemplateDto) {
if (!options.canManage.value) {
return;
}
resetMessageEditorForm(options.form);
options.form.templateId = template.templateId;
options.form.title = template.name;
options.form.content = template.content;
options.audienceEstimateCount.value = 0;
options.messageDrawerMode.value = 'create';
options.isMessageDrawerOpen.value = true;
options.activeTab.value = 'list';
}
function validateMessagePayload(payload: SaveMemberMessageReachPayload) {
if (!payload.title) {
message.warning('请输入消息标题');
return false;
}
if (!payload.content) {
message.warning('请输入消息内容');
return false;
}
if (payload.channels.length === 0) {
message.warning('请至少选择一个推送渠道');
return false;
}
if (payload.audienceType === 'tag' && payload.audienceTags.length === 0) {
message.warning('请选择目标标签');
return false;
}
if (payload.scheduleType === 'scheduled' && !payload.scheduledAt) {
message.warning('请选择定时发送时间');
return false;
}
return true;
}
async function submitMessage(submitAction: 'draft' | 'send') {
if (!options.canManage.value) {
return;
}
const payload = mapMessageEditorFormToSavePayload(
options.form,
submitAction,
);
if (!validateMessagePayload(payload)) {
return;
}
options.isMessageSubmitting.value = true;
try {
const result = await saveMemberMessageReachApi(payload);
message.success(
submitAction === 'send' ? '发送任务已提交' : '草稿已保存',
);
options.isMessageDrawerOpen.value = false;
await Promise.all([options.loadStats(), options.loadMessageList()]);
await refreshDetailIfNeeded(result.messageId);
} catch (error) {
console.error(error);
message.error(submitAction === 'send' ? '发送失败' : '保存草稿失败');
} finally {
options.isMessageSubmitting.value = false;
}
}
async function removeMessage(messageId: string) {
if (!options.canManage.value) {
return;
}
Modal.confirm({
title: '确认删除消息?',
content: '删除后无法恢复,且会取消未执行的发送任务。',
okText: '删除',
okType: 'danger',
cancelText: '取消',
async onOk() {
try {
await deleteMemberMessageReachApi({ messageId });
message.success('删除成功');
if (options.detailDrawerMessageId.value === messageId) {
options.isDetailDrawerOpen.value = false;
options.detailDrawerMessageId.value = '';
options.detail.value = null;
}
await Promise.all([options.loadStats(), options.loadMessageList()]);
} catch (error) {
console.error(error);
message.error('删除失败');
}
},
});
}
function setDetailDrawerOpen(value: boolean) {
options.isDetailDrawerOpen.value = value;
if (!value) {
options.detailDrawerMessageId.value = '';
options.detail.value = null;
}
}
return {
openCreateMessageDrawer,
openDetailDrawer,
openEditMessageDrawer,
removeMessage,
setAudienceType,
setDetailDrawerOpen,
setMessageContent,
setMessageDrawerOpen,
setMessageChannel,
setMessageTitle,
setScheduleType,
setScheduledAt,
submitMessage,
switchToTemplateTab,
toggleAudienceTag,
useTemplateToCreateMessage,
};
}