deploy-ease-platform/frontend/src/pages/FormDesigner/index.tsx
2025-10-25 00:20:19 +08:00

627 lines
21 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* FormDesigner 组件使用示例页面
*
* 展示如何使用 @/components/FormDesigner 中的组件:
* - FormDesigner: 表单设计器(设计时)
* - FormRenderer: 表单渲染器(运行时)
*/
import React, { useState } from 'react';
import { message, Tabs, Card, Button, Modal, Space, Divider, Alert } from 'antd';
import { FormDesigner, FormRenderer, type FormSchema } from '@/components/FormDesigner';
import { WORKFLOW_COMPONENTS } from '@/components/FormDesigner/extensions/workflow';
const FormDesignerExamplesPage: React.FC = () => {
// ==================== 示例 1: 普通表单设计器 ====================
const [designerSchema, setDesignerSchema] = useState<FormSchema>();
const handleDesignerSave = async (schema: FormSchema) => {
console.log('💾 保存的表单 Schema:', JSON.stringify(schema, null, 2));
message.success('表单设计已保存!请查看控制台');
setPreviewSchema(schema); // 保存后更新预览
// 实际项目中这里会调用后端 API 保存
// await formSchemaService.save(schema);
};
// ==================== 示例 1.5: 工作流表单设计器(带审批字段) ====================
const [workflowDesignerSchema, setWorkflowDesignerSchema] = useState<FormSchema>();
const handleWorkflowDesignerSave = async (schema: FormSchema) => {
console.log('🔄 保存的工作流表单 Schema:', JSON.stringify(schema, null, 2));
message.success('工作流表单设计已保存!请查看控制台');
setWorkflowPreviewSchema(schema);
// 实际项目中这里会调用后端 API 保存
// await workflowFormService.save(schema);
};
const [workflowPreviewSchema, setWorkflowPreviewSchema] = useState<FormSchema>();
// ==================== 示例 2: 表单渲染器(静态) ====================
const [previewSchema, setPreviewSchema] = useState<FormSchema>();
const handleStaticFormSubmit = async (data: Record<string, any>) => {
console.log('📤 静态表单提交数据:', data);
message.success('表单提交成功!请查看控制台');
};
// ==================== 示例 3: 工作流表单(弹窗) ====================
const [workflowModalVisible, setWorkflowModalVisible] = useState(false);
const workflowFormSchema: FormSchema = {
version: '1.0',
formConfig: {
labelAlign: 'right',
size: 'middle',
formWidth: 600
},
fields: [
{
id: 'wf_field_1',
type: 'input',
label: '工作流名称',
name: 'workflowName',
placeholder: '请输入工作流名称',
required: true
},
{
id: 'wf_field_2',
type: 'select',
label: '目标环境',
name: 'environment',
placeholder: '请选择环境',
required: true,
dataSourceType: 'static',
options: [
{ label: '开发环境', value: 'dev' },
{ label: '测试环境', value: 'test' },
{ label: '生产环境', value: 'prod' }
]
},
{
id: 'wf_field_3',
type: 'textarea',
label: '执行说明',
name: 'description',
placeholder: '请输入执行说明',
rows: 4
},
],
};
const handleWorkflowSubmit = async (data: Record<string, any>) => {
console.log('🚀 工作流启动参数:', data);
message.success('工作流已启动!');
setWorkflowModalVisible(false);
};
// ==================== 示例 4: 只读模式 ====================
const readonlySchema: FormSchema = {
version: '1.0',
formConfig: {
labelAlign: 'right',
size: 'middle',
formWidth: 600
},
fields: [
{
id: 'ro_field_1',
type: 'input',
label: '申请人',
name: 'applicant'
},
{
id: 'ro_field_2',
type: 'date',
label: '申请日期',
name: 'applyDate'
},
{
id: 'ro_field_3',
type: 'select',
label: '审批状态',
name: 'approvalStatus',
dataSourceType: 'static',
options: [
{ label: '待审批', value: 'pending' },
{ label: '已通过', value: 'approved' },
{ label: '已拒绝', value: 'rejected' }
]
},
{
id: 'ro_field_4',
type: 'textarea',
label: '审批意见',
name: 'approvalComment'
},
],
};
const readonlyData = {
applicant: '张三',
applyDate: '2024-07-20',
approvalStatus: 'approved',
approvalComment: '同意申请,准予执行。',
};
// ==================== 示例 5: 复杂表单(带栅格布局) ====================
const complexFormSchema: FormSchema = {
version: '1.0',
formConfig: {
labelAlign: 'right',
size: 'middle',
formWidth: 800,
title: '用户信息登记表'
},
fields: [
{
id: 'grid_1',
type: 'grid',
label: '基本信息',
name: 'grid_basic',
columns: 2,
columnSpans: [12, 12],
gutter: 16,
children: [
[
{ id: 'f1', type: 'input', label: '姓名', name: 'name', placeholder: '请输入姓名', required: true },
{ id: 'f2', type: 'number', label: '年龄', name: 'age', placeholder: '请输入年龄', min: 1, max: 150 },
],
[
{ id: 'f3', type: 'radio', label: '性别', name: 'gender', dataSourceType: 'static', options: [{ label: '男', value: 'male' }, { label: '女', value: 'female' }] },
{ id: 'f4', type: 'date', label: '出生日期', name: 'birthday', placeholder: '请选择日期' },
],
],
},
{
id: 'grid_2',
type: 'grid',
label: '联系方式',
name: 'grid_contact',
columns: 2,
columnSpans: [12, 12],
gutter: 16,
children: [
[
{ id: 'f5', type: 'input', label: '手机号', name: 'phone', placeholder: '请输入手机号' },
{ id: 'f6', type: 'input', label: '邮箱', name: 'email', placeholder: '请输入邮箱' },
],
[
{ id: 'f7', type: 'input', label: '微信', name: 'wechat', placeholder: '请输入微信号' },
{ id: 'f8', type: 'input', label: 'QQ', name: 'qq', placeholder: '请输入QQ号' },
],
],
},
{
id: 'f9',
type: 'textarea',
label: '个人简介',
name: 'bio',
placeholder: '请输入个人简介',
rows: 4,
},
],
};
const handleComplexFormSubmit = async (data: Record<string, any>) => {
console.log('📋 复杂表单提交数据:', data);
message.success('表单提交成功!');
};
// ==================== 示例 6: 带生命周期钩子的表单 ====================
const lifecycleFormSchema: FormSchema = {
version: '1.0',
formConfig: {
labelAlign: 'right',
size: 'middle',
formWidth: 600
},
fields: [
{
id: 'lc_field_1',
type: 'input',
label: '用户名',
name: 'username',
placeholder: '请输入用户名',
required: true
},
{
id: 'lc_field_2',
type: 'input',
label: '邮箱',
name: 'email',
placeholder: '请输入邮箱',
required: true,
validationRules: [
{ type: 'email', message: '请输入有效的邮箱地址' }
]
},
{
id: 'lc_field_3',
type: 'select',
label: '用户角色',
name: 'role',
placeholder: '请选择角色',
required: true,
dataSourceType: 'static',
options: [
{ label: '管理员', value: 'admin' },
{ label: '普通用户', value: 'user' },
{ label: '访客', value: 'guest' }
]
},
],
};
const handleLifecycleBeforeSubmit = async (values: Record<string, any>) => {
console.log('🎣 beforeSubmit 钩子被调用:', values);
// 模拟数据处理
const processedValues = {
...values,
email: values.email?.toLowerCase(), // 邮箱转小写
submitTime: new Date().toISOString(), // 添加提交时间
};
message.info('正在处理表单数据...');
await new Promise(resolve => setTimeout(resolve, 500));
console.log('✅ beforeSubmit 处理完成,返回修改后的数据:', processedValues);
return processedValues;
};
const handleLifecycleSubmit = async (data: Record<string, any>) => {
console.log('📤 提交到服务器:', data);
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 1000));
const response = { id: Math.random(), success: true, data };
console.log('✅ 服务器响应:', response);
message.success('用户创建成功!');
return response;
};
const handleLifecycleAfterSubmit = async (response: any) => {
console.log('🎣 afterSubmit 钩子被调用,服务器响应:', response);
// 模拟后续操作(如跳转、刷新列表等)
await new Promise(resolve => setTimeout(resolve, 300));
message.success(`用户 ID: ${response.id.toFixed(0)} 已成功创建`);
console.log('✅ afterSubmit 完成,可以执行跳转或刷新操作');
};
const handleLifecycleError = async (error: any) => {
console.error('🎣 onError 钩子被调用:', error);
// 可以在这里做错误上报、记录等
message.error(`操作失败: ${error.message || '未知错误'}`);
};
// ==================== Tab 配置 ====================
const tabItems = [
{
key: '1',
label: '📝 示例 1: 普通表单设计器',
children: (
<Card>
<h2>FormDesigner</h2>
<p style={{ marginBottom: 16, color: '#666' }}>
/ JSON Schema
</p>
<Alert
message="核心组件列表"
description="此设计器仅包含核心表单组件(输入框、下拉框、日期等),不包含工作流扩展字段。"
type="info"
showIcon
style={{ marginBottom: 16 }}
/>
<Divider />
<FormDesigner
value={designerSchema}
onChange={setDesignerSchema}
onSave={handleDesignerSave}
/>
</Card>
),
},
{
key: '1.5',
label: '🔄 示例 1.5: 工作流表单设计器',
children: (
<Card>
<h2></h2>
<p style={{ marginBottom: 16, color: '#666' }}>
<code>extraComponents</code>
</p>
<Alert
message="🔌 插件式架构展示"
description={
<div>
<p> + </p>
<p> "高级字段""审批人"</p>
<p> 9</p>
<p> </p>
</div>
}
type="success"
showIcon
style={{ marginBottom: 16 }}
/>
<div style={{
padding: 12,
background: '#f5f5f5',
borderRadius: 4,
marginBottom: 16,
fontFamily: 'monospace',
fontSize: 12
}}>
<strong>使:</strong><br />
{`<FormDesigner`}<br />
{` value={schema}`}<br />
{` onChange={setSchema}`}<br />
{` extraComponents={WORKFLOW_COMPONENTS}`}<br />
{`/>`}
</div>
<Divider />
<FormDesigner
value={workflowDesignerSchema}
onChange={setWorkflowDesignerSchema}
onSave={handleWorkflowDesignerSave}
extraComponents={WORKFLOW_COMPONENTS}
/>
</Card>
),
},
{
key: '2',
label: '🎨 示例 2: 普通表单渲染',
children: (
<Card>
<h2></h2>
<p style={{ marginBottom: 16, color: '#666' }}>
Schema
</p>
<Divider />
<Alert
message="✅ 正确用法"
description="FormRenderer 现在是非受控组件,不需要传递 value 和 onChange。表单内部自动管理状态onSubmit 会返回完整数据。"
type="success"
showIcon
style={{ marginBottom: 16 }}
/>
{previewSchema ? (
<FormRenderer
schema={previewSchema}
onSubmit={handleStaticFormSubmit}
showSubmit
showCancel
/>
) : (
<div style={{ padding: 40, textAlign: 'center', color: '#999' }}>
"示例 1"
</div>
)}
</Card>
),
},
{
key: '2.5',
label: '🔄 示例 2.5: 工作流表单渲染',
children: (
<Card>
<h2></h2>
<p style={{ marginBottom: 16, color: '#666' }}>
<code>extraComponents</code>
</p>
<Alert
message="重要提示"
description="使用 FormRenderer 渲染包含扩展字段的表单时,必须传递 extraComponents={WORKFLOW_COMPONENTS},否则扩展字段无法正确渲染。"
type="warning"
showIcon
style={{ marginBottom: 16 }}
/>
<Alert
message="✅ 下拉框可选中"
description="FormRenderer 现已修复,下拉框可以正常选中。不需要传递 value 和 onChange表单内部自动管理状态。"
type="success"
showIcon
style={{ marginBottom: 16 }}
/>
<Divider />
{workflowPreviewSchema ? (
<FormRenderer
schema={workflowPreviewSchema}
extraComponents={WORKFLOW_COMPONENTS}
onSubmit={async (data) => {
console.log('🔄 工作流表单提交数据:', data);
message.success('工作流表单提交成功!');
}}
showSubmit
showCancel
/>
) : (
<div style={{ padding: 40, textAlign: 'center', color: '#999' }}>
"示例 1.5"
</div>
)}
</Card>
),
},
{
key: '3',
label: '🚀 示例 3: 工作流表单',
children: (
<Card>
<h2></h2>
<p style={{ marginBottom: 16, color: '#666' }}>
Modal 使
</p>
<Divider />
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<div>
<h3>使</h3>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<Button type="primary" size="large" onClick={() => setWorkflowModalVisible(true)}>
</Button>
</Space>
<Modal
title="启动工作流"
open={workflowModalVisible}
onCancel={() => setWorkflowModalVisible(false)}
footer={null}
width={workflowFormSchema.formConfig.formWidth || 600}
>
<FormRenderer
schema={workflowFormSchema}
onSubmit={handleWorkflowSubmit}
onCancel={() => setWorkflowModalVisible(false)}
showCancel
submitText="启动"
cancelText="取消"
/>
</Modal>
</Card>
),
},
{
key: '4',
label: '👁️ 示例 4: 只读模式',
children: (
<Card>
<h2></h2>
<p style={{ marginBottom: 16, color: '#666' }}>
</p>
<Divider />
<div>
<h3>使</h3>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<Divider />
<FormRenderer
schema={readonlySchema}
value={readonlyData}
readonly={true}
/>
</Card>
),
},
{
key: '5',
label: '📋 示例 5: 复杂表单',
children: (
<Card>
<h2></h2>
<p style={{ marginBottom: 16, color: '#666' }}>
</p>
<Divider />
<FormRenderer
schema={complexFormSchema}
onSubmit={handleComplexFormSubmit}
showSubmit
showCancel
/>
</Card>
),
},
{
key: '6',
label: '🎣 示例 6: 生命周期钩子',
children: (
<Card>
<h2>beforeSubmit / afterSubmit / onError</h2>
<p style={{ marginBottom: 16, color: '#666' }}>
</p>
<Alert
message="🎣 生命周期钩子"
description={
<div>
<p><strong>beforeSubmit</strong>: false</p>
<p><strong>onSubmit</strong>: </p>
<p><strong>afterSubmit</strong>: </p>
<p><strong>onError</strong>: </p>
</div>
}
type="info"
showIcon
style={{ marginBottom: 16 }}
/>
<div style={{
padding: 12,
background: '#f5f5f5',
borderRadius: 4,
marginBottom: 16,
fontFamily: 'monospace',
fontSize: 12
}}>
<strong>使:</strong><br />
{`<FormRenderer`}<br />
{` schema={schema}`}<br />
{` beforeSubmit={async (values) => {`}<br />
{` // 数据预处理`}<br />
{` return modifiedValues;`}<br />
{` }}`}<br />
{` onSubmit={async (data) => {`}<br />
{` // 提交到服务器`}<br />
{` return response;`}<br />
{` }}`}<br />
{` afterSubmit={async (response) => {`}<br />
{` // 提交后处理`}<br />
{` }}`}<br />
{` onError={async (error) => {`}<br />
{` // 错误处理`}<br />
{` }}`}<br />
{`/>`}
</div>
<Divider />
<FormRenderer
schema={lifecycleFormSchema}
beforeSubmit={handleLifecycleBeforeSubmit}
onSubmit={handleLifecycleSubmit}
afterSubmit={handleLifecycleAfterSubmit}
onError={handleLifecycleError}
showSubmit
showCancel
/>
<Alert
message="📝 提示"
description="打开浏览器控制台查看完整的钩子执行流程,包括 beforeSubmit、onSubmit、afterSubmit 的详细日志输出。"
type="success"
showIcon
style={{ marginTop: 16 }}
/>
</Card>
),
},
];
return (
<div style={{ padding: 24 }}>
<div style={{ marginBottom: 24 }}>
<h1>FormDesigner 使</h1>
<p style={{ color: '#666', fontSize: 14 }}>
使 <code>@/components/FormDesigner</code> <strong>FormDesigner</strong> <strong>FormRenderer</strong>
</p>
</div>
<Tabs defaultActiveKey="1" items={tabItems} />
</div>
);
};
export default FormDesignerExamplesPage;