diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/components/NotificationTemplateDialog.tsx b/frontend/src/pages/Deploy/NotificationTemplate/List/components/NotificationTemplateDialog.tsx index ee13ebe2..fb5364c6 100644 --- a/frontend/src/pages/Deploy/NotificationTemplate/List/components/NotificationTemplateDialog.tsx +++ b/frontend/src/pages/Deploy/NotificationTemplate/List/components/NotificationTemplateDialog.tsx @@ -23,15 +23,12 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -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, +import type { ChannelTypeOption, - TemplateVariable } from '../types'; import { createTemplate, @@ -39,7 +36,7 @@ import { getTemplateById, checkTemplateCodeUnique, } from '../service'; -import { TemplateEditor, TemplatePreview } from './'; +import { TemplateEditor, TemplateRender } from './'; interface NotificationTemplateDialogProps { open: boolean; @@ -81,7 +78,6 @@ export const NotificationTemplateDialog: React.FC([]); const [formData, setFormData] = useState>({}); const { @@ -107,34 +103,6 @@ export const NotificationTemplateDialog: React.FC { - const commonVars: TemplateVariable[] = [ - { name: 'projectName', description: '项目名称', example: 'deploy-ease-platform' }, - { name: 'buildNumber', description: '构建号', example: '123' }, - { name: 'buildStatus', description: '构建状态', example: 'SUCCESS' }, - { name: 'operator', description: '操作人', example: 'admin' }, - { name: 'timestamp', description: '时间戳', example: new Date().toLocaleString() }, - ]; - - if (channelType === NotificationChannelType.EMAIL) { - return [ - ...commonVars, - { name: 'recipient', description: '收件人', example: 'user@example.com' }, - { name: 'subject', description: '邮件主题', example: '部署通知' }, - ]; - } - - return commonVars; - }; - - // 监听渠道类型变化 - useEffect(() => { - if (watchedChannelType) { - setVariables(getVariables(watchedChannelType)); - } - }, [watchedChannelType]); - // 验证编码唯一性 const validateCodeUnique = async (code: string) => { if (!code || code.length < 1) return; @@ -238,134 +206,127 @@ export const NotificationTemplateDialog: React.FC { - const currentTemplate = watchedTemplate || ''; - const variableText = `\${${variable.name}}`; - setValue('contentTemplate', currentTemplate + variableText); - }; return ( - - - - - {mode === 'edit' ? '编辑' : '创建'}通知模板 - - + <> + + + + + {mode === 'edit' ? '编辑' : '创建'}通知模板 + + - - {loading ? ( -
- - 加载中... -
- ) : ( -
- {/* 基础信息 */} -
-
- - - {errors.name && ( -

{errors.name.message}

- )} -
- -
- - - {mode === 'edit' && ( -

- 编辑时不可修改模板编码 -

- )} - {errors.code && ( -

{errors.code.message}

- )} -
+ + {loading ? ( +
+ + 加载中...
+ ) : ( + + {/* 基础信息 */} +
+
+ + + {errors.name && ( +

{errors.name.message}

+ )} +
-
-
- - - {mode === 'edit' && ( -

- 编辑时不可修改渠道类型 -

- )} - {errors.channelType && ( -

{errors.channelType.message}

- )} -
- -
- - - {errors.description && ( -

{errors.description.message}

- )} -
-
- - {/* 模板配置 */} - {watchedChannelType && ( -
- -
- {TemplateConfigFormFactory.createConfigForm( - watchedChannelType as NotificationChannelType, - { - register, - errors, - setValue, - configState: {}, - updateConfigState: () => {}, - } +
+ + + {mode === 'edit' && ( +

+ 编辑时不可修改模板编码 +

+ )} + {errors.code && ( +

{errors.code.message}

)}
- )} - {/* 模板编辑器区域 */} -
- {/* 编辑器 */} -
+
+
+ + + {mode === 'edit' && ( +

+ 编辑时不可修改渠道类型 +

+ )} + {errors.channelType && ( +

{errors.channelType.message}

+ )} +
+ +
+ + + {errors.description && ( +

{errors.description.message}

+ )} +
+
+ + {/* 模板配置 */} + {watchedChannelType && ( +
+ +
+ {TemplateConfigFormFactory.createConfigForm( + watchedChannelType as NotificationChannelType, + { + register, + errors, + setValue, + configState: {}, + updateConfigState: () => {}, + } + )} +
+
+ )} + + {/* 模板编辑器区域 */} +
- +
setValue('contentTemplate', value)} channelType={watchedChannelType} - variables={variables} - onVariableInsert={handleInsertVariable} onFormDataChange={setFormData} mode={mode} />
- + {errors.contentTemplate && (

{errors.contentTemplate.message}

)}
+ + )} + - {/* 预览面板 - 只在编辑模式显示 */} - {mode === 'edit' && showPreview && ( -
- -
- -
-
- )} -
- + {!loading && ( + + + + )} - + +
- {!loading && ( - - - - - )} -
-
+ {/* 预览弹窗 - 只在编辑模式显示 */} + {mode === 'edit' && showPreview && ( + + + + 模板预览 + + + + + + + + + + )} + ); }; diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplateEditor.tsx b/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplateEditor.tsx index dac01292..c37c931a 100644 --- a/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplateEditor.tsx +++ b/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplateEditor.tsx @@ -288,17 +288,20 @@ export const TemplateEditor: React.FC = ({
+ {console.log('formSchema:', formSchema)} {formSchema ? (
- { - setFormData(data); - }} - showSubmit={false} - showCancel={false} - /> + { + console.log('FormRenderer onChange called with data:', data); + setFormData(data); + }} + showSubmit={false} + showCancel={false} + readonly={false} + />
) : ( diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplatePreview.tsx b/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplatePreview.tsx deleted file mode 100644 index 0c8bd2d5..00000000 --- a/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplatePreview.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/** - * 模板预览组件 - */ -import React, { useState, useEffect } from 'react'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { Alert, AlertDescription } from '@/components/ui/alert'; -import { Loader2, RefreshCw, AlertCircle } from 'lucide-react'; -import { NotificationChannelType } from '../../../NotificationChannel/List/types'; - -interface TemplatePreviewProps { - template: string; - channelType: NotificationChannelType; - formData?: Record; // 来自FormDesigner的表单数据 -} - -export const TemplatePreview: React.FC = ({ - template, - channelType, - formData, -}) => { - const [loading, setLoading] = useState(false); - const [previewContent, setPreviewContent] = useState(''); - const [error, setError] = useState(null); - - // 预览模板 - const handlePreview = async () => { - if (!template.trim()) { - setPreviewContent(''); - setError(null); - return; - } - - setLoading(true); - setError(null); - - try { - // 预览时直接显示模板内容,不调用后端渲染 - setPreviewContent(template); - } catch (error: any) { - setError('预览失败'); - setPreviewContent(''); - } finally { - setLoading(false); - } - }; - - // 自动预览 - useEffect(() => { - const timer = setTimeout(() => { - handlePreview(); - }, 500); - - return () => clearTimeout(timer); - }, [template, channelType, formData]); - - return ( -
- - {/* 当有formData时显示提示信息 */} - {formData && Object.keys(formData).length > 0 && ( -
-
-
-

使用表单设计器数据

-

- 正在使用表单设计器中的参数进行模板渲染 -

-
- -
-
- )} - - {/* 预览内容 */} -
-
-
-

预览结果

- -
-
- -
- {loading ? ( -
- - 渲染中... -
- ) : error ? ( - - - {error} - - ) : ( -
-
- {previewContent || '请输入模板内容进行预览'} -
-
- )} -
-
-
- ); -}; diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplateRender.tsx b/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplateRender.tsx new file mode 100644 index 00000000..6d3c0b16 --- /dev/null +++ b/frontend/src/pages/Deploy/NotificationTemplate/List/components/TemplateRender.tsx @@ -0,0 +1,80 @@ +/** + * 模板预览组件 + */ +import React, { useState, useEffect } from 'react'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { Loader2, AlertCircle } from 'lucide-react'; +import { NotificationChannelType } from '../../../NotificationChannel/List/types'; + +interface TemplateRenderProps { + template: string; + channelType: NotificationChannelType; + formData?: Record; // 来自FormDesigner的表单数据 +} + +export const TemplateRender: React.FC = ({ + template, + channelType, + formData, +}) => { + const [loading, setLoading] = useState(false); + const [previewContent, setPreviewContent] = useState(''); + const [error, setError] = useState(null); + + // 预览模板 + const handlePreview = async () => { + console.log('handlePreview called, template:', template); + console.log('formData:', formData); + + if (!template.trim()) { + setPreviewContent(''); + setError(null); + console.log('Template is empty, returning'); + return; + } + + setLoading(true); + setError(null); + + try { + // 不调用render接口,直接使用模板内容 + setPreviewContent(template); + } catch (error: any) { + setError('渲染失败'); + setPreviewContent(''); + } finally { + setLoading(false); + } + }; + + // 自动预览 + useEffect(() => { + const timer = setTimeout(() => { + handlePreview(); + }, 500); + + return () => clearTimeout(timer); + }, [template, channelType, formData]); + + return ( +
+ {loading ? ( +
+ + 加载中... +
+ ) : error ? ( + + + {error} + + ) : ( +
+
+ {previewContent || '请输入模板内容进行预览'} +
+
+ )} +
+ ); +}; diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/components/index.ts b/frontend/src/pages/Deploy/NotificationTemplate/List/components/index.ts index 27576f05..83468973 100644 --- a/frontend/src/pages/Deploy/NotificationTemplate/List/components/index.ts +++ b/frontend/src/pages/Deploy/NotificationTemplate/List/components/index.ts @@ -3,5 +3,5 @@ */ export { default as NotificationTemplateDialog } from './NotificationTemplateDialog'; export { TemplateEditor } from './TemplateEditor'; -export { TemplatePreview } from './TemplatePreview'; +export { TemplateRender } from './TemplateRender'; export { default as TestTemplateDialog } from './TestTemplateDialog'; diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/examples/usage.ts b/frontend/src/pages/Deploy/NotificationTemplate/List/examples/usage.ts deleted file mode 100644 index f40e95a0..00000000 --- a/frontend/src/pages/Deploy/NotificationTemplate/List/examples/usage.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * 通知模板使用示例 - */ -import { renderTemplate } from '../service'; -import type { TemplateRenderRequest } from '../types'; - -/** - * 渲染Jenkins构建通知模板示例 - */ -export const renderJenkinsBuildNotification = async () => { - const request: TemplateRenderRequest = { - templateCode: 'jenkins_build_wework', - params: { - projectName: 'deploy-ease-platform', - buildNumber: 123, - buildStatus: 'SUCCESS', - buildUrl: 'http://jenkins.example.com/job/deploy-ease-platform/123/', - duration: '2m 30s', - commitId: 'abc123def', - commitMessage: 'feat: 添加通知模板功能', - author: 'developer@example.com', - timestamp: new Date().toISOString(), - }, - }; - - try { - const response = await renderTemplate(request); - if (response.success) { - console.log('渲染成功:', response.content); - return response.content; - } else { - console.error('渲染失败:', response.error); - throw new Error(response.error); - } - } catch (error) { - console.error('调用渲染接口失败:', error); - throw error; - } -}; - -/** - * 渲染部署成功通知模板示例 - */ -export const renderDeploySuccessNotification = async () => { - const request: TemplateRenderRequest = { - templateCode: 'deploy_success_email', - params: { - applicationName: '部署平台', - environment: '生产环境', - version: 'v1.2.3', - deployTime: new Date().toLocaleString('zh-CN'), - deployUrl: 'https://deploy.example.com', - operator: '运维团队', - releaseNotes: [ - '新增通知模板管理功能', - '优化部署流程', - '修复已知问题', - ], - }, - }; - - try { - const response = await renderTemplate(request); - if (response.success) { - return response.content; - } else { - throw new Error(response.error); - } - } catch (error) { - console.error('渲染部署通知失败:', error); - throw error; - } -}; - -/** - * 在其他业务模块中使用模板渲染 - * - * 使用方式: - * 1. 导入渲染函数 - * 2. 准备模板编码和参数 - * 3. 调用渲染接口 - * 4. 处理渲染结果 - */ -export const useTemplateInBusiness = async ( - templateCode: string, - businessData: Record -) => { - const request: TemplateRenderRequest = { - templateCode, - params: businessData, - }; - - const response = await renderTemplate(request); - - if (response.success) { - // 渲染成功,可以发送通知 - return { - success: true, - content: response.content, - }; - } else { - // 渲染失败,记录错误 - console.error(`模板 ${templateCode} 渲染失败:`, response.error); - return { - success: false, - error: response.error, - }; - } -}; diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/service.ts b/frontend/src/pages/Deploy/NotificationTemplate/List/service.ts index 4ffec48b..c01c1685 100644 --- a/frontend/src/pages/Deploy/NotificationTemplate/List/service.ts +++ b/frontend/src/pages/Deploy/NotificationTemplate/List/service.ts @@ -117,12 +117,16 @@ export const checkTemplateCodeUnique = async ( }; /** - * 渲染模板(根据模板编码) + * 渲染模板(根据模板内容) */ export const renderTemplate = async ( - renderRequest: TemplateRenderRequest + templateContext: string, + params: Record ): Promise<{ content: string; success: boolean; error?: string }> => { - return request.post(`${API_PREFIX}/render`, renderRequest); + return request.post(`${API_PREFIX}/render`, { + templateContext, + params + }); }; diff --git a/frontend/src/pages/Deploy/NotificationTemplate/List/types.ts b/frontend/src/pages/Deploy/NotificationTemplate/List/types.ts index 6ced4e1c..bad2f0a4 100644 --- a/frontend/src/pages/Deploy/NotificationTemplate/List/types.ts +++ b/frontend/src/pages/Deploy/NotificationTemplate/List/types.ts @@ -105,8 +105,8 @@ export function isEmailTemplateConfig( // 7. 模板渲染参数 // ============================================ export interface TemplateRenderRequest { - /** 模板编码 */ - templateCode: string; + /** 模板内容 */ + templateContext: string; /** 模板参数 */ params: Record;