重构消息通知弹窗
This commit is contained in:
parent
8647997e63
commit
79b6f4bade
@ -27,6 +27,7 @@ import { Textarea } from '@/components/ui/textarea';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { Loader2, Eye, Code } from 'lucide-react';
|
||||
import { NotificationChannelType } from '../../../NotificationChannel/List/types';
|
||||
import { TemplateConfigFormFactory } from './template-config-forms/TemplateConfigFormFactory';
|
||||
import type {
|
||||
NotificationTemplateDTO,
|
||||
ChannelTypeOption,
|
||||
@ -58,6 +59,13 @@ const formSchema = z.object({
|
||||
description: z.string().max(500, '描述不能超过500个字符').optional(),
|
||||
channelType: z.nativeEnum(NotificationChannelType),
|
||||
contentTemplate: z.string().min(1, '请输入模板内容'),
|
||||
templateConfig: z.object({
|
||||
// 企业微信配置
|
||||
messageType: z.enum(['TEXT', 'MARKDOWN', 'FILE']).optional(),
|
||||
// 邮件配置
|
||||
contentType: z.enum(['TEXT', 'HTML']).optional(),
|
||||
priority: z.enum(['LOW', 'NORMAL', 'HIGH']).optional(),
|
||||
}).optional(),
|
||||
});
|
||||
|
||||
type FormValues = z.infer<typeof formSchema>;
|
||||
@ -165,6 +173,7 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
description: '',
|
||||
channelType: NotificationChannelType.WEWORK,
|
||||
contentTemplate: '',
|
||||
templateConfig: TemplateConfigFormFactory.getDefaultConfig(NotificationChannelType.WEWORK),
|
||||
});
|
||||
setShowPreview(false);
|
||||
}
|
||||
@ -182,6 +191,7 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
description: data.description || '',
|
||||
channelType: data.channelType,
|
||||
contentTemplate: data.contentTemplate,
|
||||
templateConfig: data.templateConfig || TemplateConfigFormFactory.getDefaultConfig(data.channelType),
|
||||
});
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
@ -204,6 +214,7 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
description: values.description,
|
||||
channelType: values.channelType,
|
||||
contentTemplate: values.contentTemplate,
|
||||
templateConfig: values.templateConfig || TemplateConfigFormFactory.getDefaultConfig(values.channelType),
|
||||
};
|
||||
|
||||
if (mode === 'edit' && editId) {
|
||||
@ -332,6 +343,25 @@ export const NotificationTemplateDialog: React.FC<NotificationTemplateDialogProp
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 模板配置 */}
|
||||
{watchedChannelType && (
|
||||
<div className="space-y-2">
|
||||
<Label>模板配置</Label>
|
||||
<div className="border rounded-lg p-4 bg-muted/30">
|
||||
{TemplateConfigFormFactory.createConfigForm(
|
||||
watchedChannelType as NotificationChannelType,
|
||||
{
|
||||
register,
|
||||
errors,
|
||||
setValue,
|
||||
configState: {},
|
||||
updateConfigState: () => {},
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 模板编辑器区域 */}
|
||||
<div className="flex-1 flex gap-4 min-h-0">
|
||||
{/* 编辑器 */}
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 邮件模板配置表单
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import type { TemplateConfigComponentProps } from '../template-config-strategies/types';
|
||||
|
||||
export const EmailTemplateConfigForm: React.FC<TemplateConfigComponentProps> = ({
|
||||
register,
|
||||
errors,
|
||||
setValue,
|
||||
}) => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="contentType">内容类型</Label>
|
||||
<Select
|
||||
onValueChange={(value) => setValue('templateConfig.contentType', value)}
|
||||
defaultValue="TEXT"
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择内容类型" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="TEXT">纯文本</SelectItem>
|
||||
<SelectItem value="HTML">HTML格式</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors?.templateConfig?.contentType && (
|
||||
<p className="text-sm text-destructive mt-1">
|
||||
{errors.templateConfig.contentType.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="priority">优先级</Label>
|
||||
<Select
|
||||
onValueChange={(value) => setValue('templateConfig.priority', value)}
|
||||
defaultValue="NORMAL"
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择优先级" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="LOW">低</SelectItem>
|
||||
<SelectItem value="NORMAL">普通</SelectItem>
|
||||
<SelectItem value="HIGH">高</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors?.templateConfig?.priority && (
|
||||
<p className="text-sm text-destructive mt-1">
|
||||
{errors.templateConfig.priority.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 模板配置表单工厂
|
||||
*/
|
||||
import React from 'react';
|
||||
import { NotificationChannelType } from '../../../../NotificationChannel/List/types';
|
||||
import type { TemplateConfigComponentProps } from '../template-config-strategies/types';
|
||||
import { WeworkTemplateConfigForm } from './WeworkTemplateConfigForm';
|
||||
import { EmailTemplateConfigForm } from './EmailTemplateConfigForm';
|
||||
|
||||
export class TemplateConfigFormFactory {
|
||||
static createConfigForm(
|
||||
type: NotificationChannelType,
|
||||
props: TemplateConfigComponentProps
|
||||
): React.ReactElement | null {
|
||||
switch (type) {
|
||||
case NotificationChannelType.WEWORK:
|
||||
return <WeworkTemplateConfigForm {...props} />;
|
||||
|
||||
case NotificationChannelType.EMAIL:
|
||||
return <EmailTemplateConfigForm {...props} />;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static getSupportedTypes(): NotificationChannelType[] {
|
||||
return [
|
||||
NotificationChannelType.WEWORK,
|
||||
NotificationChannelType.EMAIL,
|
||||
];
|
||||
}
|
||||
|
||||
static getDefaultConfig(type: NotificationChannelType): any {
|
||||
switch (type) {
|
||||
case NotificationChannelType.WEWORK:
|
||||
return { messageType: 'TEXT' };
|
||||
|
||||
case NotificationChannelType.EMAIL:
|
||||
return { contentType: 'TEXT', priority: 'NORMAL' };
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 企业微信模板配置表单
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import type { TemplateConfigComponentProps } from '../template-config-strategies/types';
|
||||
|
||||
export const WeworkTemplateConfigForm: React.FC<TemplateConfigComponentProps> = ({
|
||||
register,
|
||||
errors,
|
||||
setValue,
|
||||
}) => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="messageType">消息类型</Label>
|
||||
<Select
|
||||
onValueChange={(value) => setValue('templateConfig.messageType', value)}
|
||||
defaultValue="TEXT"
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择消息类型" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="TEXT">文本消息</SelectItem>
|
||||
<SelectItem value="MARKDOWN">Markdown消息</SelectItem>
|
||||
<SelectItem value="FILE">文件消息</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors?.templateConfig?.messageType && (
|
||||
<p className="text-sm text-destructive mt-1">
|
||||
{errors.templateConfig.messageType.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 模板配置策略类型定义
|
||||
*/
|
||||
import { NotificationChannelType } from '../../../../NotificationChannel/List/types';
|
||||
|
||||
export interface TemplateConfigStrategy {
|
||||
/** 渠道类型 */
|
||||
type: NotificationChannelType;
|
||||
|
||||
/** 获取默认配置 */
|
||||
getDefaultConfig(): any;
|
||||
|
||||
/** 验证配置 */
|
||||
validateConfig(config: any): boolean;
|
||||
|
||||
/** 从API数据加载配置到表单状态 */
|
||||
loadFromData(data: any): TemplateConfigState;
|
||||
|
||||
/** 从表单状态构建API配置 */
|
||||
buildConfig(state: TemplateConfigState, formConfig: any): any;
|
||||
}
|
||||
|
||||
export interface TemplateConfigState {
|
||||
/** 额外的状态数据 */
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface TemplateConfigComponentProps {
|
||||
/** 表单注册函数 */
|
||||
register: any;
|
||||
|
||||
/** 表单错误 */
|
||||
errors: any;
|
||||
|
||||
/** 设置表单值 */
|
||||
setValue: any;
|
||||
|
||||
/** 配置状态 */
|
||||
configState: TemplateConfigState;
|
||||
|
||||
/** 更新配置状态 */
|
||||
updateConfigState: (updates: Partial<TemplateConfigState>) => void;
|
||||
}
|
||||
@ -22,6 +22,9 @@ export interface NotificationTemplateDTO extends BaseResponse {
|
||||
|
||||
/** 内容模板(FreeMarker格式) */
|
||||
contentTemplate: string;
|
||||
|
||||
/** 模板配置(根据 channelType 不同而不同) */
|
||||
templateConfig?: WeworkTemplateConfig | EmailTemplateConfig;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@ -51,6 +54,53 @@ export interface ChannelTypeOption {
|
||||
description: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 5. 模板配置类型
|
||||
// ============================================
|
||||
|
||||
/** 模板配置基类 */
|
||||
export interface BaseTemplateConfig {
|
||||
// 运行时通过 channelType 判断具体类型
|
||||
}
|
||||
|
||||
/** 企业微信模板配置 */
|
||||
export interface WeworkTemplateConfig extends BaseTemplateConfig {
|
||||
/** 消息类型 */
|
||||
messageType: 'TEXT' | 'MARKDOWN' | 'FILE';
|
||||
}
|
||||
|
||||
/** 邮件模板配置 */
|
||||
export interface EmailTemplateConfig extends BaseTemplateConfig {
|
||||
/** 内容类型 */
|
||||
contentType: 'TEXT' | 'HTML';
|
||||
/** 优先级 */
|
||||
priority: 'LOW' | 'NORMAL' | 'HIGH';
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 6. 类型守卫函数
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 判断是否为企业微信模板配置
|
||||
*/
|
||||
export function isWeworkTemplateConfig(
|
||||
config: BaseTemplateConfig,
|
||||
channelType: NotificationChannelType
|
||||
): config is WeworkTemplateConfig {
|
||||
return channelType === 'WEWORK' && 'messageType' in config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为邮件模板配置
|
||||
*/
|
||||
export function isEmailTemplateConfig(
|
||||
config: BaseTemplateConfig,
|
||||
channelType: NotificationChannelType
|
||||
): config is EmailTemplateConfig {
|
||||
return channelType === 'EMAIL' && 'contentType' in config && 'priority' in config;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 7. 模板渲染参数
|
||||
// ============================================
|
||||
|
||||
Loading…
Reference in New Issue
Block a user