增加上传组件,对接注册字段

This commit is contained in:
linxd
2025-06-30 14:16:17 +08:00
parent 8176a9b3d0
commit 662053b731
13 changed files with 720 additions and 340 deletions

View File

@ -3,43 +3,7 @@ import proxy from './proxy';
export default defineConfig({
proxy: proxy['dev'],
define: {
//商城2.0跳转地址
REACT_APP_MALL_V2_URL: 'http://111.198.162.67/zglt/index_hzf.jsp?id=dev',
//各系统跳转参数
REACT_APP_CLIENT_KEY: 'KgPEkttG',
// REACT_APP_CLIENT_SECRET: 'ae5bdb183c502355d2055b3de73300aa73cbfdf3',
//密码加密参数
REACT_APP_PASSWORD_CIPHERMODE: '1',
REACT_APP_PASSWORD_PUBLICKEY: '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
//当前环境
START_ENV: 'DEV',
//询价查看报价跳转地址
// REACT_APP_XUNJIA_REDIRECT: 'http://10.0.204.215:8080/provider_dev',
//询价-查看报价详情-虚拟用户uid
REACT_APP_XUNJIA_UID: 'admin_entrance',
//智慧客服用户中心地址
REACT_APP_CUSTOMERSERVICE_USERCENTER: 'http://10.242.31.158:8100/auth/oauth/authorize?response_type=code',
//智慧客服client_id
REACT_APP_CUSTOMERSERVICE_CLIENT_ID: 'COsHJydx',
//智慧客服地址
REACT_APP_CUSTOMERSERVICE_REDIRECT: 'http://10.242.31.158:8632',
// //智慧客服ws地址
// REACT_APP_CUSTOMERSERVICE_WS_REDIRECT: 'ws://10.242.37.148:18022/api/api/biz-customer-service',
// //智慧客服文档中心查看图片地址
// REACT_APP_CUSTOMERSERVICE_DOC_REDIRECT: 'http://cos.gz-tst.cos.tg.unicom.local/349553515466:mall/',
// //智慧客服加密公钥私钥ciphercode
// REACT_APP_CUSTOMERSERVICE_PUBLICKEY : '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
// REACT_APP_CUSTOMERSERVICE_PRIVATEKEY : '4F7144028D4DCF88FA50F0E2B3FFDDCF63BBE17D1700537DCE037687D3AA3DA7',
// REACT_APP_CUSTOMERSERVICE_CIPHERCODE : 1,
UPLOAD_URL: '/upload',
REQUEST_BASE: '/api',
},
});

View File

@ -3,41 +3,7 @@ import proxy from './proxy';
export default defineConfig({
proxy: proxy['prod'],
define:{
//商城2.0跳转地址
REACT_APP_MALL_V2_URL:'http://111.198.162.67/zglt/index_hzf.jsp?id=dev',
//各系统跳转参数
REACT_APP_CLIENT_KEY : 'KgPEkttG',
// REACT_APP_CLIENT_SECRET :'ae5bdb183c502355d2055b3de73300aa73cbfdf3',
//密码加密参数
REACT_APP_PASSWORD_CIPHERMODE:'1',
REACT_APP_PASSWORD_PUBLICKEY:'04819CF427F9150FEEBD91E8D2346F203FC47312D212022A967D8372EA30B9581CCEEFCE2670BDDAF2E8DA1620EA73948126078ED9FF9773AA3A94EE6C80035A18',
//当前环境
START_ENV:'PROD',
//询价查看报价跳转地址
// REACT_APP_XUNJIA_REDIRECT: 'https://60.10.26.178/provider',
//询价-查看报价详情-虚拟用户uid
REACT_APP_XUNJIA_UID: 'admin_entrance',
//智慧客服用户中心地址
REACT_APP_CUSTOMERSERVICE_USERCENTER: 'https://uscm.chinaunicom.cn:18023/auth/oauth/authorize?response_type=code',
//智慧客服client_id
REACT_APP_CUSTOMERSERVICE_CLIENT_ID: 'COsHJydx',
//智慧客服地址
REACT_APP_CUSTOMERSERVICE_REDIRECT: 'https://uscm.chinaunicom.cn:18011',
// //智慧客服ws地址
// REACT_APP_CUSTOMERSERVICE_WS_REDIRECT: 'ws://uscm.unicom.local:18022/api/api/biz-customer-service',
// //智慧客服文档中心查看图片地址
// REACT_APP_CUSTOMERSERVICE_DOC_REDIRECT: 'http://cos.xx-pbc.cos.tg.unicom.local/349553515466:mall/',
// //智慧客服加密公钥私钥ciphercode
// REACT_APP_CUSTOMERSERVICE_PUBLICKEY : '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B',
// REACT_APP_CUSTOMERSERVICE_PRIVATEKEY : '4F7144028D4DCF88FA50F0E2B3FFDDCF63BBE17D1700537DCE037687D3AA3DA7',
// REACT_APP_CUSTOMERSERVICE_CIPHERCODE : 1,
UPLOAD_URL: '/upload',
REQUEST_BASE: '/api',
},
});

View File

@ -7,10 +7,15 @@ export default {
// },
'/api': {
// target: 'http://10.242.37.148:18022',//连接天宫的ng
target: 'http://10.0.0.10:18013',//连接天宫的ng
target: 'http://10.0.0.125:18012',//连接天宫的ng
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
'/upload': {
target: 'http://10.0.0.125:18012',//
changeOrigin: true,
pathRewrite: { '^/upload': '' },
},
},
prod: {
'/api/*': {

View File

@ -0,0 +1,51 @@
.file-upload-wrapper {
.ant-upload-list {
margin-top: 8px;
}
.ant-upload-list-item-name {
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
}
.ant-upload-select {
margin-bottom: 8px;
}
.file-upload-tip {
margin-top: 8px;
color: rgba(0, 0, 0, 0.45);
font-size: 12px;
line-height: 1.5;
span {
cursor: help;
text-decoration: underline dotted;
}
}
.ant-upload-drag {
padding: 16px;
.ant-upload-drag-icon {
margin-bottom: 8px;
.anticon {
color: #40a9ff;
font-size: 48px;
}
}
.ant-upload-text {
margin: 0 0 4px;
color: rgba(0, 0, 0, 0.85);
font-size: 16px;
}
.ant-upload-hint {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
}
}

View File

@ -0,0 +1,226 @@
import React, { useState, useEffect } from 'react';
import { Upload, Button, message, Tooltip } from 'antd';
import { UploadOutlined, InboxOutlined } from '@ant-design/icons';
import type { UploadFile, UploadProps } from 'antd/es/upload/interface';
import { validateFileSize } from '@/utils/utils';
import { useIntl } from 'umi';
import './FileUpload.less';
export interface FileUploadProps {
value?: UploadFile[];
onChange?: (fileList: UploadFile[]) => void;
maxCount?: number;
maxSize?: number;
allowedTypes?: string[];
listType?: 'text' | 'picture' | 'picture-card';
buttonText?: string;
disabled?: boolean;
accept?: string;
showUploadList?:
| boolean
| { showPreviewIcon?: boolean; showRemoveIcon?: boolean; showDownloadIcon?: boolean };
isDragger?: boolean;
tip?: string;
action?: string;
}
// 上传接口返回数据格式
interface UploadResponseData {
fileName: string;
fileSize: string;
filePath: string;
fileType: string;
url: string;
}
// API响应格式
interface ApiResponse<T> {
code: number;
success: boolean;
message: string;
data: T;
}
const FileUpload: React.FC<FileUploadProps> = ({
value,
onChange,
maxCount = 1,
maxSize = 5,
allowedTypes = ['*'],
listType = 'text',
buttonText,
disabled = false,
accept,
showUploadList = true,
isDragger = false,
tip,
action,
}) => {
const actionUrl = action ? `${UPLOAD_URL}${action}` : `${UPLOAD_URL}/fileConfig/files/upload`;
const intl = useIntl();
const [fileList, setFileList] = useState<UploadFile[]>([]);
// 监听value变化
useEffect(() => {
// 处理字符串URL值这是关键修复
if (typeof value === 'string' && value) {
const file: Partial<UploadFile> = {
uid: '-1',
name: 'image.jpg',
status: 'done',
url: value,
size: 0,
type: 'image/jpeg',
};
setFileList([file as UploadFile]);
return;
}
// 处理空值
if (!value || (Array.isArray(value) && value.length === 0)) {
setFileList([]);
return;
}
// 处理正常的文件数组
if (Array.isArray(value)) {
setFileList(value);
return;
}
// 处理对象格式
if (typeof value === 'object') {
// 处理fileList属性
if ('fileList' in value && Array.isArray((value as any).fileList)) {
const files = (value as { fileList: UploadFile[] }).fileList;
setFileList(files);
return;
}
// 尝试作为单个文件对象处理
if ('uid' in value && 'status' in value) {
setFileList([value as unknown as UploadFile]);
return;
}
console.warn('FileUpload: Unrecognized object format', value);
setFileList([]);
return;
}
// 无法识别的格式
console.warn('FileUpload component: unrecognized value format:', value);
setFileList([]);
}, [value]);
const defaultButtonText = intl.formatMessage({
id: 'component.fileUpload.buttonText',
defaultMessage: '上传文件',
});
// 处理上传接口返回的数据格式
const getFileUrl = (file: UploadFile) => {
// 如果response是API的标准响应格式
if (file.response && typeof file.response === 'object') {
// 首先检查是否是标准API响应格式
if ('success' in file.response && 'data' in file.response) {
const response = file.response as ApiResponse<UploadResponseData>;
if (response.success && response.data) {
return response.data.url;
}
}
// 如果response直接包含url属性
else if ('url' in file.response) {
return (file.response as UploadResponseData).url;
}
}
// 回退到文件的url或thumbUrl
return file.url || file.thumbUrl;
};
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
const filteredList = newFileList.filter((file) => file.status !== 'error');
// 为每个文件添加正确的url
const processedList = filteredList.map(file => {
if (file.status === 'done' && file.response && !file.url) {
return {
...file,
url: getFileUrl(file)
};
}
return file;
});
setFileList(processedList);
if (onChange) {
onChange(processedList);
}
};
const beforeUpload = (file: File) => {
return validateFileSize(file, maxSize, allowedTypes);
};
const UploadComponent = isDragger ? Upload.Dragger : Upload;
const uploadProps = {
fileList,
onChange: handleChange,
beforeUpload,
listType,
maxCount,
disabled,
accept,
showUploadList,
action: actionUrl,
};
const renderUploadButton = () => {
if (fileList.length >= maxCount) {
return null;
}
if (isDragger) {
return (
<>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">
{buttonText ||
intl.formatMessage({
id: 'component.fileUpload.dragText',
defaultMessage: '点击或拖拽文件到此区域上传',
})}
</p>
{tip && <p className="ant-upload-hint">{tip}</p>}
</>
);
}
return (
<Button icon={<UploadOutlined />} disabled={disabled}>
{buttonText || defaultButtonText}
</Button>
);
};
return (
<div className="file-upload-wrapper">
<UploadComponent {...uploadProps}>{renderUploadButton()}</UploadComponent>
{!isDragger && tip && (
<div className="file-upload-tip">
<Tooltip title={tip}>
<span>{tip}</span>
</Tooltip>
</div>
)}
</div>
);
};
export default FileUpload;

View File

@ -0,0 +1,127 @@
# FileUpload 文件上传组件
基于Ant Design Upload组件封装的文件上传组件支持文件类型和大小校验。
## 组件特性
- 支持文件类型校验
- 支持文件大小限制
- 支持拖拽上传
- 支持上传多个文件
- 支持国际化
- 支持自定义提示信息
- 支持表单受控模式
## 使用示例
### 基本使用
```tsx
import React from 'react';
import { Form } from 'antd';
import FileUpload from '@/components/FileUpload';
const App: React.FC = () => {
const [form] = Form.useForm();
return (
<Form form={form}>
<Form.Item
name="files"
label="上传文件"
rules={[{ required: true, message: '请上传文件' }]}
>
<FileUpload maxSize={5} allowedTypes={['pdf', 'doc', 'docx']} />
</Form.Item>
</Form>
);
};
export default App;
```
### 拖拽上传
```tsx
import React from 'react';
import { Form } from 'antd';
import FileUpload from '@/components/FileUpload';
import { useIntl } from 'umi';
const App: React.FC = () => {
const [form] = Form.useForm();
const intl = useIntl();
const fileTip = intl.formatMessage(
{ id: 'component.fileUpload.fileTypeTip' },
{ types: 'PDF, Word' }
);
return (
<Form form={form}>
<Form.Item
name="files"
label="上传文件"
rules={[{ required: true, message: '请上传文件' }]}
>
<FileUpload
maxSize={10}
allowedTypes={['pdf', 'doc', 'docx']}
isDragger={true}
tip={fileTip}
/>
</Form.Item>
</Form>
);
};
export default App;
```
### 图片上传
```tsx
import React from 'react';
import { Form } from 'antd';
import FileUpload from '@/components/FileUpload';
const App: React.FC = () => {
const [form] = Form.useForm();
return (
<Form form={form}>
<Form.Item
name="images"
label="上传图片"
rules={[{ required: true, message: '请上传图片' }]}
>
<FileUpload
maxSize={2}
allowedTypes={['jpg', 'png']}
listType="picture-card"
tip="支持jpg, png格式大小不超过2MB"
/>
</Form.Item>
</Form>
);
};
export default App;
```
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| value | 文件列表 | UploadFile[] | [] |
| onChange | 文件列表改变时的回调 | (fileList: UploadFile[]) => void | - |
| maxCount | 最大上传数量 | number | 1 |
| maxSize | 文件大小限制MB | number | 5 |
| allowedTypes | 允许的文件类型 | string[] | ['*'] |
| listType | 上传列表的样式 | 'text' \| 'picture' \| 'picture-card' | 'text' |
| buttonText | 上传按钮文字 | string | '上传文件' |
| disabled | 是否禁用 | boolean | false |
| accept | 接受的文件类型 | string | - |
| showUploadList | 是否展示文件列表 | boolean \| { showPreviewIcon?: boolean; showRemoveIcon?: boolean; showDownloadIcon?: boolean } | true |
| isDragger | 是否启用拖拽上传 | boolean | false |
| tip | 提示文字 | string | - |

View File

@ -0,0 +1,4 @@
import FileUpload from './FileUpload';
export default FileUpload;
export type { FileUploadProps } from './FileUpload';

View File

@ -69,7 +69,65 @@ const SupplierRegister: React.FC = () => {
values.coscoSupplierBase = values.coscoSupplierBase || {};
values.coscoSupplierBase.supplierType = supplierType;
console.log('供应商注册信息:', values);
// 记录详细的表单数据,便于调试
console.log('表单原始数据:', JSON.parse(JSON.stringify(values)));
console.log('供应商基本信息:', values.coscoSupplierBase);
console.log('开票信息:', values.coscoSupplierInvoice);
console.log('银行账户信息:', values.coscoSupplierBank);
console.log('资质信息:', values.coscoSupplierQualifications);
console.log('调查问卷信息:', values.coscoSupplierSurvey);
console.log('调查问卷回复:', values.coscoSupplierSurveyQuestionReply);
console.log('调查问卷附件:', values.coscoSupplierSurveyAttachments);
// 处理文件上传组件返回的文件列表值
// 处理营业执照附件
if (values.coscoSupplierBase.licenceAccessory && Array.isArray(values.coscoSupplierBase.licenceAccessory)) {
const licenceFile = values.coscoSupplierBase.licenceAccessory[0];
values.coscoSupplierBase.licenceAccessory = licenceFile?.response?.url || licenceFile?.response?.filePath || '';
}
// 处理纳税人资格证明
if (values.coscoSupplierInvoice?.qualificationCertificate && Array.isArray(values.coscoSupplierInvoice.qualificationCertificate)) {
const taxFile = values.coscoSupplierInvoice.qualificationCertificate[0];
values.coscoSupplierInvoice.qualificationCertificate = taxFile?.response?.url || taxFile?.response?.filePath || '';
}
// 处理资质证书附件
if (values.coscoSupplierQualifications) {
values.coscoSupplierQualifications = values.coscoSupplierQualifications.map((qual: any) => {
if (qual.accessory && Array.isArray(qual.accessory)) {
const accessoryFile = qual.accessory[0];
qual.accessory = accessoryFile?.response?.url || accessoryFile?.response?.filePath || '';
}
return qual;
});
}
// 处理调查问卷附件
if (values.coscoSupplierSurveyAttachments) {
values.coscoSupplierSurveyAttachments = values.coscoSupplierSurveyAttachments.map((item: any) => {
if (item.fileUrl && Array.isArray(item.fileUrl)) {
const file = item.fileUrl[0];
item.fileUrl = file?.response?.url || file?.response?.filePath || '';
}
return item;
});
}
// 处理调查问卷回复
if (values.coscoSupplierSurveyQuestionReply) {
// 过滤出有效的问卷回复并确保数据格式正确
values.coscoSupplierSurveyQuestionReply = values.coscoSupplierSurveyQuestionReply
.filter((item: any) => item && item.surveyQuestionId && item.replyValue)
.map((item: any) => ({
surveyQuestionId: item.surveyQuestionId,
replyValue: item.replyValue
}));
console.log('处理后的问卷回复:', values.coscoSupplierSurveyQuestionReply);
}
console.log('最终提交的表单数据:', JSON.parse(JSON.stringify(values)));
// 直接调用API
const response = await coscoSupplierBaseAdd(values);
@ -93,6 +151,10 @@ const SupplierRegister: React.FC = () => {
setSupplierType(e.target.value);
};
const onFinishFailed = (errorInfo: any) => {
console.log('表单验证失败:', errorInfo);
};
return (
<div className="register-page ">
<div className="register-container large-width">
@ -112,6 +174,7 @@ const SupplierRegister: React.FC = () => {
name="supplier_register"
className="register-form"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
layout="horizontal"
labelAlign="right"
size="large"

View File

@ -8,7 +8,6 @@ import {
Input,
Button,
Select,
Upload,
DatePicker,
Row,
Col,
@ -17,9 +16,9 @@ import {
Cascader,
Empty,
} from 'antd';
import { UploadOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons';
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons';
import { message } from 'antd';
import { validateFileSize } from '@/utils/utils';
import FileUpload from '@/components/FileUpload';
const { Option } = Select;
@ -103,7 +102,7 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
dataIndex: 'certType',
render: (_, record) => (
<Form.Item
name={[record.name, 'certType']}
name={[record.name, 'certificateType']}
noStyle
rules={[{ required: true, message: '请选择资质证书类型' }]}
>
@ -122,7 +121,7 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
dataIndex: 'certName',
render: (_, record) => (
<Form.Item
name={[record.name, 'certName']}
name={[record.name, 'name']}
noStyle
rules={[{ required: true, message: '请输入资质名称' }]}
>
@ -135,7 +134,7 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
dataIndex: 'certNumber',
render: (_, record) => (
<Form.Item
name={[record.name, 'certNumber']}
name={[record.name, 'code']}
noStyle
rules={[{ required: true, message: '请输入资质证书编号' }]}
>
@ -147,7 +146,7 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
title: '资质类别和等级',
dataIndex: 'certLevel',
render: (_, record) => (
<Form.Item name={[record.name, 'certLevel']} noStyle>
<Form.Item name={[record.name, 'typeLevel']} noStyle>
<Input placeholder="请输入资质类别和等级" />
</Form.Item>
),
@ -157,7 +156,7 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
dataIndex: 'issuingAuthority',
render: (_, record) => (
<Form.Item
name={[record.name, 'issuingAuthority']}
name={[record.name, 'authority']}
noStyle
rules={[{ required: true, message: '请输入发证机构' }]}
>
@ -204,15 +203,17 @@ export const QualificationSection: React.FC<CommonFormSectionsProps> = ({ form }
dataIndex: 'certFile',
render: (_, record) => (
<Form.Item
name={[record.name, 'certFile']}
name={[record.name, 'accessory']}
noStyle
rules={[{ required: true, message: '请上传资质证书附件' }]}
rules={[{ required: true, message: '请上传资质证书附件' }]}
valuePropName="value"
>
<Upload name="certFile" action="/api/upload" listType="text" maxCount={1}>
<Button type="link" size="small">
</Button>
</Upload>
<FileUpload
maxSize={10}
allowedTypes={['pdf', 'jpg', 'jpeg', 'png']}
maxCount={1}
buttonText="上传"
/>
</Form.Item>
),
},
@ -268,7 +269,7 @@ export const InvoiceSection: React.FC<CommonFormSectionsProps> = ({ form }) => {
</Col>
<Col span={8}>
<Form.Item
name="invoiceTitle"
name={['coscoSupplierInvoice', 'head']}
label="开票抬头"
rules={[{ required: true, message: '请输入开票抬头' }]}
>
@ -277,7 +278,7 @@ export const InvoiceSection: React.FC<CommonFormSectionsProps> = ({ form }) => {
</Col>
<Col span={8}>
<Form.Item
name="taxpayerNumber"
name={['coscoSupplierInvoice', 'taxpayerCode']}
label="纳税人识别号"
rules={[{ required: true, message: '请输入纳税人识别号' }]}
>
@ -285,36 +286,33 @@ export const InvoiceSection: React.FC<CommonFormSectionsProps> = ({ form }) => {
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoiceAddress" label="开票地址">
<Form.Item name={['coscoSupplierInvoice', 'address']} label="开票地址">
<Input placeholder="请输入开票地址" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoicePhone" label="开票电话">
<Form.Item name={['coscoSupplierInvoice', 'phone']} label="开票电话">
<Input placeholder="请输入开票电话" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="invoiceBank" label="开票户行">
<Form.Item name={['coscoSupplierInvoice', 'bank']} label="开票户行">
<Input placeholder="请输入开票银行" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="bankAccountNumber" label="开票户行账号">
<Form.Item name={['coscoSupplierInvoice', 'account']} label="开票户行账号">
<Input placeholder="请输入开票户行账号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="generalTaxpayerCert" label="一般纳税人资格证明">
<Upload
name="generalTaxpayerCert"
action="/api/upload"
listType="text"
<Form.Item name={['coscoSupplierInvoice', 'qualificationCertificate']} label="一般纳税人资格证明" valuePropName="value">
<FileUpload
maxSize={10}
allowedTypes={['pdf', 'jpg', 'jpeg', 'png']}
maxCount={1}
beforeUpload={(file) => validateFileSize(file, 10, ['pdf', 'jpg', 'jpeg', 'png'])}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
buttonText="上传文件"
/>
</Form.Item>
</Col>
</Row>
@ -356,7 +354,7 @@ export const BankAccountSection: React.FC<CommonFormSectionsProps> = ({ form })
dataIndex: 'bankName',
render: (_, record) => (
<Form.Item
name={[record.name, 'bankName']}
name={[record.name, 'bank']}
noStyle
rules={[{ required: true, message: '请输入开户银行' }]}
>
@ -382,7 +380,7 @@ export const BankAccountSection: React.FC<CommonFormSectionsProps> = ({ form })
dataIndex: 'accountNumber',
render: (_, record) => (
<Form.Item
name={[record.name, 'accountNumber']}
name={[record.name, 'account']}
noStyle
rules={[{ required: true, message: '请输入账号' }]}
>
@ -452,7 +450,10 @@ export const BankAccountSection: React.FC<CommonFormSectionsProps> = ({ form })
*/
export const SurveySection: React.FC<SurveySectionProps> = ({ form, surveyQuestions }) => {
// 使用API获取的问卷数据如果没有则显示无数据状态
const hasQuestions = surveyQuestions && surveyQuestions.length > 0;
const hasQuestions = surveyQuestions && Array.isArray(surveyQuestions) && surveyQuestions.length > 0;
// 调试日志
console.log('调查问卷数据:', surveyQuestions);
return (
<>
@ -524,22 +525,16 @@ export const SurveySection: React.FC<SurveySectionProps> = ({ form, surveyQuesti
</div>
{hasQuestions ? (
<Form.List name="coscoSupplierSurveyQuestionReply">
<Form.List name="coscoSupplierSurveyQuestionReply" initialValue={surveyQuestions.map((q: any, index) => ({ surveyQuestionId: q.id, replyValue: '' }))}>
{(fields, { add, remove }) => {
// 确保有足够的表单项对应每个问题
if (fields.length < surveyQuestions.length) {
const diff = surveyQuestions.length - fields.length;
for (let i = 0; i < diff; i++) {
add();
}
}
console.log('Form.List fields:', fields);
return (
<Table
pagination={false}
bordered
size="middle"
rowKey="id"
rowKey={(record, index) => `survey_question_${index}`}
dataSource={surveyQuestions}
columns={[
{
@ -557,30 +552,39 @@ export const SurveySection: React.FC<SurveySectionProps> = ({ form, surveyQuesti
{
title: '回复',
width: 650,
render: (_, record, index) => (
<>
<Form.Item
name={[index, 'surveyQuestionId']}
initialValue={record.id}
hidden
>
<Input />
</Form.Item>
<Form.Item
name={[index, 'replyValue']}
rules={[{ required: true, message: '请选择答案' }]}
wrapperCol={{ span: 24 }}
>
<Radio.Group>
{record.coscoSurveyQuestionOptionList?.map((option: any) => (
<Radio key={option.id} value={option.opentionValue}>
{option.optionName}
</Radio>
))}
</Radio.Group>
</Form.Item>
</>
),
render: (_, record, index) => {
return (
<>
<Form.Item
name={[index, 'surveyQuestionId']}
initialValue={record.id}
hidden
>
<Input />
</Form.Item>
<Form.Item
name={[index, 'replyValue']}
rules={[{ required: true, message: `请选择问题${index + 1}的答案` }]}
wrapperCol={{ span: 24 }}
>
{record.coscoSurveyQuestionOptionList && record.coscoSurveyQuestionOptionList.length > 0 ? (
<Radio.Group>
{record.coscoSurveyQuestionOptionList.map((option: any) => (
<Radio
key={option.id}
value={option.opentionValue}
>
{option.optionName}
</Radio>
))}
</Radio.Group>
) : (
<Input placeholder="请输入回答" />
)}
</Form.Item>
</>
);
}
},
]}
/>
@ -638,43 +642,40 @@ export const AttachmentSection: React.FC<CommonFormSectionsProps> = ({ form }) =
<Form.Item
name={[field.name, 'fileUrl']}
rules={[{ required: true, message: '请上传已盖章的反商业贿赂承诺书' }]}
valuePropName="value"
>
<Upload
name="file"
action="/api/upload"
listType="text"
<FileUpload
maxSize={10}
allowedTypes={['pdf', 'doc', 'docx']}
maxCount={1}
beforeUpload={(file) =>
validateFileSize(file, 10, ['pdf', 'doc', 'docx'])
}
onChange={(info) => {
if (info.file.status === 'done') {
const response = info.file.response;
if (response && response.success) {
// 填充文件信息
form.setFieldsValue({
coscoSupplierSurveyAttachments: [
{
...form.getFieldValue([
'coscoSupplierSurveyAttachments',
field.name,
]),
fileName: info.file.name,
fileType: info.file.type,
fileSize: info.file.size.toString(),
filePath: response.filePath || response.url,
},
],
});
message.success(`${info.file.name} 上传成功`);
} else {
message.error(`${info.file.name} 上传失败`);
buttonText="上传文件"
onChange={(fileList) => {
if (fileList && fileList.length > 0) {
const file = fileList[0];
if (file.status === 'done' && file.response) {
const response = file.response;
if (response && response.success) {
// 填充文件信息
form.setFieldsValue({
coscoSupplierSurveyAttachments: [
{
...form.getFieldValue([
'coscoSupplierSurveyAttachments',
field.name,
]),
fileName: file.name,
fileType: file.type,
fileSize: file.size?.toString(),
filePath: response.filePath || response.url,
},
],
});
message.success(`${file.name} 上传成功`);
}
}
}
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
/>
</Form.Item>
</div>
))}
@ -711,42 +712,40 @@ export const AttachmentSection: React.FC<CommonFormSectionsProps> = ({ form }) =
)}
{index > 0 && (
<Form.Item name={[field.name, 'fileUrl']}>
<Upload
name="file"
action="/api/upload"
listType="text"
<Form.Item name={[field.name, 'fileUrl']} valuePropName="value">
<FileUpload
maxSize={20}
allowedTypes={['*']}
maxCount={1}
beforeUpload={(file) => validateFileSize(file, 20, ['*'])}
onChange={(info) => {
if (info.file.status === 'done') {
const response = info.file.response;
if (response && response.success) {
// 填充文件信息
const fieldValue = form.getFieldValue([
'coscoSupplierSurveyAttachments',
field.name,
]);
form.setFieldsValue({
coscoSupplierSurveyAttachments: [
{
...fieldValue,
fileName: info.file.name,
fileType: info.file.type,
fileSize: info.file.size.toString(),
filePath: response.filePath || response.url,
},
],
});
message.success(`${info.file.name} 上传成功`);
} else {
message.error(`${info.file.name} 上传失败`);
buttonText="上传"
onChange={(fileList) => {
if (fileList && fileList.length > 0) {
const file = fileList[0];
if (file.status === 'done' && file.response) {
const response = file.response;
if (response && response.success) {
// 填充文件信息
const fieldValue = form.getFieldValue([
'coscoSupplierSurveyAttachments',
field.name,
]);
form.setFieldsValue({
coscoSupplierSurveyAttachments: [
{
...fieldValue,
fileName: file.name,
fileType: file.type,
fileSize: file.size?.toString(),
filePath: response.filePath || response.url,
},
],
});
message.success(`${file.name} 上传成功`);
}
}
}
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
/>
</Form.Item>
)}
</div>

View File

@ -1,12 +1,8 @@
/* 境内企业/机构 表单项 */
import React from 'react';
import { Form, Input, Button, Select, Upload, DatePicker, Row, Col, message } from 'antd';
import {
MobileOutlined,
MailOutlined,
EnvironmentOutlined,
UploadOutlined,
} from '@ant-design/icons';
import { Form, Input, Button, Select, DatePicker, Row, Col, message } from 'antd';
import { MobileOutlined, MailOutlined, EnvironmentOutlined } from '@ant-design/icons';
import FileUpload from '@/components/FileUpload';
/**
* 引入通用表单组件
@ -18,7 +14,6 @@ import {
SurveySection,
AttachmentSection,
} from './CommonFormSections';
import { validateFileSize } from '@/utils/utils';
const { Option } = Select;
const { TextArea } = Input;
@ -53,31 +48,26 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
<Form.Item
name={['coscoSupplierBase', 'licenceAccessory']}
label="营业执照附件"
extra="pdf,jpg,jpeg,png类型的文件,大小不超过10MB"
// extra="pdf,jpg,jpeg,png类型的文件,大小不超过10MB"
rules={[{ required: true, message: '请上传营业执照附件' }]}
valuePropName="value"
>
<Upload
name="businessLicense"
action="/api/upload"
listType="text"
<FileUpload
maxSize={10}
allowedTypes={['pdf', 'jpg', 'jpeg', 'png']}
maxCount={1}
beforeUpload={(file) => validateFileSize(file, 10, ['pdf', 'jpg', 'jpeg', 'png'])}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
buttonText="上传文件"
tip="支持PDF、JPG、PNG格式不超过10MB"
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name={['coscoSupplierBase', 'licenceDate']}
label="营业执照有效期"
rules={[{ required: false, message: '请选择营业执照有效期' }]}
rules={[{ required: true, 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}>
@ -91,7 +81,16 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
</Col>
<Col span={8}>
<Form.Item
name="socialCreditCode"
name={['coscoSupplierBase', 'nameEn']}
label="企业英文名"
rules={[{ required: false, message: '请输入企业英文名' }]}
>
<Input placeholder="请输入企业英文名" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name={['coscoSupplierBase', 'socialCreditCode']}
label="统一社会信用代码"
rules={[
{ required: true, message: '请输入统一社会信用代码' },
@ -101,82 +100,9 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
<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="registeredCapital"
label="注册资本"
rules={[{ required: true, message: '请输入注册资本' }]}
>
<Input
type="number"
placeholder="请输入金额"
addonBefore="人民币"
addonAfter="万元"
/>
</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"
name={['coscoSupplierBase', 'range']}
label="经营范围"
rules={[{ required: true, message: '请输入经营范围' }]}
labelCol={{ span: 2 }}
@ -191,9 +117,69 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
labelCol={{ span: 2 }}
wrapperCol={{ span: 22 }}
name={['coscoSupplierBase', 'regAddress']}
label="注册地址"
rules={[{ required: true, message: '请输入注册地址' }]}
>
<Input prefix={<EnvironmentOutlined />} placeholder="上海市普陀区XX路1888号" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name={['coscoSupplierBase', 'workAddress']}
label="办公地址"
labelCol={{ span: 2 }}
wrapperCol={{ span: 22 }}
>
<Input
prefix={<EnvironmentOutlined />}
placeholder="请具体注明省、市、区、路、门牌号"
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name={['coscoSupplierBase', 'parentCompanyInvestor']}
label="母公司/出资人"
labelCol={{ span: 2 }}
wrapperCol={{ span: 22 }}
>
<Input placeholder="请输入母公司或出资人信息" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="contactPerson"
name={['coscoSupplierBase', 'legalPerson']}
label="企业法定代表人"
rules={[{ required: true, message: '请输入企业法定代表人/负责人' }]}
>
<Input placeholder="张三" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name={['coscoSupplierBase', 'idCard']} label="联系人证件号码">
<Input placeholder="请填写联系人正确的身份证号" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name={['coscoSupplierBase', 'capital']}
label="注册资本"
rules={[{ required: true, message: '请输入注册资本' }]}
>
<Input type="number" placeholder="请输入金额" addonBefore="人民币" addonAfter="万元" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name={['coscoSupplierBase', 'contactsName']}
label="联系人姓名"
rules={[{ required: true, message: '请输入联系人姓名' }]}
>
@ -202,7 +188,7 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
</Col>
<Col span={8}>
<Form.Item
name="contactPhone"
name={['coscoSupplierBase', 'contactsPhone']}
label="联系人手机"
rules={[
{ required: true, message: '请输入联系人手机号' },
@ -235,23 +221,10 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
</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"
name={['coscoSupplierBase', 'contactsEmail']}
label="联系人邮箱"
rules={[
{ type: 'email', message: '请输入有效的电子邮箱' },
@ -262,7 +235,7 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="telephone" label="固定电话">
<Form.Item name={['coscoSupplierBase', 'telephone']} label="固定电话">
<Input placeholder="XXX@XXX.com" />
</Form.Item>
</Col>

View File

@ -41,7 +41,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
<Row gutter={24}>
<Col span={8}>
<Form.Item
name="companyName"
name={['coscoSupplierBase', 'name']}
label="企业名称"
rules={[{ required: true, message: '请输入企业名称' }]}
>
@ -50,7 +50,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="companyEnglishName"
name={['coscoSupplierBase', 'nameEn']}
label="企业英文名称"
rules={[{ required: true, message: '请输入企业英文名称' }]}
>
@ -59,7 +59,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="companyRegNumber"
name={['coscoSupplierBase', 'socialCreditCode']}
label="公司注册号"
rules={[{ required: true, message: '请输入公司注册号' }]}
>
@ -123,7 +123,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="registeredAddress"
name={['coscoSupplierBase', 'regAddress']}
label="注册地址"
rules={[{ required: true, message: '请输入注册地址' }]}
>
@ -131,18 +131,18 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="officeAddress" label="办公地址">
<Form.Item name={['coscoSupplierBase', 'workAddress']} label="办公地址">
<Input prefix={<EnvironmentOutlined />} placeholder="请具体注明" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="parentCompanyInfo" label="母公司/出资人">
<Form.Item name={['coscoSupplierBase', 'parentCompanyInvestor']} label="母公司/出资人">
<Input placeholder="请输入母公司或出资人信息" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="legalPerson"
name={['coscoSupplierBase', 'legalPerson']}
label="企业法定代表人"
rules={[{ required: true, message: '请输入企业法定代表人' }]}
>
@ -151,7 +151,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="companyType"
name={['coscoSupplierBase', 'enterpriseType']}
label="企业性质"
rules={[{ required: true, message: '请选择企业性质' }]}
>
@ -166,7 +166,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="supplierType"
name={['coscoSupplierBase', 'supplierType']}
label="供应商类型"
rules={[{ required: true, message: '请选择供应商类型' }]}
>
@ -195,7 +195,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Select>
</Form.Item>
<Form.Item
name="capitalAmount"
name={['coscoSupplierBase', 'capital']}
noStyle
rules={[{ required: true, message: '请输入注册资本金额' }]}
>
@ -210,7 +210,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={24}>
<Form.Item
name="businessScope"
name={['coscoSupplierBase', 'range']}
label="经营范围"
labelCol={{ span: 2 }}
wrapperCol={{ span: 22 }}
@ -221,7 +221,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="contactName"
name={['coscoSupplierBase', 'contactsName']}
label="联系人姓名"
rules={[{ required: true, message: '请输入联系人姓名' }]}
>
@ -230,7 +230,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="contactPhone"
name={['coscoSupplierBase', 'contactsPhone']}
label="联系人手机"
rules={[
{ required: true, message: '请输入联系人手机号码' },
@ -268,7 +268,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="contactIdType"
name={['coscoSupplierBase', 'contactsType']}
label="联系人身份类别"
rules={[{ required: true, message: '请选择联系人身份类别' }]}
>
@ -280,7 +280,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="contactIdNumber"
name={['coscoSupplierBase', 'idCard']}
label="联系人证件号码"
rules={[{ required: true, message: '请输入联系人证件号码' }]}
>
@ -289,7 +289,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Col>
<Col span={8}>
<Form.Item
name="contactEmail"
name={['coscoSupplierBase', 'contactsEmail']}
label="联系人邮箱"
rules={[
{ type: 'email', message: '请输入有效的电子邮箱' },
@ -300,7 +300,7 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="telephone" label="固定电话">
<Form.Item name={['coscoSupplierBase', 'telephone']} label="固定电话">
<Input placeholder="请输入企业联系电话" />
</Form.Item>
</Col>

2
src/typings.d.ts vendored
View File

@ -60,3 +60,5 @@ declare const REACT_APP_XUNJIA_UID: string
declare const REACT_APP_CUSTOMERSERVICE_USERCENTER: string
declare const REACT_APP_CUSTOMERSERVICE_CLIENT_ID: string
declare const REACT_APP_CUSTOMERSERVICE_REDIRECT: string
declare const UPLOAD_URL: string
declare const REQUEST_BASE: string

View File

@ -53,7 +53,7 @@ const codeMessage = {
*/
const request = extend({
// errorHandler, // 默认错误处理
prefix: '/api',
prefix: REQUEST_BASE,
credentials: 'include' // 默认请求是否带上cookie
});
// request拦截器, 改变url 或 options.