供应商
This commit is contained in:
@ -0,0 +1,453 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Form, Select, Button, Tree, message, DatePicker, Radio, Upload, Input } from 'antd';
|
||||
//
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import type { UploadFile } from 'antd/es/upload/interface';
|
||||
import moment from 'moment';
|
||||
//组件
|
||||
import SupplierSelector from './SupplierSelector';
|
||||
import ReviewerSelector from './ReviewerSelector';
|
||||
import DivisionModal from './DivisionModal';
|
||||
// 请求
|
||||
import { categoryTree, add, uploadFile } from '../services';
|
||||
const { Option } = Select;
|
||||
const { RangePicker } = DatePicker;
|
||||
//selected 类型
|
||||
interface Reviewer {
|
||||
key: string;
|
||||
name: string;
|
||||
id: string;
|
||||
dept: string;
|
||||
}
|
||||
// 传入的人接口
|
||||
interface ReviewerSelectorData {
|
||||
selected: Reviewer[];
|
||||
leader: Reviewer | null;
|
||||
}
|
||||
// 主体
|
||||
const CreateModal: React.FC<{ visible: boolean; onCancel: () => void; }> = ({ visible, onCancel }) => {
|
||||
|
||||
const [form] = Form.useForm();
|
||||
//品类选择
|
||||
const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]);
|
||||
//供应商
|
||||
const [selectedSuppliers, setSelectedSuppliers] = useState<any[]>([]);
|
||||
// 选择评审人员
|
||||
const [selectedReviewers, setSelectedReviewers] = useState<ReviewerSelectorData>({
|
||||
selected: [],
|
||||
leader: null,
|
||||
});
|
||||
//评审分工
|
||||
const [divisionData, setDivisionData] = useState<any[]>([]);
|
||||
//准入方式
|
||||
const [admissionMethod, setAdmissionMethod] = useState<string>('online');
|
||||
//供应商符合性审查
|
||||
const [fileList, setFileList] = useState<UploadFile<any>[]>([]);
|
||||
//供应商弹出
|
||||
const [supplierModalVisible, setSupplierModalVisible] = useState(false);
|
||||
const [reviewerModalVisible, setReviewerModalVisible] = useState(false);
|
||||
const [divisionModalVisible, setDivisionModalVisible] = useState(false);
|
||||
//品类选择渲染数据
|
||||
const [categoriesTreeData, setCategoriesTreeData] = useState([]);
|
||||
//品类选择数据中字段转换
|
||||
const convertTreeData = (data: any) => {
|
||||
return data.map((item: any) => ({
|
||||
...item,
|
||||
title: item.categoryName,
|
||||
key: item.id,
|
||||
children: item.children ? convertTreeData(item.children) : undefined,
|
||||
|
||||
}));
|
||||
}
|
||||
function findLeafKeys(treeData: any[]): string[] {
|
||||
let leafKeys: string[] = [];
|
||||
function dfs(nodes: any[]) {
|
||||
nodes.forEach(node => {
|
||||
if (!node.children || node.children.length === 0) {
|
||||
leafKeys.push(node.key);
|
||||
} else {
|
||||
dfs(node.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
dfs(treeData);
|
||||
return leafKeys;
|
||||
}
|
||||
//品类选择
|
||||
const onCheck = (
|
||||
checkedKeysValue:
|
||||
| React.Key[]
|
||||
| { checked: React.Key[]; halfChecked: React.Key[] }
|
||||
) => {
|
||||
const keys = Array.isArray(checkedKeysValue)
|
||||
? checkedKeysValue
|
||||
: checkedKeysValue.checked;
|
||||
|
||||
// 只取叶子节点 key
|
||||
const leafKeys = findLeafKeys(convertTreeData(categoriesTreeData));
|
||||
const onlyLeafChecked = keys.filter(key => leafKeys.includes(key));
|
||||
|
||||
setCheckedKeys(keys); // UI 显示用,还是全量
|
||||
form.setFieldsValue({ categoryIds: onlyLeafChecked }); // 只存叶子到表单
|
||||
// setCheckedKeys(keys);
|
||||
// form.setFieldsValue({ categoryIds: keys });
|
||||
};
|
||||
// 选择准入方式
|
||||
const onMethodChange = (e: any) => {
|
||||
setAdmissionMethod(e.target.value);
|
||||
form.setFieldsValue({ method: e.target.value });
|
||||
|
||||
// 如果切换到线上准入,清空下面相关字段
|
||||
if (e.target.value === 'online') {
|
||||
form.setFieldsValue({
|
||||
rangePicker: undefined,
|
||||
reviewers: undefined,
|
||||
division: undefined,
|
||||
supplierCompliance: undefined,
|
||||
});
|
||||
setSelectedReviewers({ selected: [], leader: null, });
|
||||
setDivisionData([]);
|
||||
}
|
||||
};
|
||||
|
||||
// 自定义上传
|
||||
const handleCustomRequest = async (options: any) => {
|
||||
const { file, onSuccess, onError } = options;
|
||||
try {
|
||||
// 1. 调用你自己写的上传接口
|
||||
const res = await uploadFile(file);
|
||||
|
||||
// 2. 可根据后端返回结构,给 fileList 项增加 url 或其他字段
|
||||
const uploadedFile: UploadFile = {
|
||||
...file,
|
||||
status: 'done',
|
||||
url: res?.data?.url || res?.url, // 按后端返回实际字段
|
||||
name: res?.data?.name || file.name,
|
||||
coscoAccessWorkAttachments: res
|
||||
};
|
||||
setFileList([uploadedFile]);
|
||||
// 同步到 Form
|
||||
form.setFieldsValue({ supplierCompliance: [uploadedFile] });
|
||||
|
||||
onSuccess && onSuccess(res, file);
|
||||
message.success('上传成功');
|
||||
} catch (e) {
|
||||
onError && onError(e);
|
||||
message.error('上传失败');
|
||||
}
|
||||
};
|
||||
// 提交
|
||||
const onFinish = async (values: any) => {
|
||||
const finalPayload = {
|
||||
coscoAccessWork: {
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
accessType: '',
|
||||
accessWorkName: '',
|
||||
deptId: '',
|
||||
},
|
||||
categoryIds: [],
|
||||
supplierIds: [],
|
||||
coscoAccessUserls: [],
|
||||
coscoAccessItems: [],
|
||||
coscoAccessWorkAttachments: {},
|
||||
};
|
||||
|
||||
//标题名称
|
||||
finalPayload.coscoAccessWork.accessWorkName = values.accessWorkName;
|
||||
//准入方式
|
||||
finalPayload.coscoAccessWork.accessType = values.accessType;
|
||||
//准入部门
|
||||
finalPayload.coscoAccessWork.deptId = values.deptId;
|
||||
//品类选择
|
||||
finalPayload.categoryIds = values.categoryIds;
|
||||
//选择供应商
|
||||
if(values.supplier.length != 0) {
|
||||
values.supplier.forEach((item: { id: string }) => {
|
||||
finalPayload.supplierIds.push(item.id)
|
||||
})
|
||||
}
|
||||
|
||||
if (values.accessType === 'online') {
|
||||
// 评审时间
|
||||
if (values.rangePicker && values.rangePicker.length === 2) {
|
||||
const [start, end] = values.rangePicker;
|
||||
finalPayload.coscoAccessWork.startTime = start.format('YYYY-MM-DD HH:mm');
|
||||
finalPayload.coscoAccessWork.endTime = end.format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
//选择评审人员
|
||||
finalPayload.coscoAccessUserls = values.reviewers.selected.map((item:{ userId:string, deptId:string,isLeader:number }) => {
|
||||
return { userId: item.userId, deptId: item.deptId, isLeader: item.isLeader }
|
||||
});
|
||||
//评审分工
|
||||
values.division.forEach((item: any) => {
|
||||
const dataJson = {
|
||||
itemName: item.itemName,
|
||||
reviewBy: [] as string[],
|
||||
};
|
||||
Object.entries(item.reviewerChecks || {}).forEach(([key, value]) => {
|
||||
dataJson.reviewBy.push(key);
|
||||
});
|
||||
finalPayload.coscoAccessItems.push(dataJson);
|
||||
});
|
||||
} else {
|
||||
// 供应商符合性审查
|
||||
finalPayload.coscoAccessWorkAttachments = values.supplierCompliance[0].response
|
||||
}
|
||||
|
||||
console.log( finalPayload, values);
|
||||
const res = await add(finalPayload);
|
||||
if (res?.success) {
|
||||
message.success('创建成功');
|
||||
form.resetFields();
|
||||
setCheckedKeys([]);
|
||||
setSelectedSuppliers([]);
|
||||
setSelectedReviewers({ selected: [], leader: null, });
|
||||
setDivisionData([]);
|
||||
setAdmissionMethod('online');
|
||||
onCancel();
|
||||
} else {
|
||||
message.error('创建失败');
|
||||
}
|
||||
};
|
||||
//初始化
|
||||
useEffect(() => {
|
||||
categoryTree().then((res) => {
|
||||
const { code, data } = res;
|
||||
if (code == 200) {
|
||||
setCategoriesTreeData(data)
|
||||
}
|
||||
|
||||
})
|
||||
form.setFieldsValue({ method: 'online' });
|
||||
}, [visible, form]);
|
||||
return (
|
||||
<Modal
|
||||
title="发起准入"
|
||||
visible={visible}
|
||||
footer={null}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
setCheckedKeys([]);
|
||||
setSelectedSuppliers([]);
|
||||
setSelectedReviewers({ selected: [], leader: null, });
|
||||
setDivisionData([]);
|
||||
setAdmissionMethod('online');
|
||||
onCancel();
|
||||
}}
|
||||
width="700px"
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
labelCol={{ span: 5 }}
|
||||
wrapperCol={{ span: 19 }}
|
||||
>
|
||||
<Form.Item
|
||||
label="标题名称"
|
||||
name="accessWorkName"
|
||||
rules={[{ required: true, message: '请输入标题名称' }]}
|
||||
>
|
||||
<Input placeholder="请输入标题名称" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="准入部门"
|
||||
name="deptId"
|
||||
rules={[{ required: true, message: '请选择准入部门' }]}
|
||||
>
|
||||
<Select placeholder="请选择部门">
|
||||
<Option value="DEPT001">部门A</Option>
|
||||
<Option value="DEPT001">部门B</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="准入方式"
|
||||
name="accessType"
|
||||
rules={[{ required: true, message: '请选择准入方式' }]}
|
||||
>
|
||||
<Radio.Group onChange={onMethodChange} value={admissionMethod}>
|
||||
<Radio value="online">线上准入</Radio>
|
||||
<Radio value="offline">线下准入</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="品类选择"
|
||||
name="categoryIds"
|
||||
rules={[{ required: true, message: '请选择准入品类' }]}
|
||||
>
|
||||
<Tree
|
||||
checkable
|
||||
selectable={false}
|
||||
treeData={convertTreeData(categoriesTreeData)}
|
||||
checkedKeys={checkedKeys}
|
||||
onCheck={onCheck}
|
||||
defaultExpandAll
|
||||
style={{
|
||||
maxHeight: 200,
|
||||
overflowY: 'auto',
|
||||
border: '1px solid #d9d9d9',
|
||||
padding: 8,
|
||||
borderRadius: 4,
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="选择供应商"
|
||||
name="supplier"
|
||||
rules={[{ required: true, message: '请选择供应商' }]}
|
||||
>
|
||||
<Button onClick={() => setSupplierModalVisible(true)}>
|
||||
选择供应商
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
{admissionMethod !== 'online' && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="供应商符合性审查"
|
||||
name="supplierCompliance"
|
||||
rules={[
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (admissionMethod === 'online') {
|
||||
if (!value || value.length === 0) {
|
||||
return Promise.reject(new Error('请上传供应商符合性审查文件'));
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={e => (Array.isArray(e) ? e : e && e.fileList)}
|
||||
>
|
||||
<Upload
|
||||
fileList={fileList}
|
||||
customRequest={handleCustomRequest}
|
||||
beforeUpload={(file) => {
|
||||
if (fileList.length >= 1) {
|
||||
message.error('只能上传一个文件');
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
return true; // 允许进入 customRequest
|
||||
}}
|
||||
onChange={({ fileList: newFileList }) => {
|
||||
setFileList(newFileList);
|
||||
form.setFieldsValue({ supplierCompliance: newFileList });
|
||||
}}
|
||||
onRemove={(file) => {
|
||||
const newList = fileList.filter(item => item.uid !== file.uid);
|
||||
setFileList(newList);
|
||||
form.setFieldsValue({ supplierCompliance: newList });
|
||||
}}
|
||||
accept=".pdf,.doc,.docx" // 可选,限制文件类型
|
||||
maxCount={1} // 只允许一个
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>点击上传文件</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
{admissionMethod === 'online' && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="评审时间"
|
||||
name="rangePicker"
|
||||
rules={[{ required: true, message: '请选择评审时间' }]}
|
||||
>
|
||||
<RangePicker
|
||||
style={{ width: '300px' }}
|
||||
showTime={{ format: 'HH:mm' }}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="选择评审人员"
|
||||
name="reviewers"
|
||||
rules={[{ required: true, message: '请选择评审人员' }]}
|
||||
>
|
||||
<Button onClick={() => setReviewerModalVisible(true)}>
|
||||
选择评审人员
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="评审分工"
|
||||
name="division"
|
||||
rules={[{ required: true, message: '请设置评审分工' }]}
|
||||
>
|
||||
<Button onClick={() => {
|
||||
if (selectedReviewers.selected.length != 0) {
|
||||
setDivisionModalVisible(true)
|
||||
} else {
|
||||
message.warning('请选择评审人员');
|
||||
}
|
||||
}}>
|
||||
设置评审分工
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Form.Item wrapperCol={{ offset: 6 }}>
|
||||
<Button type="primary" htmlType="submit" style={{ marginRight: 8 }}>
|
||||
提交
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
form.resetFields();
|
||||
setCheckedKeys([]);
|
||||
setSelectedSuppliers([]);
|
||||
setSelectedReviewers({ selected: [], leader: null, });
|
||||
setDivisionData([]);
|
||||
setAdmissionMethod('online');
|
||||
}}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<SupplierSelector
|
||||
visible={supplierModalVisible}
|
||||
onCancel={() => setSupplierModalVisible(false)}
|
||||
onSelect={(selected) => {
|
||||
setSelectedSuppliers(selected);
|
||||
form.setFieldsValue({ supplier: selected });
|
||||
setSupplierModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
<ReviewerSelector
|
||||
visible={reviewerModalVisible}
|
||||
onCancel={() => setReviewerModalVisible(false)}
|
||||
onSelect={(result) => {
|
||||
console.log(result,'result');
|
||||
|
||||
setSelectedReviewers(result);
|
||||
form.setFieldsValue({ reviewers: result });
|
||||
setReviewerModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
<DivisionModal
|
||||
reviewerSelector={selectedReviewers}
|
||||
visible={divisionModalVisible}
|
||||
onCancel={() => setDivisionModalVisible(false)}
|
||||
onSelect={(data) => {
|
||||
setDivisionData(data);
|
||||
form.setFieldsValue({ division: data });
|
||||
setDivisionModalVisible(false);
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateModal;
|
Reference in New Issue
Block a user