deploy-ease-platform/frontend/src/components/FormDesigner/FORM_PREVIEW_GUIDE.md
2025-10-25 00:20:19 +08:00

8.5 KiB
Raw Blame History

FormPreview 使用指南

📖 概述

FormPreview 是一个独立的表单预览组件,从 FormDesigner 中提取出来,可在任何地方使用。它支持完整的表单交互、验证和联动规则。

特点

  • 完全独立:可在任何页面使用,不依赖 FormDesigner
  • 支持所有字段类型input, select, textarea, date, grid, cascader 等
  • 支持验证规则required, pattern, min/max, email, phone 等
  • 支持联动规则:显示/隐藏、禁用/启用、必填控制、值联动
  • 支持动态数据源API 数据源、预定义数据源
  • 非受控组件:内部管理状态,无冲突
  • 简单易用:只需传入 fieldsformConfig

📦 导入

import { FormPreview, FormPreviewRef } from '@/components/FormDesigner';
import type { FieldConfig, FormConfig } from '@/components/FormDesigner';

🚀 基础用法

1. 最简单的使用

import React, { useRef } from 'react';
import { FormPreview, FormPreviewRef } from '@/components/FormDesigner';

const MyComponent = () => {
  const previewRef = useRef<FormPreviewRef>(null);
  
  // 从后端获取表单定义
  const formSchema = await getFormDefinitionById(id);
  
  return (
    <FormPreview 
      ref={previewRef}
      fields={formSchema.fields}
      formConfig={formSchema.formConfig}
    />
  );
};

2. 在 Modal 中使用

import React from 'react';
import { Modal } from 'antd';
import { FormPreview } from '@/components/FormDesigner';

const FormPreviewModal = ({ open, onClose, formDefinition }) => {
  if (!formDefinition) return null;

  const { schema } = formDefinition;

  return (
    <Modal
      title={`预览表单:${formDefinition.name}`}
      open={open}
      onCancel={onClose}
      footer={null}
      width={schema.formConfig.formWidth || 600}
      styles={{
        body: {
          maxHeight: '70vh',
          overflowY: 'auto',
          padding: 0,
        }
      }}
    >
      <FormPreview
        fields={schema.fields}
        formConfig={schema.formConfig}
      />
    </Modal>
  );
};

3. 使用 Ref 方法

import React, { useRef } from 'react';
import { Button } from 'antd';
import { FormPreview, FormPreviewRef } from '@/components/FormDesigner';

const MyComponent = () => {
  const previewRef = useRef<FormPreviewRef>(null);
  
  const handleSubmit = async () => {
    // 触发表单验证和提交
    await previewRef.current?.submit();
  };
  
  const handleReset = () => {
    // 重置表单
    previewRef.current?.reset();
  };
  
  return (
    <div>
      <FormPreview 
        ref={previewRef}
        fields={fields}
        formConfig={formConfig}
      />
      
      <div style={{ marginTop: 16 }}>
        <Button type="primary" onClick={handleSubmit}>提交</Button>
        <Button onClick={handleReset} style={{ marginLeft: 8 }}>重置</Button>
      </div>
    </div>
  );
};

📝 Props 说明

FormPreviewProps

属性 类型 必填 说明
fields FieldConfig[] 字段列表
formConfig FormConfig 表单配置

FormPreviewRef 方法

方法 参数 返回值 说明
submit() Promise<void> 提交表单(触发验证)
reset() void 重置表单

🔧 数据格式

FormConfig 示例

const formConfig: FormConfig = {
  title: "部署申请表单",         // 表单标题(可选)
  formWidth: 600,                // 表单弹窗宽度单位px
  labelAlign: "right",           // 标签对齐left | right | top
  size: "middle"                 // 表单尺寸small | middle | large
};

Fields 示例

const fields: FieldConfig[] = [
  // 基础输入框
  {
    id: "field_1",
    type: "input",
    label: "应用名称",
    name: "appName",
    placeholder: "请输入应用名称",
    required: true,
    validationRules: [
      { type: "required", message: "应用名称不能为空" },
      { type: "maxLength", value: 50, message: "不能超过50个字符" }
    ]
  },
  
  // 下拉选择(静态选项)
  {
    id: "field_2",
    type: "select",
    label: "环境选择",
    name: "environment",
    required: true,
    dataSourceType: "static",
    options: [
      { label: "开发环境", value: "dev" },
      { label: "测试环境", value: "test" },
      { label: "生产环境", value: "prod" }
    ]
  },
  
  // 下拉选择API 数据源)
  {
    id: "field_3",
    type: "select",
    label: "Jenkins服务器",
    name: "jenkinsServer",
    required: true,
    dataSourceType: "api",
    apiDataSource: {
      url: "/api/v1/jenkins-servers/list",
      method: "GET",
      params: { enabled: true },
      labelField: "name",
      valueField: "id"
    }
  },
  
  // 下拉选择(预定义数据源)
  {
    id: "field_4",
    type: "select",
    label: "项目组",
    name: "projectGroup",
    required: true,
    dataSourceType: "predefined",
    predefinedDataSource: {
      sourceType: "PROJECT_GROUPS"
    }
  },
  
  // 栅格布局
  {
    id: "field_5",
    type: "grid",
    label: "基础信息",
    name: "grid_basic",
    columns: 2,
    columnSpans: [12, 12],
    gutter: 16,
    children: [
      // 第一列
      [
        {
          id: "field_6",
          type: "input",
          label: "创建人",
          name: "creator",
          required: true
        }
      ],
      // 第二列
      [
        {
          id: "field_7",
          type: "date",
          label: "创建日期",
          name: "createDate",
          required: true
        }
      ]
    ]
  }
];

🌟 实际应用场景

场景1工作流设计页面预览启动表单

// src/pages/Workflow/Design/components/FormPreviewModal.tsx
import { FormPreview } from '@/components/FormDesigner';

const FormPreviewModal = ({ formDefinition, open, onClose }) => {
  const { schema } = formDefinition;
  
  return (
    <Modal open={open} onCancel={onClose} width={schema.formConfig.formWidth}>
      <FormPreview 
        fields={schema.fields}
        formConfig={schema.formConfig}
      />
    </Modal>
  );
};

场景2表单设计器内部预览

// src/components/FormDesigner/Designer.tsx
const FormDesigner = () => {
  const [previewVisible, setPreviewVisible] = useState(false);
  const [fields, setFields] = useState<FieldConfig[]>([]);
  const [formConfig, setFormConfig] = useState<FormConfig>({});
  
  return (
    <>
      <Button onClick={() => setPreviewVisible(true)}>预览</Button>
      
      <Modal open={previewVisible} onCancel={() => setPreviewVisible(false)}>
        <FormPreview fields={fields} formConfig={formConfig} />
      </Modal>
    </>
  );
};

场景3独立的表单预览页面

// src/pages/Form/Preview/index.tsx
import { useParams } from 'react-router-dom';
import { FormPreview } from '@/components/FormDesigner';

const FormPreviewPage = () => {
  const { id } = useParams();
  const [formSchema, setFormSchema] = useState(null);
  
  useEffect(() => {
    const loadForm = async () => {
      const data = await getFormDefinitionById(id);
      setFormSchema(data.schema);
    };
    loadForm();
  }, [id]);
  
  if (!formSchema) return <div>加载中...</div>;
  
  return (
    <div className="container">
      <h1>表单预览</h1>
      <FormPreview 
        fields={formSchema.fields}
        formConfig={formSchema.formConfig}
      />
    </div>
  );
};

⚠️ 重要说明

1. 非受控组件

FormPreview非受控组件,内部通过 Ant Design Form 管理状态:

  • 不要传递 valueonChange(会导致冲突)
  • 不要尝试从外部控制字段值
  • 使用 ref 方法获取表单值或提交表单

2. 数据格式统一

无论在哪里使用,数据格式都是一致的:

<FormPreview 
  fields={formSchema.fields}     // FieldConfig[]
  formConfig={formSchema.formConfig}  // FormConfig
/>

3. 与 FormRenderer 的区别

特性 FormPreview FormRenderer
推荐使用 已废弃
受控模式 非受控 ⚠️ 部分受控(有问题)
下拉框可选中 正常 有bug
使用场景 预览、交互 已废弃

建议:所有新代码都使用 FormPreview,不要再使用 FormRenderer

📚 相关文档