前言
现在的后台的项目大同小异,form + Table。写法也是很多种。要么封装太厚,要么联动不好用,要不ts类型不齐全。因为后台表单场景还是千奇百怪的。用别人的未必通用 遇到问题没法调整,所以最好是自己实现 方便修改。这里就用最简单的代码实现下。
首先 现在大家一般用的方式有
- 1.按照antd的一把嗦。灵活但麻烦
tsx
<Form.Item label='xx' name='xxx'>
<Input/>
</Form.Item>
-
2.formity
- 动态表单每一个动态应该都需要state控制并且传进去
- 其次ts类型的支持的很差。
- 没有特别清晰的文档
-
3.ProComponents Avue 等 JSON多多少少有点小问题
- ProComponents 的 SchemaForm 和编辑表格都有很多问题
- Avue 没有具体研究过。但是vue没有一点ts提示,其他缺点应该也差不多
然后看了ProComponents
的 SchemaForm.shouldUpdate
控制是否渲染是在最外层的。但是何必自己实现呢。我只需要封装一个自定义的shouldUpdate
的表单类型即可, 每个表单都可以单独写是否渲染的条件。
tsx
<BetaSchemaForm<DataItem>
shouldUpdate={(newValues, oldValues) => {
if (newValues.title !== oldValues?.title) {
return true;
}
return false;
}}
layoutType="Form"
columns={columns}
/>
因此想象中是这样使用的
tsx
{
type: 'update',
col: 24,
itemProps: {
noStyle: true,
shouldUpdate: (pre, cru) => {
return !_.isEqual(pre?.xxx, cru?.xxx);
},
next: (values, form) => {
const currentValues = values?.activity;
if (currentValues?.busiType == '2') {
return [
{
name: 'custName',
label: '公司',
type: 'input',
col: 12,
allowClear: true,
placeholder: '请输入',
rules: [{ required: true }],
itemProps: ITEM_PROPS,
},
];
}
return false;
},
},
}
所以就需要设计一个update
表单类型。
tsx
<Form.Item noStyle shouldUpdate={()=>xxx}>
{form => {
const values = form.getFieldsValue()
const result = next(values, form)
if(!result) return null;
return renderFormItem(result)
}}
</Form.Item>
- 借助
Antd.Form.Item.ShouldUpdate
进行实现动态控制是否渲染 - 通过
next
传入当前所有表单的值 及form实例 - 如果返回false 则不展示内容
- 如果返回了数组 就调用JSON渲染表单函数渲染
实现
TODO迁移
最终效果


tsx
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Col, Form, Input, Row, Switch } from 'antd';
import dayjs from 'dayjs';
import { ISearchesType, renderFormItem } from 'ims-view-pc';
import React, { Fragment } from 'react';
interface Option {
value: string;
label: string;
children?: Option[];
disabled?: boolean;
}
const dict = [
{ label: '1', value: 1 },
{ label: '2', value: 2 },
{ label: '3', value: 3 },
] as const;
const options: Option[] = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
{
value: 'xiasha',
label: 'Xia Sha',
disabled: true,
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua men',
},
],
},
],
},
];
const mentionsOptions = [
{
value: 'afc163',
label: 'afc163',
},
{
value: 'zombieJ',
label: 'zombieJ',
},
{
value: 'yesmeck',
label: 'yesmeck',
},
];
interface IRecord {
cascader: string[];
mentions: string;
input: string;
radio: (typeof dict)[number]['value'];
editor: string;
custom: any;
update: AudioNode;
updateInput: string;
time: dayjs.Dayjs;
date: dayjs.Dayjs;
week: dayjs.Dayjs;
month: dayjs.Dayjs;
quarter: dayjs.Dayjs;
year: dayjs.Dayjs;
timeRange: dayjs.Dayjs[];
dateRange: dayjs.Dayjs[];
weekRange: dayjs.Dayjs[];
monthRange: dayjs.Dayjs[];
quarterRange: dayjs.Dayjs[];
yearRange: dayjs.Dayjs[];
}
export default () => {
const [form] = Form.useForm();
const formList: ISearchesType<IRecord, { customParams?: '2' }> = [
{
name: 'cascader',
label: 'cascader',
type: 'cascader',
dict: options as any[],
},
{
name: 'mentions',
label: 'mentions',
type: 'mentions',
initialValue: '@afc163',
controlProps: {},
fetchConfig: {
request(params) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(mentionsOptions);
}, 2000);
});
},
},
},
{
name: 'time',
label: 'time',
type: 'time',
},
{
name: 'date',
label: 'date',
type: 'date',
},
{
name: 'week',
label: 'week',
type: 'week',
},
{
name: 'month',
label: 'month',
type: 'month',
},
{
name: 'quarter',
label: 'quarter',
type: 'quarter',
},
{
name: 'year',
label: 'year',
type: 'year',
},
{
name: 'timeRange',
label: 'timeRange',
type: 'timeRange',
},
{
name: 'dateRange',
label: 'dateRange',
type: 'dateRange',
itemProps: {
rules: [{ required: true }],
},
},
{
name: 'weekRange',
label: 'weekRange',
type: 'weekRange',
},
{
name: 'monthRange',
label: 'monthRange',
type: 'monthRange',
},
{
name: 'quarterRange',
label: 'quarterRange',
type: 'quarterRange',
},
{
name: 'yearRange',
label: 'yearRange',
type: 'yearRange',
},
{
name: 'input',
label: 'input',
type: 'input',
initialValue: 'input',
customParams: '2',
},
{
name: 'radio',
label: 'radio',
type: 'radio',
dict,
itemProps: {
extra: '当值为3时 显示update表单',
},
controlProps: {
buttonStyle: 'solid',
},
initialValue: 1,
},
{
name: 'updateInput',
label: 'update',
type: 'update',
itemProps: {
noStyle: true,
shouldUpdate: (pre, cru) => pre.radio != cru.radio,
next: (values, form) => {
if (values?.radio != 3) return false;
return [
{
label: 'update',
name: 'updateInput',
type: 'input',
},
];
},
},
},
{
name: 'editor',
label: 'editor',
type: 'editor',
initialValue: '<h1>editor</h1>',
},
{
name: 'custom',
label: 'custom',
type: 'custom',
initialValue: true,
Component: (props) => {
return (
<div>
<Switch checked={!!props?.value} onChange={(e) => props.onChange(!props?.value)} />
<div>{!!props?.value ? 'light' : 'dark'}</div>
</div>
);
},
},
{
label: 'list',
type: 'custom',
itemProps: {},
Component(props) {
return (
<Form.List
initialValue={[{ name: 1 }]}
name="names"
rules={[
{
validator: async (_, names) => {
if (!names || names.length < 1) {
return Promise.reject(new Error('At least 1 passengers'));
}
},
},
]}
>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Form.Item noStyle key={field.key}>
<Row gutter={16}>
<Col>
<Form.Item name={[field.name, 'name']}>
<Input placeholder="passenger name" />
</Form.Item>
</Col>
<Col>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Col>
</Row>
</Form.Item>
))}
<Form.Item noStyle>
<Button type="dashed" onClick={() => add()} icon={<PlusOutlined />}>
Add field
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</>
)}
</Form.List>
);
},
},
];
return (
<Form
scrollToFirstError
labelAlign="right"
labelCol={{ span: 3 }}
wrapperCol={{ span: 21 }}
style={{ overflow: 'auto' }}
form={form}
onFinish={(value) => console.log(value)}
>
{formList.map((item, index) => (
<Fragment key={index}>{renderFormItem({ ...item, form })}</Fragment>
))}
<div>
<Button type="primary" htmlType="submit">
submit
</Button>
</div>
</Form>
);
};