注册页面

This commit is contained in:
linxd
2025-06-16 19:33:33 +08:00
parent c7b9ffac10
commit 99650e3eda
7 changed files with 2160 additions and 349 deletions

View File

@ -75,16 +75,10 @@ export default {
// Expert registration text
"register.expert.title": "Expert Registration",
"register.expert.realName.label": "Real Name",
"register.expert.realName.placeholder": "Please enter real name",
"register.expert.realName.required": "Please enter real name",
"register.expert.idType.label": "ID Type",
"register.expert.idType.placeholder": "Please select ID type",
"register.expert.idType.required": "Please select ID type",
"register.expert.idCard.label": "ID Number",
"register.expert.idCard.placeholder": "Please enter ID number",
"register.expert.idCard.required": "Please enter ID number",
"register.expert.idCard.invalid": "Please enter a valid ID number",
"register.expert.specialty.label": "Specialty Field",
"register.expert.specialty.placeholder": "Please select specialty field",
"register.expert.specialty.required": "Please select specialty field"
"register.expert.idCard.required": "Please enter ID number"
};

View File

@ -75,16 +75,10 @@ export default {
// 专家注册文案
"register.expert.title": "专家注册",
"register.expert.realName.label": "真实姓名",
"register.expert.realName.placeholder": "请输入真实姓名",
"register.expert.realName.required": "请输入真实姓名",
"register.expert.idType.label": "证件类型",
"register.expert.idType.placeholder": "请选择证件类型",
"register.expert.idType.required": "请选择证件类型",
"register.expert.idCard.label": "证件号",
"register.expert.idCard.placeholder": "请输入证件号",
"register.expert.idCard.required": "请输入证件号",
"register.expert.idCard.invalid": "请输入有效的证件号",
"register.expert.specialty.label": "专业领域",
"register.expert.specialty.placeholder": "请选择专业领域",
"register.expert.specialty.required": "请选择专业领域"
"register.expert.idCard.required": "请输入证件号"
};

View File

@ -0,0 +1,949 @@
/* 境内企业/机构 表单项 */
import React from 'react';
import { Form, Input, Button, Select, Upload, DatePicker, Row, Col, Table, Cascader } from 'antd';
import {
MobileOutlined,
MailOutlined,
EnvironmentOutlined,
UploadOutlined,
PlusOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import { Radio } from 'antd';
import { message } from 'antd';
const { Option } = Select;
const { TextArea } = Input;
// 中国省市区级联数据
const addressOptions = [
{
value: '330000',
label: '浙江省',
children: [
{
value: '330100',
label: '杭州市',
children: [
{ value: '330102', label: '上城区' },
{ value: '330103', label: '下城区' },
{ value: '330104', label: '江干区' },
{ value: '330105', label: '拱墅区' },
{ value: '330106', label: '西湖区' },
{ value: '330108', label: '滨江区' },
],
},
{
value: '330200',
label: '宁波市',
children: [
{ value: '330203', label: '海曙区' },
{ value: '330205', label: '江北区' },
{ value: '330206', label: '北仑区' },
{ value: '330211', label: '镇海区' },
{ value: '330212', label: '鄞州区' },
],
},
],
},
{
value: '310000',
label: '上海市',
children: [
{
value: '310100',
label: '上海市',
children: [
{ value: '310101', label: '黄浦区' },
{ value: '310104', label: '徐汇区' },
{ value: '310105', label: '长宁区' },
{ value: '310106', label: '静安区' },
{ value: '310107', label: '普陀区' },
{ value: '310109', label: '虹口区' },
{ value: '310110', label: '杨浦区' },
{ value: '310112', label: '闵行区' },
],
},
],
},
{
value: '440000',
label: '广东省',
children: [
{
value: '440100',
label: '广州市',
children: [
{ value: '440103', label: '荔湾区' },
{ value: '440104', label: '越秀区' },
{ value: '440105', label: '海珠区' },
{ value: '440106', label: '天河区' },
{ value: '440111', label: '白云区' },
{ value: '440112', label: '黄埔区' },
],
},
{
value: '440300',
label: '深圳市',
children: [
{ value: '440303', label: '罗湖区' },
{ value: '440304', label: '福田区' },
{ value: '440305', label: '南山区' },
{ value: '440306', label: '宝安区' },
{ value: '440307', label: '龙岗区' },
{ value: '440308', label: '盐田区' },
],
},
],
},
{
value: '110000',
label: '北京市',
children: [
{
value: '110100',
label: '北京市',
children: [
{ value: '110101', label: '东城区' },
{ value: '110102', label: '西城区' },
{ value: '110105', label: '朝阳区' },
{ value: '110106', label: '丰台区' },
{ value: '110107', label: '石景山区' },
{ value: '110108', label: '海淀区' },
],
},
],
},
];
interface DomesticFormProps {
form: any;
countdown: number;
handleGetCaptcha: () => void;
}
const DomesticForm: React.FC<DomesticFormProps> = ({ form, countdown, handleGetCaptcha }) => {
return (
<>
<div className="form-section-title"></div>
{/* 营业执照附件和有效期 */}
<Row gutter={24}>
<Col span={8}>
<Form.Item
name="businessLicense"
label="营业执照附件"
rules={[{ required: true, message: '请上传营业执照附件' }]}
>
<Upload
name="businessLicense"
action="/api/upload"
listType="text"
maxCount={1}
beforeUpload={(file) => {
const isValidFormat =
file.type === 'application/pdf' ||
file.type === 'image/jpeg' ||
file.type === 'image/png';
if (!isValidFormat) {
message.error('只能上传PDF/JPG/PNG格式文件!');
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('文件大小不能超过10MB!');
}
return isValidFormat && isLt10M;
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="businessLicenseExpiry"
label="营业执照有效期"
rules={[{ required: false, message: '请选择营业执照有效期' }]}
>
<DatePicker placeholder="请选择日期" style={{ width: '100%' }} />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="companyName"
label="企业名称"
rules={[{ required: true, message: '请输入企业名称' }]}
>
<Input placeholder="请输入企业名称" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="socialCreditCode"
label="统一社会信用代码"
rules={[
{ required: true, message: '请输入统一社会信用代码' },
{ pattern: /^[0-9A-HJ-NPQRTUWXY]{18}$/, message: '请输入正确的统一社会信用代码' },
]}
>
<Input placeholder="请输入正确的统一社会信用代码" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="legalPerson"
label="企业法定代表人"
rules={[{ required: true, message: '请输入企业法定代表人/负责人' }]}
>
<Input placeholder="张三" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="registeredCapitalGroup"
label="注册资本"
rules={[{ required: true, message: '请输入注册资本' }]}
>
<Input.Group compact style={{ display: 'flex' }}>
<span
style={{
display: 'flex',
alignItems: 'center',
padding: '0 11px',
border: '1px solid #d9d9d9',
borderRight: 0,
backgroundColor: '#f5f5f5',
borderRadius: '2px 0 0 2px',
}}
>
</span>
<Form.Item
name="registeredCapital"
noStyle
rules={[{ required: true, message: '请输入注册资本金额' }]}
>
<Input
type="number"
placeholder="请输入金额"
style={{ flex: 1, borderRadius: '0' }}
/>
</Form.Item>
<span
style={{
display: 'flex',
alignItems: 'center',
padding: '0 11px',
border: '1px solid #d9d9d9',
borderLeft: 0,
backgroundColor: '#f5f5f5',
borderRadius: '0 2px 2px 0',
}}
>
</span>
</Input.Group>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="companyType"
label="企业性质"
rules={[{ required: true, message: '请选择企业性质' }]}
>
<Select placeholder="请选择企业性质">
<Option value="limited"></Option>
<Option value="joint"></Option>
<Option value="individual"></Option>
<Option value="other"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="supplierType"
label="供应商类型"
rules={[{ required: true, message: '请选择供应商类型' }]}
>
<Select placeholder="请选择类型">
<Option value="manufacturer"></Option>
<Option value="agent"></Option>
<Option value="service"></Option>
<Option value="other"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="parentCompanyInfo" label="母公司/出资人">
<Input placeholder="请输入母公司或出资人信息" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="registeredAddress"
label="注册地址"
rules={[{ required: true, message: '请输入注册地址' }]}
>
<Input prefix={<EnvironmentOutlined />} placeholder="上海市普陀区XX路1888号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="officeAddress" label="办公地址">
<Input
prefix={<EnvironmentOutlined />}
placeholder="请具体注明省、市、区、路、门牌号"
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name="businessScope"
label="经营范围"
rules={[{ required: true, message: '请输入经营范围' }]}
labelCol={{ span: 2 }}
wrapperCol={{ span: 21 }}
>
<TextArea
placeholder="金融专用产品、广告传媒"
rows={2}
maxLength={200}
showCount
style={{ resize: 'none' }}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactPerson"
label="联系人姓名"
rules={[{ required: true, message: '请输入联系人姓名' }]}
>
<Input placeholder="请输入联系人姓名" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactPhone"
label="联系人手机"
rules={[
{ required: true, message: '请输入联系人手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号' },
]}
>
<Input prefix={<MobileOutlined />} placeholder="请输入11位手机号码" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="captcha"
label="验证码"
rules={[{ required: true, message: '请输入验证码' }]}
>
<Row gutter={8}>
<Col span={14}>
<Input placeholder="请输入短信验证码" />
</Col>
<Col span={10}>
<Button
type="primary"
style={{ marginTop: 0 }}
disabled={countdown > 0}
onClick={handleGetCaptcha}
>
{countdown > 0 ? `${countdown}s` : '获取验证码'}
</Button>
</Col>
</Row>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="contactIdType" label="联系人身份类别">
<Select placeholder="请选择类型">
<Option value="idcard"></Option>
<Option value="passport"></Option>
<Option value="other"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="contactIdNumber" label="联系人证件号码">
<Input placeholder="请填写联系人正确的身份证号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactEmail"
label="联系人邮箱"
rules={[
{ type: 'email', message: '请输入有效的电子邮箱' },
{ required: true, message: '请输入电子邮箱' },
]}
>
<Input prefix={<MailOutlined />} placeholder="请输入企业联系电话" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="telephone" label="固定电话">
<Input placeholder="XXX@XXX.com" />
</Form.Item>
</Col>
</Row>
{/* 资质信息 */}
<div className="form-section-title"></div>
<Form.List name="qualifications">
{(fields, { add, remove }) => (
<>
<Table
dataSource={fields.map((field) => ({
...field,
key: field.key,
fieldKey: field.name,
}))}
pagination={false}
bordered
size="middle"
className="qualification-table"
rowKey="key"
columns={[
{
title: '序号',
dataIndex: 'name',
width: 60,
render: (_, __, index) => index + 1,
},
{
title: '资质证书类型',
dataIndex: 'certType',
render: (_, record) => (
<Form.Item
name={[record.name, 'certType']}
noStyle
rules={[{ required: true, message: '请选择资质证书类型' }]}
>
<Select placeholder="请选择类型" style={{ width: '100%' }}>
<Option value="机构资质"></Option>
<Option value="CMMI资质等级">CMMI资质等级</Option>
<Option value="质量体系认证"></Option>
<Option value="环境管理体系认证"></Option>
<Option value="行业资质"></Option>
</Select>
</Form.Item>
),
},
{
title: '资质名称',
dataIndex: 'certName',
render: (_, record) => (
<Form.Item
name={[record.name, 'certName']}
noStyle
rules={[{ required: true, message: '请输入资质名称' }]}
>
<Input placeholder="请输入资质名称" />
</Form.Item>
),
},
{
title: '资质证书编号',
dataIndex: 'certNumber',
render: (_, record) => (
<Form.Item
name={[record.name, 'certNumber']}
noStyle
rules={[{ required: true, message: '请输入资质证书编号' }]}
>
<Input placeholder="请输入证书编号" />
</Form.Item>
),
},
{
title: '资质类别和等级',
dataIndex: 'certLevel',
render: (_, record) => (
<Form.Item name={[record.name, 'certLevel']} noStyle>
<Input placeholder="请输入资质类别和等级" />
</Form.Item>
),
},
{
title: '发证机构',
dataIndex: 'issuingAuthority',
render: (_, record) => (
<Form.Item
name={[record.name, 'issuingAuthority']}
noStyle
rules={[{ required: true, message: '请输入发证机构' }]}
>
<Input placeholder="请输入发证机构" />
</Form.Item>
),
},
{
title: '发证日期',
dataIndex: 'issueDate',
render: (_, record) => (
<Form.Item
name={[record.name, 'issueDate']}
noStyle
rules={[{ required: true, message: '请选择发证日期' }]}
>
<DatePicker placeholder="年/月/日" style={{ width: '100%' }} />
</Form.Item>
),
},
{
title: '资质有效期至',
dataIndex: 'expiryDate',
render: (_, record) => (
<Form.Item
name={[record.name, 'expiryDate']}
noStyle
rules={[{ required: true, message: '请选择资质有效期' }]}
>
<DatePicker placeholder="年/月/日" style={{ width: '100%' }} />
</Form.Item>
),
},
{
title: '附件',
dataIndex: 'certFile',
render: (_, record) => (
<Form.Item
name={[record.name, 'certFile']}
noStyle
rules={[{ required: true, message: '请上传资质证书附件' }]}
>
<Upload name="certFile" action="/api/upload" listType="text" maxCount={1}>
<Button type="link" size="small">
</Button>
</Upload>
</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>
</>
)}
</Form.List>
<div className="form-section-title"></div>
<Row gutter={24}>
<Col span={8}>
<Form.Item
name="invoiceType"
label="纳税人类型"
rules={[{ required: true, message: '请选择纳税人类型' }]}
>
<Select placeholder="请选择纳税人类型">
<Option value="general"></Option>
<Option value="small"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="invoiceTitle"
label="开票抬头"
rules={[{ required: true, message: '请输入开票抬头' }]}
>
<Input placeholder="请输入开票抬头" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="taxpayerNumber"
label="纳税人识别号"
rules={[{ required: true, message: '请输入纳税人识别号' }]}
>
<Input placeholder="请输入纳税人识别号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoiceAddress" label="开票地址">
<Input placeholder="请输入开票地址" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoicePhone" label="开票电话">
<Input placeholder="请输入开票电话" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoiceBank" label="开票户行">
<Input placeholder="请输入开票银行" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="bankAccountNumber" label="开票户行账号">
<Input placeholder="请输入开票户行账号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="generalTaxpayerCert" label="一般纳税人资格证明">
<Upload
name="generalTaxpayerCert"
action="/api/upload"
listType="text"
maxCount={1}
beforeUpload={(file) => {
const isValidFormat =
file.type === 'application/pdf' ||
file.type === 'image/jpeg' ||
file.type === 'image/png';
if (!isValidFormat) {
message.error('只能上传PDF/JPG/PNG格式文件!');
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('文件大小不能超过10MB!');
}
return isValidFormat && isLt10M;
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</Form.Item>
</Col>
</Row>
<div className="form-section-title"></div>
<Form.List name="bankAccounts">
{(fields, { add, remove }) => (
<>
<Table
dataSource={fields.map((field) => ({
...field,
key: field.key,
fieldKey: field.name,
}))}
pagination={false}
bordered
size="middle"
className="qualification-table"
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>
</>
)}
</Form.List>
<div className="form-section-title"></div>
<div className="questionnaire-header"></div>
<Row gutter={24}>
<Col span={8}>
<Form.Item
name="surveyCompanyName"
label="供应商名称"
rules={[{ required: true, message: '请输入供应商名称' }]}
>
<Input placeholder="请输入供应商名称" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyPersonName"
label="姓名"
rules={[{ required: true, message: '请输入姓名' }]}
>
<Input placeholder="请输入姓名" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyPosition"
label="职位"
rules={[{ required: true, message: '请输入职位' }]}
>
<Input placeholder="请输入职位" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyPhone"
label="电话号"
rules={[{ required: true, message: '请输入电话号' }]}
>
<Input placeholder="请输入电话号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyEmail"
label="邮箱"
rules={[
{ type: 'email', message: '请输入有效的电子邮箱' },
{ required: true, message: '请输入电子邮箱' },
]}
>
<Input placeholder="请输入电子邮箱" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyDate"
label="日期"
rules={[{ required: true, message: '请选择日期' }]}
>
<DatePicker placeholder="请选择日期" style={{ width: '100%' }} />
</Form.Item>
</Col>
</Row>
<div className="questionnaire-header" style={{ marginTop: '20px' }}>
</div>
<Table
pagination={false}
bordered
size="middle"
rowKey="id"
dataSource={[
{
id: 1,
question:
'法律法规:\n我们确保经营和提供的产品服务遵守国家及各业务所在地的所有使用法律、法规及健康和安全',
},
{
id: 2,
question:
'健康和安全:\n我们为员工提供符合法律法规的安全且健康的工作场所。我们建立安全管理体系并向员工传达工作场所或生活设施的健康和安全标准致力于减少工作对员工造成的伤害和疾病。',
},
{
id: 3,
question:
'环境:\n我们能够以环境友好的方式经营。我们遵守适用的环境法律、法规和标准并建立有效的环境管理体系。\n我们遵守贵集团对相关产品或服务的部分附加环境要求这些要求和规定体现在设计与产品规范的合同文档中。',
},
{
id: 4,
question:
'监督和记录\n我们保留记录遵守所有法律和此行为准则的必要文件并根据要求为贵集团提供对文件的查看权。我们接允许贵集团在适当的时候以验证行为准则执行为目的的现场勘查',
},
]}
columns={[
{
title: '序号',
dataIndex: 'id',
width: 60,
align: 'center',
},
{
title: '问题',
dataIndex: 'question',
render: (text) => <div style={{ whiteSpace: 'pre-line' }}>{text}</div>,
},
{
title: '回复',
width: 650,
render: (_, record) => (
<Form.Item
name={['survey', `question${record.id}`]}
rules={[{ required: true, message: '请选择答案' }]}
>
<Radio.Group>
<Radio value="yes"></Radio>
<Radio value="no"></Radio>
{record.id !== 1 && (
<>
<Radio value="partialYes"></Radio>
<Radio value="noPlan"></Radio>
</>
)}
</Radio.Group>
</Form.Item>
),
},
]}
/>
<div className="form-section-title">贿</div>
<Row gutter={24}>
<Col span={12}>
<div className="upload-label">
<Button
type="link"
href="/templates/anti-bribery-template.docx"
download="供应商反商业贿赂承诺书模板.docx"
>
</Button>
</div>
<Form.Item
name="antiBriberyDoc"
rules={[{ required: true, message: '请上传已盖章的反商业贿赂承诺书' }]}
>
<Upload
name="antiBriberyDoc"
action="/api/upload"
listType="text"
maxCount={1}
beforeUpload={(file) => {
const isValidFormat =
file.type === 'application/pdf' ||
file.type === 'application/msword' ||
file.type ===
'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
if (!isValidFormat) {
message.error('只能上传PDF/DOC/DOCX格式文件!');
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('文件大小不能超过10MB!');
}
return isValidFormat && isLt10M;
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</Form.Item>
</Col>
</Row>
<div className="form-section-title"></div>
<Row gutter={24}>
<Col span={24}>
<Form.Item name="otherAttachments">
<div className="upload-label"></div>
<Upload
name="otherAttachments"
action="/api/upload"
listType="text"
multiple
beforeUpload={(file) => {
const isLt20M = file.size / 1024 / 1024 < 20;
if (!isLt20M) {
message.error('文件大小不能超过20MB!');
}
return isLt20M;
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</Form.Item>
</Col>
</Row>
</>
);
};
export default DomesticForm;

View File

@ -0,0 +1,891 @@
/* 境外企业 表单项 */
import React from 'react';
import { Form, Input, Button, Select, Upload, DatePicker, Row, Col, Cascader, Table, Radio } from 'antd';
import {
MobileOutlined,
MailOutlined,
EnvironmentOutlined,
UploadOutlined,
PlusOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import { message } from 'antd';
const { Option } = Select;
const { TextArea } = Input;
// 中国省市区级联数据
const addressOptions = [
{
value: '330000',
label: '浙江省',
children: [
{
value: '330100',
label: '杭州市',
children: [
{ value: '330102', label: '上城区' },
{ value: '330103', label: '下城区' },
{ value: '330104', label: '江干区' },
{ value: '330105', label: '拱墅区' },
{ value: '330106', label: '西湖区' },
{ value: '330108', label: '滨江区' },
],
},
],
},
{
value: '310000',
label: '上海市',
children: [
{
value: '310100',
label: '上海市',
children: [
{ value: '310101', label: '黄浦区' },
{ value: '310104', label: '徐汇区' },
{ value: '310105', label: '长宁区' },
],
},
],
},
];
interface ForeignFormProps {
form: any;
countdown: number;
handleGetCaptcha: () => void;
}
const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCaptcha }) => {
return (
<>
<div className="form-section-title"></div>
<Row gutter={24}>
<Col span={8}>
<Form.Item
name="companyName"
label="企业名称"
rules={[{ required: true, message: '请输入企业名称' }]}
>
<Input placeholder="请输入企业名称" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="companyEnglishName"
label="企业英文名称"
rules={[{ required: true, message: '请输入企业英文名称' }]}
>
<Input placeholder="请输入企业英文名称" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="companyRegNumber"
label="公司注册号"
rules={[{ required: true, message: '请输入公司注册号' }]}
>
<Input placeholder="请输入公司注册号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="country"
label="国家/地区"
rules={[{ required: true, message: '请选择国家/地区' }]}
>
<Select placeholder="请选择国家/地区">
<Option value="US"></Option>
<Option value="UK"></Option>
<Option value="JP"></Option>
<Option value="DE"></Option>
<Option value="FR"></Option>
<Option value="AU"></Option>
<Option value="CA"></Option>
<Option value="SG"></Option>
<Option value="HK"></Option>
<Option value="OTHER"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="registrationPassword"
label="登录密码"
rules={[
{ required: true, message: '请输入登录密码' },
{ min: 8, message: '密码长度为8-20位' },
{
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,20}$/,
message: '需要同时包含字母、数字、大小写',
},
]}
>
<Input.Password placeholder="长度为8-20位需要同时包含字母、数字、大小写" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="confirmPassword"
label="确认密码"
rules={[
{ required: true, message: '请再次输入密码' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('registrationPassword') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('两次输入的密码不一致'));
},
}),
]}
>
<Input.Password placeholder="请再次输入密码" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="registeredAddress"
label="注册地址"
rules={[{ required: true, message: '请输入注册地址' }]}
>
<Input prefix={<EnvironmentOutlined />} placeholder="请具体注明" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="officeAddress" label="办公地址">
<Input prefix={<EnvironmentOutlined />} placeholder="请具体注明" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="parentCompanyInfo" label="母公司/出资人">
<Input placeholder="请输入母公司或出资人信息" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="legalPerson"
label="企业法定代表人"
rules={[{ required: true, message: '请输入企业法定代表人' }]}
>
<Input placeholder="请输入企业法定代表人" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="companyType"
label="企业性质"
rules={[{ required: true, message: '请选择企业性质' }]}
>
<Select placeholder="请选择企业性质">
<Option value="corporation"></Option>
<Option value="partnership"></Option>
<Option value="joint"></Option>
<Option value="sole"></Option>
<Option value="other"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="supplierType"
label="供应商类型"
rules={[{ required: true, message: '请选择供应商类型' }]}
>
<Select placeholder="请选择供应商类型">
<Option value="manufacturer"></Option>
<Option value="agent"></Option>
<Option value="service"></Option>
<Option value="other"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="registeredCapitalGroup"
label="注册资本"
rules={[{ required: true, message: '请输入注册资本' }]}
>
<Input.Group compact style={{ display: 'flex' }}>
<Form.Item name="capitalCurrency" noStyle initialValue="USD">
<Select style={{ width: 100, borderRadius: '2px 0 0 2px' }}>
<Option value="USD"></Option>
<Option value="EUR"></Option>
<Option value="GBP"></Option>
<Option value="JPY"></Option>
<Option value="HKD"></Option>
</Select>
</Form.Item>
<Form.Item
name="capitalAmount"
noStyle
rules={[{ required: true, message: '请输入金额' }]}
>
<Input
type="number"
placeholder="请输入金额"
style={{ flex: 1, borderRadius: '0 2px 2px 0', marginLeft: -1 }}
/>
</Form.Item>
</Input.Group>
</Form.Item>
</Col>
<Col span={16}>
<Form.Item
name="businessScope"
label="经营范围"
labelCol={{ span: 2 }}
wrapperCol={{ span: 21 }}
rules={[{ required: true, message: '请输入经营范围' }]}
>
<TextArea placeholder="请输入经营范围" rows={2} maxLength={200} showCount />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactName"
label="联系人姓名"
rules={[{ required: true, message: '请输入联系人姓名' }]}
>
<Input placeholder="请输入联系人姓名" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactPhone"
label="联系人手机"
rules={[
{ required: true, message: '请输入联系人手机号码' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' },
]}
>
<Input
placeholder="请输入11位手机号码"
prefix={<MobileOutlined />}
addonAfter={
<Button
type="link"
size="small"
disabled={countdown > 0}
onClick={handleGetCaptcha}
>
{countdown ? `${countdown}秒后重新获取` : '获取验证码'}
</Button>
}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="verificationCode"
label="验证码"
rules={[
{ required: true, message: '请输入验证码' },
{ pattern: /^\d{6}$/, message: '请输入6位数字验证码' },
]}
extra="该手机号用于后续联系和找回密码"
>
<Input placeholder="请输入验证码" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactIdType"
label="联系人身份类别"
rules={[{ required: true, message: '请选择联系人身份类别' }]}
>
<Select placeholder="请选择类型">
<Option value="internal"></Option>
<Option value="external"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactIdNumber"
label="联系人证件号码"
rules={[{ required: true, message: '请输入联系人证件号码' }]}
>
<Input placeholder="请填写联系人正确的身份证号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactEmail"
label="联系人邮箱"
rules={[
{ type: 'email', message: '请输入有效的电子邮箱' },
{ required: true, message: '请输入电子邮箱' },
]}
>
<Input prefix={<MailOutlined />} placeholder="XXX@XXX.com" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="telephone" label="固定电话">
<Input placeholder="请输入企业联系电话" />
</Form.Item>
</Col>
</Row>
{/* 资质信息 */}
<div className="form-section-title"></div>
<Form.List name="qualifications">
{(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: 'certType',
render: (_, record) => (
<Form.Item
name={[record.name, 'certType']}
noStyle
rules={[{ required: true, message: '请选择资质证书类型' }]}
>
<Select placeholder="请选择类型" style={{ width: '100%' }}>
<Option value="机构资质"></Option>
<Option value="CMMI资质等级">CMMI资质等级</Option>
<Option value="质量体系认证"></Option>
<Option value="环境管理体系认证"></Option>
<Option value="行业资质"></Option>
</Select>
</Form.Item>
),
},
{
title: '资质名称',
dataIndex: 'certName',
render: (_, record) => (
<Form.Item
name={[record.name, 'certName']}
noStyle
rules={[{ required: true, message: '请输入资质名称' }]}
>
<Input placeholder="请输入资质名称" />
</Form.Item>
),
},
{
title: '资质证书编号',
dataIndex: 'certNumber',
render: (_, record) => (
<Form.Item
name={[record.name, 'certNumber']}
noStyle
rules={[{ required: true, message: '请输入资质证书编号' }]}
>
<Input placeholder="请输入证书编号" />
</Form.Item>
),
},
{
title: '资质类别和等级',
dataIndex: 'certLevel',
render: (_, record) => (
<Form.Item name={[record.name, 'certLevel']} noStyle>
<Input placeholder="请输入资质类别和等级" />
</Form.Item>
),
},
{
title: '发证机构',
dataIndex: 'issuingAuthority',
render: (_, record) => (
<Form.Item
name={[record.name, 'issuingAuthority']}
noStyle
rules={[{ required: true, message: '请输入发证机构' }]}
>
<Input placeholder="请输入发证机构" />
</Form.Item>
),
},
{
title: '发证日期',
dataIndex: 'issueDate',
render: (_, record) => (
<Form.Item
name={[record.name, 'issueDate']}
noStyle
rules={[{ required: true, message: '请选择发证日期' }]}
>
<DatePicker placeholder="年/月/日" style={{ width: '100%' }} />
</Form.Item>
),
},
{
title: '资质有效期至',
dataIndex: 'expiryDate',
render: (_, record) => (
<Form.Item
name={[record.name, 'expiryDate']}
noStyle
rules={[{ required: true, message: '请选择资质有效期' }]}
>
<DatePicker placeholder="年/月/日" style={{ width: '100%' }} />
</Form.Item>
),
},
{
title: '附件',
dataIndex: 'certFile',
render: (_, record) => (
<Form.Item
name={[record.name, 'certFile']}
noStyle
rules={[{ required: true, message: '请上传资质证书附件' }]}
>
<Upload name="certFile" action="/api/upload" listType="text" maxCount={1}>
<Button type="link" size="small"></Button>
</Upload>
</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>
</>
)}
</Form.List>
{/* 开票信息 */}
<div className="form-section-title"></div>
<Row gutter={24}>
<Col span={8}>
<Form.Item
name="invoiceType"
label="纳税人类型"
rules={[{ required: true, message: '请选择纳税人类型' }]}
>
<Select placeholder="请选择纳税人类型">
<Option value="general"></Option>
<Option value="small"></Option>
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="invoiceTitle"
label="开票抬头"
rules={[{ required: true, message: '请输入开票抬头' }]}
>
<Input placeholder="请输入开票抬头" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="taxpayerNumber"
label="纳税人识别号"
rules={[{ required: true, message: '请输入纳税人识别号' }]}
>
<Input placeholder="请输入纳税人识别号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoiceAddress" label="开票地址">
<Input placeholder="请输入开票地址" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoicePhone" label="开票电话">
<Input placeholder="请输入开票电话" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoiceBank" label="开票户行">
<Input placeholder="请输入开票银行" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="bankAccountNumber" label="开票户行账号">
<Input placeholder="请输入开票户行账号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="generalTaxpayerCert" label="一般纳税人资格证明">
<Upload
name="generalTaxpayerCert"
action="/api/upload"
listType="text"
maxCount={1}
beforeUpload={(file) => {
const isValidFormat =
file.type === 'application/pdf' ||
file.type === 'image/jpeg' ||
file.type === 'image/png';
if (!isValidFormat) {
message.error('只能上传PDF/JPG/PNG格式文件!');
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('文件大小不能超过10MB!');
}
return isValidFormat && isLt10M;
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</Form.Item>
</Col>
</Row>
{/* 银行账户 */}
<div className="form-section-title"></div>
<Form.List name="bankAccounts">
{(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>
</>
)}
</Form.List>
{/* 社会准则符合性自查问卷 */}
<div className="form-section-title"></div>
<div className="questionnaire-header"></div>
<Row gutter={24}>
<Col span={8}>
<Form.Item
name="surveyCompanyName"
label="供应商名称"
rules={[{ required: true, message: '请输入供应商名称' }]}
>
<Input placeholder="请输入供应商名称" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyPersonName"
label="姓名"
rules={[{ required: true, message: '请输入姓名' }]}
>
<Input placeholder="请输入姓名" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyPosition"
label="职位"
rules={[{ required: true, message: '请输入职位' }]}
>
<Input placeholder="请输入职位" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyPhone"
label="电话号"
rules={[{ required: true, message: '请输入电话号' }]}
>
<Input placeholder="请输入电话号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyEmail"
label="邮箱"
rules={[
{ type: 'email', message: '请输入有效的电子邮箱' },
{ required: true, message: '请输入电子邮箱' },
]}
>
<Input placeholder="请输入电子邮箱" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="surveyDate"
label="日期"
rules={[{ required: true, message: '请选择日期' }]}
>
<DatePicker placeholder="请选择日期" style={{ width: '100%' }} />
</Form.Item>
</Col>
</Row>
<div className="questionnaire-header" style={{ marginTop: '20px' }}>
</div>
<Table
pagination={false}
bordered
size="middle"
rowKey="id"
dataSource={[
{
id: 1,
question:
'法律法规:\n我们确保经营和提供的产品服务遵守国家及各业务所在地的所有使用法律、法规及健康和安全',
},
{
id: 2,
question:
'健康和安全:\n我们为员工提供符合法律法规的安全且健康的工作场所。我们建立安全管理体系并向员工传达工作场所或生活设施的健康和安全标准致力于减少工作对员工造成的伤害和疾病。',
},
{
id: 3,
question:
'环境:\n我们能够以环境友好的方式经营。我们遵守适用的环境法律、法规和标准并建立有效的环境管理体系。\n我们遵守贵集团对相关产品或服务的部分附加环境要求这些要求和规定体现在设计与产品规范的合同文档中。',
},
{
id: 4,
question:
'监督和记录\n我们保留记录遵守所有法律和此行为准则的必要文件并根据要求为贵集团提供对文件的查看权。我们接允许贵集团在适当的时候以验证行为准则执行为目的的现场勘查',
},
]}
columns={[
{
title: '序号',
dataIndex: 'id',
width: 60,
align: 'center',
},
{
title: '问题',
dataIndex: 'question',
render: (text) => <div style={{ whiteSpace: 'pre-line' }}>{text}</div>,
},
{
title: '回复',
width: 650,
render: (_, record) => (
<Form.Item
name={['survey', `question${record.id}`]}
rules={[{ required: true, message: '请选择答案' }]}
>
<Radio.Group>
<Radio value="yes"></Radio>
<Radio value="no"></Radio>
{record.id !== 1 && (
<>
<Radio value="partialYes"></Radio>
<Radio value="noPlan"></Radio>
</>
)}
</Radio.Group>
</Form.Item>
),
},
]}
/>
{/* 供应商反商业贿赂承诺书 */}
<div className="form-section-title">贿</div>
<Row gutter={24}>
<Col span={12}>
<div className="upload-label">
<Button
type="link"
href="/templates/anti-bribery-template.docx"
download="供应商反商业贿赂承诺书模板.docx"
>
</Button>
</div>
<Form.Item
name="antiBriberyDoc"
rules={[{ required: true, message: '请上传已盖章的反商业贿赂承诺书' }]}
>
<Upload
name="antiBriberyDoc"
action="/api/upload"
listType="text"
maxCount={1}
beforeUpload={(file) => {
const isValidFormat =
file.type === 'application/pdf' ||
file.type === 'application/msword' ||
file.type ===
'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
if (!isValidFormat) {
message.error('只能上传PDF/DOC/DOCX格式文件!');
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('文件大小不能超过10MB!');
}
return isValidFormat && isLt10M;
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</Form.Item>
</Col>
</Row>
{/* 其他附件 */}
<div className="form-section-title"></div>
<Row gutter={24}>
<Col span={24}>
<Form.Item name="otherAttachments">
<div className="upload-label"></div>
<Upload
name="otherAttachments"
action="/api/upload"
listType="text"
multiple
beforeUpload={(file) => {
const isLt20M = file.size / 1024 / 1024 < 20;
if (!isLt20M) {
message.error('文件大小不能超过20MB!');
}
return isLt20M;
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</Form.Item>
</Col>
</Row>
</>
);
};
export default ForeignForm;

View File

@ -1,7 +1,14 @@
// 专家注册
import React, { useState } from 'react';
import { useIntl, history } from 'umi';
import { Form, Input, Button, message, Card, Select, Row, Col } from 'antd';
import { UserOutlined, LockOutlined, MobileOutlined, IdcardOutlined, HomeOutlined } from '@ant-design/icons';
import {
UserOutlined,
LockOutlined,
MobileOutlined,
IdcardOutlined,
HomeOutlined,
} from '@ant-design/icons';
import './register.less';
const { Option } = Select;
@ -14,21 +21,24 @@ const ExpertRegister: React.FC = () => {
// 获取短信验证码
const handleGetCaptcha = () => {
form.validateFields(['phone']).then(values => {
message.success(`验证码已发送至 ${values.phone}`);
let count = 60;
setCountdown(count);
const timer = setInterval(() => {
count--;
form
.validateFields(['phone'])
.then((values) => {
message.success(`验证码已发送至 ${values.phone}`);
let count = 60;
setCountdown(count);
if (count === 0) {
clearInterval(timer);
}
}, 1000);
}).catch(errorInfo => {
message.error('请先输入正确的手机号');
});
const timer = setInterval(() => {
count--;
setCountdown(count);
if (count === 0) {
clearInterval(timer);
}
}, 1000);
})
.catch((errorInfo) => {
message.error('请先输入正确的手机号');
});
};
const onFinish = (values: any) => {
@ -45,143 +55,184 @@ const ExpertRegister: React.FC = () => {
return (
<div className="register-page">
<div className="register-container">
<div className='back-home'>
<div className="back-home">
<a onClick={() => history.push('/index')}>
<HomeOutlined /> {intl.formatMessage({ id: 'login.back.home' })}
</a>
</div>
<div className="register-title">
{intl.formatMessage({ id: 'register.expert.title' })}
</div>
<div className="register-title">{intl.formatMessage({ id: 'register.expert.title' })}</div>
<Card className="register-card">
<Form
form={form}
name="expert_register"
className="register-form"
onFinish={onFinish}
layout="vertical"
<Form
form={form}
name="expert_register"
onFinish={onFinish}
layout="horizontal"
labelAlign="right"
size='large'
labelCol={{ span: 5 }}
wrapperCol={{ span: 19 }}
>
<Form.Item
name="username"
label={intl.formatMessage({ id: 'register.username.label' })}
rules={[
{
required: true,
message: intl.formatMessage({ id: 'register.username.required' }),
},
{ min: 4, message: intl.formatMessage({ id: 'register.username.min' }) },
]}
>
<Form.Item
name="username"
label={<span className="required-label">{intl.formatMessage({ id: 'register.username.label' })}</span>}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.username.required' }) },
{ min: 4, message: intl.formatMessage({ id: 'register.username.min' }) }
]}
>
<Input prefix={<UserOutlined />} placeholder={intl.formatMessage({ id: 'register.username.placeholder' })} />
</Form.Item>
<Input
prefix={<UserOutlined />}
placeholder={intl.formatMessage({ id: 'register.username.placeholder' })}
/>
</Form.Item>
<Form.Item
name="phone"
label={<span className="required-label">{intl.formatMessage({ id: 'register.phone.label' })}</span>}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.phone.required' }) },
{ pattern: /^1[3-9]\d{9}$/, message: intl.formatMessage({ id: 'register.phone.invalid' }) }
]}
>
<Input prefix={<MobileOutlined />} placeholder={intl.formatMessage({ id: 'register.phone.placeholder' })} />
</Form.Item>
<Form.Item
name="phone"
label={intl.formatMessage({ id: 'register.phone.label' })}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.phone.required' }) },
{
pattern: /^1[3-9]\d{9}$/,
message: intl.formatMessage({ id: 'register.phone.invalid' }),
},
]}
>
<Input
prefix={<MobileOutlined />}
placeholder={intl.formatMessage({ id: 'register.phone.placeholder' })}
/>
</Form.Item>
<Form.Item
name="idType"
label={intl.formatMessage({ id: 'register.expert.idType.label' })}
<Form.Item
name="idType"
label={intl.formatMessage({ id: 'register.expert.idType.label' })}
rules={[
{
required: true,
message: intl.formatMessage({ id: 'register.expert.idType.required' }),
},
]}
>
<Select
placeholder={intl.formatMessage({ id: 'register.expert.idType.placeholder' })}
className="custom-select"
dropdownMatchSelectWidth={false}
>
<Select placeholder={intl.formatMessage({ id: 'register.expert.idType.placeholder' })}>
<Option value="idcard"></Option>
<Option value="passport"></Option>
<Option value="other"></Option>
</Select>
</Form.Item>
<Option value="idcard"></Option>
<Option value="foreignid"></Option>
<Option value="tempid"></Option>
</Select>
</Form.Item>
<Form.Item
name="idCard"
label={<span className="required-label">{intl.formatMessage({ id: 'register.expert.idCard.label' })}</span>}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.expert.idCard.required' }) }
]}
>
<Input prefix={<IdcardOutlined />} placeholder={intl.formatMessage({ id: 'register.expert.idCard.placeholder' })} />
</Form.Item>
<Form.Item
name="idCard"
label={intl.formatMessage({ id: 'register.expert.idCard.label' })}
rules={[
{
required: true,
message: intl.formatMessage({ id: 'register.expert.idCard.required' }),
},
]}
>
<Input
prefix={<IdcardOutlined />}
placeholder={intl.formatMessage({ id: 'register.expert.idCard.placeholder' })}
/>
</Form.Item>
<Form.Item
name="password"
label={<span className="required-label">{intl.formatMessage({ id: 'register.password.label' })}</span>}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.password.required' }) },
{ min: 6, message: intl.formatMessage({ id: 'register.password.min' }) },
{ pattern: /^[a-zA-Z0-9]{6,16}$/, message: intl.formatMessage({ id: 'register.password.pattern' }) }
]}
>
<Input.Password
prefix={<LockOutlined />}
placeholder={intl.formatMessage({ id: 'register.password.placeholder' })}
/>
</Form.Item>
<Form.Item
name="password"
label={intl.formatMessage({ id: 'register.password.label' })}
rules={[
{
required: true,
message: intl.formatMessage({ id: 'register.password.required' }),
},
{ min: 6, message: intl.formatMessage({ id: 'register.password.min' }) },
{
pattern: /^[a-zA-Z0-9]{6,16}$/,
message: intl.formatMessage({ id: 'register.password.pattern' }),
},
]}
>
<Input.Password
prefix={<LockOutlined />}
placeholder={intl.formatMessage({ id: 'register.password.placeholder' })}
/>
</Form.Item>
<Form.Item
name="confirmPassword"
label={<span className="required-label">{intl.formatMessage({ id: 'register.confirmPassword.label' })}</span>}
dependencies={['password']}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.confirmPassword.required' }) },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error(intl.formatMessage({ id: 'register.confirmPassword.notMatch' })));
},
}),
]}
>
<Input.Password
prefix={<LockOutlined />}
placeholder={intl.formatMessage({ id: 'register.confirmPassword.placeholder' })}
/>
</Form.Item>
<Form.Item
name="confirmPassword"
label={intl.formatMessage({ id: 'register.confirmPassword.label' })}
dependencies={['password']}
rules={[
{
required: true,
message: intl.formatMessage({ id: 'register.confirmPassword.required' }),
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(
new Error(intl.formatMessage({ id: 'register.confirmPassword.notMatch' })),
);
},
}),
]}
>
<Input.Password
prefix={<LockOutlined />}
placeholder={intl.formatMessage({ id: 'register.confirmPassword.placeholder' })}
/>
</Form.Item>
<Form.Item
name="captcha"
label={<span className="required-label">{intl.formatMessage({ id: 'register.captcha.label' })}</span>}
rules={[{ required: true, message: intl.formatMessage({ id: 'register.captcha.required' }) }]}
>
<Row gutter={8}>
<Col span={16}>
<Input placeholder={intl.formatMessage({ id: 'register.captcha.placeholder' })} />
</Col>
<Col span={8}>
<Button
className="captcha-button"
disabled={countdown > 0}
onClick={handleGetCaptcha}
>
{countdown > 0 ? `${countdown}s` : intl.formatMessage({ id: 'register.captcha.get' })}
</Button>
</Col>
</Row>
</Form.Item>
<Form.Item
name="captcha"
label={intl.formatMessage({ id: 'register.captcha.label' })}
rules={[
{
required: true,
message: intl.formatMessage({ id: 'register.captcha.required' }),
},
]}
>
<Row gutter={8}>
<Col span={16}>
<Input placeholder={intl.formatMessage({ id: 'register.captcha.placeholder' })} />
</Col>
<Col span={8}>
<Button
type="primary"
style={{ marginTop: 0 }}
disabled={countdown > 0}
onClick={handleGetCaptcha}
>
{countdown > 0
? `${countdown}s`
: intl.formatMessage({ id: 'register.captcha.get' })}
</Button>
</Col>
</Row>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
className="register-button"
loading={loading}
>
{intl.formatMessage({ id: 'register.submit' })}
</Button>
<div className="login-link">
{intl.formatMessage({ id: 'register.hasAccount' })}
<a onClick={() => history.push('/login')}>
{intl.formatMessage({ id: 'register.login' })}
</a>
</div>
</Form.Item>
</Form>
</Card>
<Form.Item wrapperCol={{ offset: 6, span: 12 }}>
<Button type="primary" htmlType="submit" block loading={loading}>
{intl.formatMessage({ id: 'register.submit' })}
</Button>
<div style={{ marginTop: 16, textAlign: 'center' }}>
{intl.formatMessage({ id: 'register.hasAccount' })}
<a onClick={() => history.push('/login')} style={{ marginLeft: 8 }}>
{intl.formatMessage({ id: 'register.login' })}
</a>
</div>
</Form.Item>
</Form>
</div>
</div>
);

View File

@ -10,17 +10,23 @@
background-image: url('~@/assets/img/loginBg.jpg');
background-size: cover;
background-position: center;
position: relative;
}
// 注册容器
.register-container {
width: 600px;
padding: 30px;
background: #fff;
background: rgba(255, 255, 255, 0.95);
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
position: relative;
margin: 40px 0;
&.large-width{
width: 80%;
position: absolute;
top: 3%;
}
}
// 标题样式
@ -53,110 +59,57 @@
}
}
}
// 注册卡片
.register-card {
border-radius: 8px;
box-shadow: none;
.ant-card-body {
padding: 20px 0;
}
.form-section-title {
font-size: 16px;
font-weight: 600;
margin: 20px 0 16px 0;
padding-left: 12px;
border-left: 4px solid #1890ff;
}
// 必填字段标记
.required-label::before {
display: inline-block;
margin-right: 4px;
color: #ff4d4f;
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '*';
.questionnaire-header,
.questionnaire-questions {
font-weight: 500;
margin: 10px 0;
padding: 5px 0;
}
// 注册表单
.register-form {
.ant-form-item {
margin-bottom: 20px;
}
.question-text {
margin-bottom: 10px;
color: #333;
}
.ant-input,
.ant-input-affix-wrapper,
.ant-select-selector {
border-radius: 6px;
.commitment-upload {
padding: 15px;
background-color: #f9f9f9;
border-radius: 4px;
}
&:focus,
&-focused,
&:hover {
border-color: @main-color;
}
}
.ant-form-item-label > label {
.qualification-table {
.ant-table-thead > tr > th {
background-color: #f0f5ff;
font-weight: 500;
}
}
// 验证码按钮
.captcha-button {
width: 100%;
height: 40px;
background-color: @main-color;
border-color: @main-color;
color: #fff;
&:hover,
&:focus {
background-color: lighten(@main-color, 10%);
border-color: lighten(@main-color, 10%);
color: #fff;
.ant-form-item {
margin-bottom: 0;
}
&:disabled {
color: rgba(0, 0, 0, 0.25);
background-color: #f5f5f5;
border-color: #d9d9d9;
.ant-table-cell {
padding: 8px;
}
}
// 注册按钮
.register-button {
width: 100%;
height: 40px;
margin-top: 10px;
background-color: @main-color;
border-color: @main-color;
border-radius: 6px;
&:hover,
&:focus {
background-color: lighten(@main-color, 10%);
border-color: lighten(@main-color, 10%);
}
.questionnaire-header {
font-size: 15px;
font-weight: 500;
margin: 15px 0 10px 0;
color: rgba(0, 0, 0, 0.85);
}
// 登录链接
.login-link {
margin-top: 15px;
text-align: center;
a {
margin-left: 5px;
color: @main-color;
&:hover {
color: lighten(@main-color, 10%);
}
}
}
// 响应式布局
@media (max-width: 768px) {
.register-container {
width: 90%;
max-width: 500px;
padding: 20px;
margin: 20px 0;
}
.register-form{
height: 75vh;
overflow-x: hidden;
overflow-y: auto;
}

View File

@ -1,24 +1,63 @@
import React from 'react';
// 供应商注册
import React, { useState } from 'react';
import { useIntl, history } from 'umi';
import { Form, Input, Button, message, Card } from 'antd';
import { UserOutlined, LockOutlined, MobileOutlined, MailOutlined, HomeOutlined } from '@ant-design/icons';
import { Form, Button, message, Radio } from 'antd';
import { HomeOutlined } from '@ant-design/icons';
import DomesticForm from './DomesticForm';
import ForeignForm from './ForeignForm';
import './register.less';
const SupplierRegister: React.FC = () => {
const [form] = Form.useForm();
const intl = useIntl();
const [supplierType, setSupplierType] = useState<string>('domestic');
const [loading, setLoading] = useState(false);
const [countdown, setCountdown] = useState(0);
// 获取短信验证码
const handleGetCaptcha = () => {
form
.validateFields(['contactPhone'])
.then((values) => {
message.success(`验证码已发送至 ${values.contactPhone}`);
let count = 60;
setCountdown(count);
const timer = setInterval(() => {
count--;
setCountdown(count);
if (count === 0) {
clearInterval(timer);
}
}, 1000);
})
.catch((errorInfo) => {
message.error('请先输入正确的手机号');
});
};
const onFinish = (values: any) => {
setLoading(true);
// 手动将 supplierType 添加到表单值中
values.supplierType = supplierType;
console.log('供应商注册信息:', values);
// 这里添加注册逻辑
message.success('注册成功,请登录');
history.push('/login');
setTimeout(() => {
setLoading(false);
message.success('注册成功,请登录');
history.push('/login');
}, 1000);
};
const handleSupplierTypeChange = (e: any) => {
form.resetFields();
setSupplierType(e.target.value);
};
return (
<div className="register-page">
<div className="register-container">
<div className='back-home'>
<div className="register-page ">
<div className="register-container large-width">
<div className="back-home">
<a onClick={() => history.push('/index')}>
<HomeOutlined /> {intl.formatMessage({ id: 'login.back.home' })}
</a>
@ -28,106 +67,46 @@ const SupplierRegister: React.FC = () => {
{intl.formatMessage({ id: 'register.supplier.title' })}
</div>
<Card className="register-card">
<Form
form={form}
name="supplier_register"
className="register-form"
onFinish={onFinish}
layout="vertical"
<Form
form={form}
name="supplier_register"
className="register-form"
onFinish={onFinish}
layout="horizontal"
labelAlign="right"
size='large'
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
>
<Form.Item
label="企业类型"
labelCol={{ span:2 }}
wrapperCol={{ span: 19 }}
>
<Form.Item
name="username"
label={intl.formatMessage({ id: 'register.username.label' })}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.username.required' }) },
{ min: 4, message: intl.formatMessage({ id: 'register.username.min' }) }
]}
>
<Input prefix={<UserOutlined />} placeholder={intl.formatMessage({ id: 'register.username.placeholder' })} />
</Form.Item>
<Radio.Group onChange={handleSupplierTypeChange} value={supplierType}>
<Radio.Button value="domestic">/</Radio.Button>
<Radio.Button value="foreign"></Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item
name="password"
label={intl.formatMessage({ id: 'register.password.label' })}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.password.required' }) },
{ min: 6, message: intl.formatMessage({ id: 'register.password.min' }) }
]}
>
<Input.Password prefix={<LockOutlined />} placeholder={intl.formatMessage({ id: 'register.password.placeholder' })} />
</Form.Item>
{supplierType === 'domestic' ? (
<DomesticForm form={form} countdown={countdown} handleGetCaptcha={handleGetCaptcha} />
) : (
<ForeignForm form={form} countdown={countdown} handleGetCaptcha={handleGetCaptcha} />
)}
<Form.Item
name="confirmPassword"
label={intl.formatMessage({ id: 'register.confirmPassword.label' })}
dependencies={['password']}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.confirmPassword.required' }) },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error(intl.formatMessage({ id: 'register.confirmPassword.notMatch' })));
},
}),
]}
>
<Input.Password prefix={<LockOutlined />} placeholder={intl.formatMessage({ id: 'register.confirmPassword.placeholder' })} />
</Form.Item>
<Form.Item
name="companyName"
label={intl.formatMessage({ id: 'register.supplier.companyName.label' })}
rules={[{ required: true, message: intl.formatMessage({ id: 'register.supplier.companyName.required' }) }]}
>
<Input placeholder={intl.formatMessage({ id: 'register.supplier.companyName.placeholder' })} />
</Form.Item>
<Form.Item
name="contactPerson"
label={intl.formatMessage({ id: 'register.supplier.contactPerson.label' })}
rules={[{ required: true, message: intl.formatMessage({ id: 'register.supplier.contactPerson.required' }) }]}
>
<Input placeholder={intl.formatMessage({ id: 'register.supplier.contactPerson.placeholder' })} />
</Form.Item>
<Form.Item
name="phone"
label={intl.formatMessage({ id: 'register.phone.label' })}
rules={[
{ required: true, message: intl.formatMessage({ id: 'register.phone.required' }) },
{ pattern: /^1[3-9]\d{9}$/, message: intl.formatMessage({ id: 'register.phone.invalid' }) }
]}
>
<Input prefix={<MobileOutlined />} placeholder={intl.formatMessage({ id: 'register.phone.placeholder' })} />
</Form.Item>
<Form.Item
name="email"
label={intl.formatMessage({ id: 'register.email.label' })}
rules={[
{ type: 'email', message: intl.formatMessage({ id: 'register.email.invalid' }) },
{ required: true, message: intl.formatMessage({ id: 'register.email.required' }) }
]}
>
<Input prefix={<MailOutlined />} placeholder={intl.formatMessage({ id: 'register.email.placeholder' })} />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="register-button">
{intl.formatMessage({ id: 'register.submit' })}
</Button>
<div className="login-link">
{intl.formatMessage({ id: 'register.hasAccount' })}
<a onClick={() => history.push('/login')}>
{intl.formatMessage({ id: 'register.login' })}
</a>
</div>
</Form.Item>
</Form>
</Card>
<Form.Item wrapperCol={{ offset: 5, span: 14 }}>
<Button type="primary" htmlType="submit" loading={loading}>
{intl.formatMessage({ id: 'register.submit' })}
</Button>
<div style={{ marginTop: 16, textAlign: 'center' }}>
{intl.formatMessage({ id: 'register.hasAccount' })}
<a onClick={() => history.push('/login')} style={{ marginLeft: 8 }}>
{intl.formatMessage({ id: 'register.login' })}
</a>
</div>
</Form.Item>
</Form>
</div>
</div>
);