供应商联系人

This commit is contained in:
孙景学
2025-07-17 15:17:49 +08:00
parent 8e921b73d4
commit 5c505150e5
7 changed files with 577 additions and 112 deletions

View File

@ -4,7 +4,7 @@ import { UploadOutlined } from '@ant-design/icons';
import { categoryTree, uploadFile, superiorLockList, getSupplierPage, library, libraryData } from '../services';
import type { UploadFile } from 'antd/es/upload/interface';
import AccessDepartmentSelect from '@/components/AccessDepartmentSelect';
import { getregionInternational } from '@/servers/api/register';
import { getregionInternational, getAllAreaList } from '@/servers/api/register';
const { Option } = Select;
const approveTypeOptions = [
@ -136,7 +136,7 @@ const CategoryAddModal: React.FC<Props> = ({ visible, onCancel, onSuccess }) =>
form.resetFields();
}
init(visible)
getregionInternational().then(res => {
getAllAreaList().then(res => {
if (res.code === 200) {
setRegionOptions(res.data);
}
@ -282,7 +282,7 @@ const CategoryAddModal: React.FC<Props> = ({ visible, onCancel, onSuccess }) =>
<Select placeholder="请选择区域">
{
regionOptions.map((item) => {
return <Option value={item.id}>{item.name}</Option>
return <Option value={item.id}>{item.dicName}</Option>
})
}
</Select>
@ -365,7 +365,7 @@ const CategoryAddModal: React.FC<Props> = ({ visible, onCancel, onSuccess }) =>
</Form.Item>
);
}}
</Form.Item> {/* ✅ 外层 Form.Item 正确闭合 */}
</Form.Item>

View File

@ -1,7 +1,18 @@
import React, { useEffect, useState } from 'react';
import { Modal, Table, Button, Checkbox, Popconfirm, message, Descriptions, Spin } from 'antd';
import { getSupplierPage, detail, apply } from "../services";
import { Modal, Table, Button, Checkbox, Form, Select, message, Descriptions, Tooltip, Upload } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import type { UploadFile } from 'antd/es/upload/interface';
import { UploadOutlined } from '@ant-design/icons';
//接口
import { getSupplierPage, detail, apply, uploadFile } from "../services";
//统一列表分页
import tableProps from '@/utils/tableProps'
import { useSupplierDetailModal } from '@/components/SupplierDetailModalContext/SupplierDetailModalContext';
const approveTypeOptions = [
{ label: '是', value: 'online' },
{ label: '否', value: 'offline' },
];
// 供应商类型
interface Supplier {
id: string;
@ -31,6 +42,7 @@ const SupplierAddModal: React.FC<{
onCancel: () => void;
onSuccess: () => void;
}> = ({ visible, storeId, onCancel, onSuccess }) => {
const [form] = Form.useForm();
// 供应商数据
const [suppliers, setSuppliers] = useState<Supplier[]>([]);
// 已勾选的供应商 id
@ -40,6 +52,9 @@ const SupplierAddModal: React.FC<{
// loading
const [loading, setLoading] = useState(false);
const supplierDetailModal = useSupplierDetailModal();
//供应商符合性审查
const [fileList, setFileList] = useState<UploadFile<any>[]>([]);
// 分页
const [pagination, setPagination] = useState({
current: 1,
@ -54,11 +69,11 @@ const SupplierAddModal: React.FC<{
setLoading(false);
if (res.code === 200 && res.data) {
const inStoreSupplierIds = Array.isArray(inStoreList)
? inStoreList.map((s: any) => typeof s === 'string' ? s : s.id)
: [];
? inStoreList.map((s: any) => typeof s === 'string' ? s : s.id)
: [];
setSuppliers((res.data.records || []).map((s: any) => ({
...s,
inStore: inStoreSupplierIds.includes(s.id),
inStore: inStoreSupplierIds.includes(s.id),
})));
setPagination(p => ({
...p,
@ -74,7 +89,7 @@ const SupplierAddModal: React.FC<{
if (!storeId) return;
const res = await detail({ id: storeId });
if (res.code === 200 && res.data) setCategoryInfo(res.data);
fetchSuppliers(pagination.current, pagination.pageSize, res.data.suppliers || []);
fetchSuppliers(pagination.current, pagination.pageSize, res.data.suppliers || []);
};
useEffect(() => {
@ -99,33 +114,91 @@ const SupplierAddModal: React.FC<{
};
// “移除”操作
const handleRemove = (id: string) => {
setSuppliers(suppliers.map(s =>
s.id === id ? { ...s, inStore: false } : s
));
message.success('移除成功');
};
// const handleRemove = (id: string) => {
// setSuppliers(suppliers.map(s =>
// s.id === id ? { ...s, inStore: false } : s
// ));
// message.success('移除成功');
// };
// 确认入库
const handleOk = () => {
if(selectedIds.length === 0) {
const handleOk = async () => {
if (selectedIds.length === 0) {
message.warning('请选择入库供应商');
return
}
apply({ categoryLibraryId: storeId ,supplierIds: selectedIds })
const values = await form.validateFields();
if(values.approveType === 'offline') {
if (values.attachments.length === 0) {
message.warning('请上传附件');
return
}
} else if(!values.approveType) {
message.warning('请选择签报');
return
}
//
if(values.attachments && values.attachments.length > 0) {
values.coscoCategoryLibrarAttachments = {
...values.attachments[0].response,
fileUrl: values.attachments[0].response.url,
attachmentsType: 'accessory'
}
}
apply({ categoryLibraryId: storeId, supplierIds: selectedIds, ...values })
setSelectedIds([]);
onSuccess && onSuccess();
};
// 自定义上传
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 columns: ColumnsType<Supplier> = [
{ title: '序号', dataIndex: 'id', align: 'center', width: 60, render: (t: any, r: any, i: number) => (pagination.current - 1) * pagination.pageSize + i + 1 },
{ title: '供应商名称', dataIndex: 'name', align: 'center', ellipsis: true },
{ title: '境内/境外', dataIndex: 'supplierType', align: 'center',
render: (ext: any, record: any) => (<span>{`${record.supplierCategory === 'dvs'? '境内企业':'境外企业'}`}</span>) },
{ title: '统一社会信用代码', dataIndex: 'socialCreditCode', align: 'center', ellipsis: true },
{ title: '企业类型', dataIndex: 'categoryName', align: 'center' , ellipsis: true },
{
title: "供应商名称", dataIndex: "name", align: "left",
width: 200,
ellipsis: true,
render: (dom, record) => {
return (
<Tooltip>
<a onClick={() => supplierDetailModal?.(record.id)}>{record.name || ''}</a>
</Tooltip>
)
}
},
{
title: '境内/境外', dataIndex: 'supplierType', align: 'center',
render: (ext: any, record: any) => (<span>{`${record.supplierCategory === 'dvs' ? '境内企业' : '境外企业'}`}</span>)
},
{ title: '统一社会信用代码', dataIndex: 'unifiedCode', align: 'center', ellipsis: true },
{ title: '企业类型', dataIndex: 'categoryName', align: 'center', ellipsis: true },
{
title: '操作',
align: 'center',
@ -134,14 +207,6 @@ const SupplierAddModal: React.FC<{
return (
<>
<span style={{ color: '#aaa' }}></span>
{/* <Popconfirm
title="确认移除该供应商?"
onConfirm={() => handleRemove(record.id)}
okText="确认"
cancelText="取消"
>
<Button size="small" type="link" danger style={{ marginLeft: 6 }}>移除</Button>
</Popconfirm> */}
</>
);
}
@ -180,23 +245,117 @@ const SupplierAddModal: React.FC<{
style={{ marginBottom: 18, background: '#fff', borderRadius: 4 }}
labelStyle={{ width: 110, fontWeight: 500 }}
>
<Descriptions.Item label="品类库名称">
{categoryInfo?.name || '-'}
</Descriptions.Item>
<Descriptions.Item label="有效期至">
{categoryInfo?.termOfValidity || '-'}
</Descriptions.Item>
<Descriptions.Item label="负责部门">
{categoryInfo?.deptName || '-'}
</Descriptions.Item>
<Descriptions.Item label="区域选择">
{categoryInfo?.area || '-'}
</Descriptions.Item>
<Descriptions.Item label="品类结构">
{categoryInfo?.categoryName || '-'}
</Descriptions.Item>
<Descriptions.Item label="品类库名称">
{categoryInfo?.name || '-'}
</Descriptions.Item>
<Descriptions.Item label="有效期至">
{categoryInfo?.termOfValidity || '-'}
</Descriptions.Item>
<Descriptions.Item label="负责部门">
{categoryInfo?.deptName || '-'}
</Descriptions.Item>
<Descriptions.Item label="区域选择">
{categoryInfo?.area || '-'}
</Descriptions.Item>
<Descriptions.Item label="品类结构">
{categoryInfo?.categoryName || '-'}
</Descriptions.Item>
</Descriptions>
</div>
{/* 供应商表格 */}
<div style={{
display: 'flex',
alignItems: 'center',
padding: '8px 0 8px 0',
fontSize: 16,
fontWeight: 500,
}}>
<span></span>
</div>
<Form
form={form}
layout="horizontal"
colon={false}
onValuesChange={(changedValues) => {
if ('approveType' in changedValues) {
form.validateFields(['attachments']); // 手动校验附件
}
}}
>
<Form.Item
label="是否已走OA签报"
name="approveType"
rules={[{ required: true, message: '请选择否已走OA签报' }]}
required
>
<Select
placeholder="请选择否已走OA签报"
style={{ width: 260 }}
options={approveTypeOptions}
/>
</Form.Item>
<Form.Item shouldUpdate={(prev, curr) => prev.approveType !== curr.approveType}>
{() => {
const isAttachmentRequired = form.getFieldValue('approveType') === 'offline';
return (
<Form.Item
label={
<span>
{isAttachmentRequired && <span style={{ color: 'red' }}>*</span>}
OA签报证明文件
</span>
}
name="attachments"
rules={[
{
validator: (_, value) => {
if (isAttachmentRequired && (!value || value.length === 0)) {
return Promise.reject('请上传OA签报附件');
}
return Promise.resolve();
},
},
]}
valuePropName="fileList"
getValueFromEvent={(e) => (Array.isArray(e) ? e : e?.fileList)}
>
<Upload
fileList={fileList}
customRequest={handleCustomRequest}
beforeUpload={(file) => {
if (fileList.length >= 1) {
message.error('只能上传一个文件');
return Upload.LIST_IGNORE;
}
return true;
}}
onChange={({ fileList: newFileList }) => {
setFileList(newFileList);
form.setFieldsValue({
supplierCompliance: newFileList,
attachments: newFileList,
});
}}
onRemove={(file) => {
const newList = fileList.filter(item => item.uid !== file.uid);
setFileList(newList);
form.setFieldsValue({
supplierCompliance: newList,
attachments: newList,
});
}}
accept=".pdf,.doc,.docx"
maxCount={1}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
</Form.Item>
);
}}
</Form.Item>
</Form>
{/* 供应商表格 */}
<div style={{
display: 'flex',
@ -207,23 +366,18 @@ const SupplierAddModal: React.FC<{
}}>
<span></span>
</div>
<Spin spinning={loading}>
<Table
columns={columns}
dataSource={suppliers}
rowKey="id"
pagination={{
current: pagination.current,
pageSize: pagination.pageSize,
total: pagination.total,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page, pageSize) => handleTableChange({ current: page, pageSize }),
}}
style={{ flex: 1, minHeight: 0 }}
scroll={{ y: 'calc(100vh - 650px)' }}
/>
</Spin>
<Table
columns={columns}
dataSource={suppliers}
loading={loading}
rowKey="id"
pagination={{ ...tableProps.pagination, total: pagination.total }}
onChange={(pagination) => {
handleTableChange({ current: pagination.current!, pageSize: pagination.pageSize! })
}}
style={{ flex: 1, minHeight: 0 }}
scroll={{ y: 'calc(100vh - 650px)' }}
/>
</Modal>
);
};