diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/components/NotificationTemplateDialog.tsx b/frontend/src/pages/Deploy/NotificationTemplate/List/components/NotificationTemplateDialog.tsx index fb5364c6..e0dec962 100644 --- a/frontend/src/pages/Deploy/NotificationTemplate/List/components/NotificationTemplateDialog.tsx +++ b/frontend/src/pages/Deploy/NotificationTemplate/List/components/NotificationTemplateDialog.tsx @@ -35,6 +35,7 @@ import { updateTemplate, getTemplateById, checkTemplateCodeUnique, + renderTemplate, } from '../service'; import { TemplateEditor, TemplateRender } from './'; @@ -57,6 +58,8 @@ const formSchema = z.object({ channelType: z.nativeEnum(NotificationChannelType), contentTemplate: z.string().min(1, '请输入模板内容'), templateConfig: z.object({ + // 用于后端多态反序列化,必须存在 + channelType: z.nativeEnum(NotificationChannelType), // 企业微信配置 messageType: z.enum(['TEXT', 'MARKDOWN', 'FILE']).optional(), // 邮件配置 @@ -78,6 +81,9 @@ export const NotificationTemplateDialog: React.FC(''); + const [currentEnabled, setCurrentEnabled] = useState(true); const [formData, setFormData] = useState>({}); const { @@ -102,6 +108,7 @@ export const NotificationTemplateDialog: React.FC { @@ -147,20 +154,33 @@ export const NotificationTemplateDialog: React.FC { + if (watchedChannelType) { + setValue('templateConfig.channelType', watchedChannelType as any); + } + }, [watchedChannelType, setValue]); + const loadTemplateData = async () => { if (!editId) return; setLoading(true); try { const data = await getTemplateById(editId); + const cfg = data.templateConfig || TemplateConfigFormFactory.getDefaultConfig(data.channelType); + // 补齐内部的 channelType + if (!cfg.channelType) { + (cfg as any).channelType = data.channelType; + } reset({ name: data.name, code: data.code, description: data.description || '', channelType: data.channelType, contentTemplate: data.contentTemplate, - templateConfig: data.templateConfig || TemplateConfigFormFactory.getDefaultConfig(data.channelType), + templateConfig: cfg, }); + setCurrentEnabled(Boolean(data.enabled)); } catch (error: any) { toast({ variant: 'destructive', @@ -173,18 +193,61 @@ export const NotificationTemplateDialog: React.FC { + if (!watchedTemplate || !watchedTemplate.trim()) { + toast({ + variant: 'destructive', + title: '无法预览', + description: '请先输入模板内容', + }); + return; + } + + setPreviewRendering(true); + try { + // 先调用渲染 API + const content = await renderTemplate( + watchedTemplate, + formData || {} + ); + + // 渲染成功,保存结果并弹出预览窗口 + setRenderedContent(content); + setShowPreview(true); + } catch (error: any) { + // 渲染失败,显示错误提示,不弹出窗口 + toast({ + variant: 'destructive', + title: '渲染失败', + description: error.response?.data?.message || error.message || '模板渲染出错,请检查模板语法', + }); + } finally { + setPreviewRendering(false); + } + }; + const onSubmit = async (values: FormValues) => { setSaving(true); try { + // 确保 templateConfig不为空 + const templateConfig = values.templateConfig || TemplateConfigFormFactory.getDefaultConfig(values.channelType); + const payload = { + id: editId!, name: values.name, code: values.code, description: values.description, channelType: values.channelType, contentTemplate: values.contentTemplate, - templateConfig: values.templateConfig || TemplateConfigFormFactory.getDefaultConfig(values.channelType), + enabled: currentEnabled, + templateConfig: templateConfig, }; + console.log('=== 提交模板数据 ==='); + console.log('Payload:', JSON.stringify(payload, null, 2)); + console.log('TemplateConfig:', templateConfig); + if (mode === 'edit' && editId) { await updateTemplate(editId, payload); toast({ title: '更新成功' }); @@ -196,6 +259,9 @@ export const NotificationTemplateDialog: React.FC - + {loading ? (
加载中...
) : ( -
+ {/* 基础信息 */}
@@ -317,7 +383,7 @@ export const NotificationTemplateDialog: React.FC {}, } )} @@ -326,7 +392,7 @@ export const NotificationTemplateDialog: React.FC +
)}
-
+
setValue('contentTemplate', value)} @@ -391,12 +462,10 @@ export const NotificationTemplateDialog: React.FC 模板预览 - - + +
+ {renderedContent} +