注册表单增加字段

This commit is contained in:
linxd
2025-07-15 10:02:33 +08:00
parent 9a3d99a8de
commit 85fddd0501
4 changed files with 319 additions and 166 deletions

View File

@ -74,6 +74,10 @@ const SupplierRegister: React.FC<supplierWithInputProps> = (props) => {
});
};
const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo);
};
const onFinish = async (values: any) => {
setLoading(true);
try {
@ -146,6 +150,7 @@ const SupplierRegister: React.FC<supplierWithInputProps> = (props) => {
name="supplier_register"
className="register-form"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
layout="horizontal"
labelAlign="right"
size="large"

View File

@ -21,6 +21,8 @@ import { UploadOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons'
import { message } from 'antd';
import { validateFileSize } from '@/utils/utils';
import { getRegionTree, getregionInternational } from '@/servers/api/register';
import { getDictList } from '@/servers/api/dicts';
import type { DictItem } from '@/servers/api/dicts';
const { Option } = Select;
@ -45,6 +47,48 @@ function convertToCascaderOptions(data: any[]): any[] {
* 包含资质证书类型、名称、编号、等级、发证机构、发证日期、有效期等
*/
export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }) => {
// 检查行是否有任何字段被填写
const hasRowValue = (formInstance: any, recordObj: any, currentField: string) => {
const values = formInstance.getFieldsValue(['coscoSupplierQualifications']);
if (!values.coscoSupplierQualifications || !values.coscoSupplierQualifications[recordObj.name]) {
return false;
}
const rowData = values.coscoSupplierQualifications[recordObj.name];
const fields = ['certType', 'certName', 'certNumber', 'certLevel', 'issuingAuthority', 'dateTime', 'termOfValidity', 'certFile'];
// 过滤掉当前正在验证的字段
return fields.filter(field => field !== currentField).some(field => {
const value = rowData[field];
if (field === 'certFile' && value) {
return value.fileList && value.fileList.length > 0;
}
return value !== undefined && value !== null && value !== '';
});
};
// 生成条件性验证规则
const createConditionalRule = (fieldName: string, errorMsg: string) => ({
validator: (_: any, value: any, callback: any) => {
const record = _.field.split('.')[1];
// 如果当前字段有值,或者行中有任何字段有值,则该字段为必填
if (value || hasRowValue(form, { name: record }, fieldName)) {
if (!value) {
return Promise.reject(new Error(errorMsg));
}
}
return Promise.resolve();
},
});
// 创建带必填标识的表头
const createRequiredTitle = (title: string) => (
<span>
{title} <span style={{ color: '#ff4d4f' }}>*</span>
</span>
);
return (
<>
<div className="form-section-title"></div>
@ -70,80 +114,77 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
render: (_, __, index) => index + 1,
},
{
title: '资质证书类型',
title: createRequiredTitle('资质证书类型'),
dataIndex: 'certType',
render: (_, record) => (
<Form.Item
name={[record.name, 'certType']}
noStyle
rules={[{ required: true, message: '请选择资质证书类型' }]}
rules={[createConditionalRule('certType', '请输入资质证书类型')]}
style={{ margin: 0 }}
>
<Select placeholder="请选择类型" style={{ width: '100%' }}>
<Option value="机构资质"></Option>
<Option value="CMMI资质等级">CMMI资质等级</Option>
<Option value="质量体系认证"></Option>
<Option value="环境管理体系认证"></Option>
<Option value="行业资质"></Option>
</Select>
<Input placeholder="请输入资质证书类型" />
</Form.Item>
),
},
{
title: '资质名称',
title: createRequiredTitle('资质名称'),
dataIndex: 'certName',
render: (_, record) => (
<Form.Item
name={[record.name, 'certName']}
noStyle
rules={[{ required: true, message: '请输入资质名称' }]}
rules={[createConditionalRule('certName', '请输入资质名称')]}
style={{ margin: 0 }}
>
<Input placeholder="请输入资质名称" />
</Form.Item>
),
},
{
title: '资质证书编号',
title: createRequiredTitle('资质证书编号'),
dataIndex: 'certNumber',
render: (_, record) => (
<Form.Item
name={[record.name, 'certNumber']}
noStyle
rules={[{ required: true, message: '请输入资质证书编号' }]}
rules={[createConditionalRule('certNumber', '请输入资质证书编号')]}
style={{ margin: 0 }}
>
<Input placeholder="请输入证书编号" />
</Form.Item>
),
},
{
title: '资质类别和等级',
title: createRequiredTitle('资质类别和等级'),
dataIndex: 'certLevel',
render: (_, record) => (
<Form.Item name={[record.name, 'certLevel']} noStyle>
<Form.Item
name={[record.name, 'certLevel']}
style={{ margin: 0 }}
>
<Input placeholder="请输入资质类别和等级" />
</Form.Item>
),
},
{
title: '发证机构',
title: createRequiredTitle('发证机构'),
dataIndex: 'issuingAuthority',
render: (_, record) => (
<Form.Item
name={[record.name, 'issuingAuthority']}
noStyle
rules={[{ required: true, message: '请输入发证机构' }]}
rules={[createConditionalRule('issuingAuthority', '请输入发证机构')]}
style={{ margin: 0 }}
>
<Input placeholder="请输入发证机构" />
</Form.Item>
),
},
{
title: '发证日期',
title: createRequiredTitle('发证日期'),
dataIndex: 'issueDate',
render: (_, record) => (
<Form.Item
name={[record.name, 'dateTime']}
noStyle
rules={[{ required: true, message: '请选择发证日期' }]}
rules={[createConditionalRule('dateTime', '请选择发证日期')]}
style={{ margin: 0 }}
>
<DatePicker
placeholder="年/月/日"
@ -154,13 +195,13 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
),
},
{
title: '资质有效期至',
title: createRequiredTitle('资质有效期至'),
dataIndex: 'expiryDate',
render: (_, record) => (
<Form.Item
name={[record.name, 'termOfValidity']}
noStyle
rules={[{ required: true, message: '请选择资质有效期' }]}
rules={[createConditionalRule('termOfValidity', '请选择资质有效期')]}
style={{ margin: 0 }}
>
<DatePicker
placeholder="年/月/日"
@ -171,13 +212,13 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
),
},
{
title: '附件',
title: createRequiredTitle('附件'),
dataIndex: 'certFile',
render: (_, record) => (
<Form.Item
name={[record.name, 'certFile']}
noStyle
rules={[{ required: true, message: '请上传资质证书附件' }]}
rules={[createConditionalRule('certFile', '请上传资质证书附件')]}
style={{ margin: 0 }}
>
<Upload name="certFile" action="/api/upload" listType="text" maxCount={1}>
<Button type="link" size="small">
@ -296,11 +337,19 @@ export const InvoiceSection: React.FC<CommonFormSectionsProps> = ({ form }) => {
/**
* 银行账户表单部分
* 包含开户银行、账户名称、账号、所在地区等
* supplierType dvs 境内 ovs 境外
*/
export const BankAccountSection: React.FC<CommonFormSectionsProps> = ({ form, supplierType }) => {
// 地区
const [addressOptions, setAddressOptions] = useState<API.RegionOption[]>([]);
const [currencyOptions, setCurrencyOptions] = useState<DictItem[]>([]);
useEffect(() => {
getDictList('currency').then(res => {
if (res.code === 200) {
setCurrencyOptions(res.data);
}
});
}, []);
useEffect(() => {
if (supplierType) {
const submitInterface = supplierType === 'dvs' ? getRegionTree : getregionInternational;
@ -312,120 +361,206 @@ export const BankAccountSection: React.FC<CommonFormSectionsProps> = ({ form, su
}
}, [supplierType]);
// 定义表格记录和表格列类型
interface TableRecord {
name: number;
key: React.Key;
fieldKey: number;
[key: string]: any;
}
interface ColumnType {
title: string;
dataIndex?: string;
width?: number;
render?: (text: any, record: TableRecord, index?: number) => React.ReactNode;
}
return (
<>
<div className="form-section-title"></div>
<Form.List name="coscoSupplierBank">
{(fields, { add, remove }) => (
<>
<Table
dataSource={fields.map((field) => ({
...field,
key: field.key,
fieldKey: field.name,
}))}
pagination={false}
bordered
size="middle"
rowKey="key"
columns={[
{
title: '序号',
dataIndex: 'name',
width: 60,
render: (_, __, index) => index + 1,
},
{
title: '开户银行',
dataIndex: 'bankName',
render: (_, record) => (
<Form.Item
name={[record.name, 'bankName']}
noStyle
rules={[{ required: true, message: '请输入开户银行' }]}
>
<Input placeholder="请输入开户银行" />
</Form.Item>
),
},
{
title: '账户名称',
dataIndex: 'accountName',
render: (_, record) => (
<Form.Item
name={[record.name, 'accountName']}
noStyle
rules={[{ required: true, message: '请输入账户名称' }]}
>
<Input placeholder="请输入账户名称" />
</Form.Item>
),
},
{
title: '账号',
dataIndex: 'accountNumber',
render: (_, record) => (
<Form.Item
name={[record.name, 'accountNumber']}
noStyle
rules={[{ required: true, message: '请输入账号' }]}
>
<Input placeholder="请输入账号" />
</Form.Item>
),
},
{
title: '地址',
dataIndex: 'location',
render: (_, record) => (
<Form.Item
name={[record.name, 'address']}
noStyle
rules={[{ required: true, message: '请选择地址' }]}
>
<Cascader
options={addressOptions}
placeholder="请选择地址"
showSearch={{
filter: (inputValue, path) => {
return path.some((option) => {
if (typeof option.label === 'string') {
return (
option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
);
}
return false;
});
},
}}
/>
</Form.Item>
),
},
{
title: '操作',
width: 70,
render: (_, record) => (
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => remove(record.name)}
>
</Button>
),
},
]}
/>
<Form.Item style={{ marginTop: 16 }} wrapperCol={{ span: 24 }}>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
</Button>
</Form.Item>
</>
)}
{(fields, { add, remove }) => {
// 根据供应商类型定义表格列
const getColumns = () => {
// 基础列定义
const baseColumns: ColumnType[] = [
{
title: '序号',
dataIndex: 'name',
width: 60,
render: (text, record, index = 0) => index + 1,
}
];
// 境内企业特有列
const domesticColumns: ColumnType[] = supplierType === 'dvs' ? [
{
title: '银联号',
dataIndex: 'interbankNumber',
render: (text, record) => (
<Form.Item
name={[record.name, 'interbankNumber']}
noStyle
rules={[{ required: true, message: '请输入银联号' }]}
>
<Input placeholder="请输入银联号" />
</Form.Item>
),
}
] : [];
// 境外企业特有列
const foreignColumns: ColumnType[] = supplierType === 'ovs' ? [
{
title: 'SWIFT CODE',
dataIndex: 'swiftCode',
render: (text, record) => (
<Form.Item
name={[record.name, 'swiftCode']}
noStyle
rules={[{ required: true, message: '请输入SWIFT CODE' }]}
>
<Input placeholder="请输入SWIFT CODE" />
</Form.Item>
),
}
] : [];
// 通用列
const commonColumns: ColumnType[] = [
{
title: '开户银行',
dataIndex: 'bankName',
render: (text, record) => (
<Form.Item
name={[record.name, 'bankName']}
noStyle
rules={[{ required: true, message: '请输入开户银行' }]}
>
<Input placeholder="请输入开户银行" />
</Form.Item>
),
},
{
title: '账户名称',
dataIndex: 'accountName',
render: (text, record) => (
<Form.Item
name={[record.name, 'accountName']}
noStyle
rules={[{ required: true, message: '请输入账户名称' }]}
>
<Input placeholder="请输入账户名称" />
</Form.Item>
),
},
{
title: '账号',
dataIndex: 'accountNumber',
render: (text, record) => (
<Form.Item
name={[record.name, 'accountNumber']}
noStyle
rules={[{ required: true, message: '请输入账号' }]}
>
<Input placeholder="请输入账号" />
</Form.Item>
),
},
{
title: '币种',
dataIndex: 'currency',
width: 200,
render: (text, record) => (
<Form.Item
name={[record.name, 'currency']}
noStyle
rules={[{ required: true, message: '请选择币种' }]}
>
<Select
style={{ width: '100%' }}
placeholder="请选择币种"
options={currencyOptions.map(item => ({
label: item.dicName,
value: item.code,
}))}
/>
</Form.Item>
),
},
{
title: '地址',
dataIndex: 'location',
render: (text, record) => (
<Form.Item
name={[record.name, 'address']}
noStyle
rules={[{ required: true, message: '请选择地址' }]}
>
<Cascader
options={addressOptions}
placeholder="请选择地址"
showSearch={{
filter: (inputValue, path) => {
return path.some((option) => {
if (typeof option.label === 'string') {
return (
option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
);
}
return false;
});
},
}}
/>
</Form.Item>
),
},
{
title: '操作',
width: 70,
render: (text, record) => (
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => remove(record.name)}
>
</Button>
),
}
];
// 合并所有列
return [...baseColumns, ...domesticColumns, ...foreignColumns, ...commonColumns];
};
return (
<>
<Table
dataSource={fields.map((field) => ({
...field,
key: field.key,
fieldKey: field.name,
}))}
pagination={false}
bordered
size="middle"
rowKey="key"
columns={getColumns()}
/>
<Form.Item style={{ marginTop: 16 }} wrapperCol={{ span: 24 }}>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
</Button>
</Form.Item>
</>
);
}}
</Form.List>
</>
);
@ -646,7 +781,7 @@ export const AttachmentSection: React.FC<CommonFormSectionsProps> = ({ form }) =
]),
fileName: info.file.name,
fileType: info.file.type,
fileSize: info.file.size.toString(),
fileSize: info.file.size?.toString() || '0',
filePath: response.filePath || response.url,
},
],
@ -718,7 +853,7 @@ export const AttachmentSection: React.FC<CommonFormSectionsProps> = ({ form }) =
...fieldValue,
fileName: info.file.name,
fileType: info.file.type,
fileSize: info.file.size.toString(),
fileSize: info.file.size?.toString() || '0',
filePath: response.filePath || response.url,
},
],

View File

@ -1,13 +1,14 @@
/* 境内企业/机构 表单项 */
import React from 'react';
import { Form, Input, Button, Select, Upload, DatePicker, Row, Col, message } from 'antd';
import React, { useEffect, useState } from 'react';
import { Form, Input, Button, Select, Upload, DatePicker, Row, Col, message, Space } from 'antd';
import {
MobileOutlined,
MailOutlined,
EnvironmentOutlined,
UploadOutlined,
} from '@ant-design/icons';
import { getDictList } from '@/servers/api/dicts';
import type { DictItem } from '@/servers/api/dicts';
/**
* 引入通用表单组件
*/
@ -43,6 +44,15 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
handleGetCaptcha,
surveyQuestions,
}) => {
const [contactsTypeOptions, setContactsTypeOptions] = useState<DictItem[]>([]);
useEffect(() => {
// 从字典中 联系人身份类别contacts_type 获取数据
getDictList('contacts_type').then(res => {
if (res.code === 200) {
setContactsTypeOptions(res.data);
}
});
}, []);
return (
<>
<div className="form-section-title"></div>
@ -73,11 +83,7 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
label="营业执照有效期"
rules={[{ required: false, message: '请选择营业执照有效期' }]}
>
<DatePicker
placeholder="请选择日期"
style={{ width: '100%' }}
format="YYYY-MM-DD"
/>
<DatePicker placeholder="请选择日期" style={{ width: '100%' }} format="YYYY-MM-DD" />
</Form.Item>
</Col>
<Col span={8}>
@ -116,21 +122,16 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
label="注册资本"
rules={[{ required: true, message: '请输入注册资本' }]}
>
<Input
type="number"
placeholder="请输入金额"
addonBefore="人民币"
addonAfter="万元"
/>
<Input type="number" placeholder="请输入金额" addonBefore="人民币" addonAfter="万元" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="companyType"
label="企业性质"
rules={[{ required: true, message: '请选择企业性质' }]}
label="企业类别"
rules={[{ required: true, message: '请选择企业类别' }]}
>
<Select placeholder="请选择企业性质">
<Select placeholder="请选择企业类别">
<Option value="limited"></Option>
<Option value="joint"></Option>
<Option value="individual"></Option>
@ -138,7 +139,7 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
</Select>
</Form.Item>
</Col>
<Col span={8}>
{/* <Col span={8}>
<Form.Item
name="supplierType"
label="供应商类型"
@ -151,7 +152,7 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
<Option value="other">其他</Option>
</Select>
</Form.Item>
</Col>
</Col> */}
<Col span={8}>
<Form.Item name="parentCompanyInfo" label="母公司/出资人">
<Input placeholder="请输入母公司或出资人信息" />
@ -233,7 +234,7 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
</Row>
</Form.Item>
</Col>
<Col span={8}>
{/* <Col span={8}>
<Form.Item name="contactIdType" label="联系人身份类别">
<Select placeholder="请选择类型">
<Option value="idcard">居民身份证</Option>
@ -241,10 +242,21 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
<Option value="other">其他证件</Option>
</Select>
</Form.Item>
</Col>
</Col> */}
<Col span={8}>
<Form.Item name="contactIdNumber" label="联系人证件号码">
<Input placeholder="请填写联系人正确的身份证号" />
<Form.Item label="联系人证件号码" required >
<Space.Compact size='large'>
<Form.Item name="contactIdType" noStyle rules={[{ required: true, message: '请选择联系人证件类型' }]}>
<Select placeholder="请选择类型">
{contactsTypeOptions.map(item => (
<Option key={item.code} value={item.code}>{item.dicName}</Option>
))}
</Select>
</Form.Item>
<Form.Item name="contactIdNumber" noStyle rules={[{ required: true, message: '请填写联系人正确的身份证号' }]}>
<Input placeholder="请填写联系人正确的身份证号" />
</Form.Item>
</Space.Compact>
</Form.Item>
</Col>
<Col span={8}>
@ -269,7 +281,7 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
{/* 使用通用表单组件 */}
<QualificationSection form={form} />
<InvoiceSection form={form} />
<BankAccountSection form={form} supplierType={'dvs'} />
<BankAccountSection form={form} supplierType={'dvs'} />
<SurveySection form={form} surveyQuestions={surveyQuestions} />
<AttachmentSection form={form} />
</>