供应商退出、准入、 工作台
This commit is contained in:
@ -0,0 +1,180 @@
|
||||
import React, { useEffect , useState } from "react";
|
||||
import { Modal, Button, Select, Input, Table, message, Form } from "antd";
|
||||
import SupplierSelectModal from "./SupplierSelectModal";
|
||||
import { add } from "../services";
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
const { Option } = Select;
|
||||
interface Supplier {
|
||||
id: number; // 作为 rowKey 用于唯一标识
|
||||
supplierName: string; // 供应商名称
|
||||
unit: string; // 准入部门
|
||||
accessTime: string; // 准入时间
|
||||
categoryName: string; // 准入品类
|
||||
lastEval: string; // 最近一次评价
|
||||
lastEvalDate: string; // 评价时间
|
||||
supplierId: string;
|
||||
categoryId: string;
|
||||
}
|
||||
|
||||
interface CreateBlacklistModalProps {
|
||||
visible: boolean;
|
||||
onOk: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const DEPT_OPTIONS = [
|
||||
{ value: "DEPT001", label: "部门A" },
|
||||
{ value: "DEPT002", label: "部门B" },
|
||||
];
|
||||
|
||||
const CreateBlacklistModal: React.FC<CreateBlacklistModalProps> = ({
|
||||
visible,
|
||||
onOk,
|
||||
onCancel,
|
||||
}) => {
|
||||
const [selectVisible, setSelectVisible] = useState(false);
|
||||
const [suppliers, setSuppliers] = useState<Supplier[]>([]);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const removeSupplier = (id: number) => {
|
||||
setSuppliers(suppliers.filter((s) => s.id !== id));
|
||||
};
|
||||
|
||||
const handleSelectOk = (selected: Supplier[]) => {
|
||||
setSuppliers(selected);
|
||||
setSelectVisible(false);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
if (suppliers.length === 0) {
|
||||
message.error("请至少选择一个供应商");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: {
|
||||
coscoSupplierexit: {
|
||||
exitTheme: string;
|
||||
exitReason: string;
|
||||
deptId: string;
|
||||
},
|
||||
supplierIdList: string[],
|
||||
coscoSupplierexitSupplierCategoryList: { supplierId: string; categoryId: string; }[];
|
||||
|
||||
} = {
|
||||
coscoSupplierexit: {...values},
|
||||
supplierIdList: suppliers.map((item) => item.supplierId),
|
||||
coscoSupplierexitSupplierCategoryList: suppliers.map((item) => ({ supplierId: item.supplierId, categoryId: item.categoryId }))
|
||||
}
|
||||
const res = await add(payload);
|
||||
if (res?.success) {
|
||||
message.success("提交成功");
|
||||
onOk();
|
||||
} else {
|
||||
message.error("提交失败");
|
||||
}
|
||||
} catch (err) {
|
||||
// 校验失败
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setSuppliers([]);
|
||||
form.resetFields();
|
||||
}
|
||||
}, [visible]);
|
||||
const columns: ColumnsType<Supplier> = [
|
||||
{ title: "供应商名称", dataIndex: "supplierName", align: "center" },
|
||||
{ title: "准入部门", dataIndex: "unit", align: "center" },
|
||||
{ title: "准入时间", dataIndex: "accessTime", align: "center", render: () => "2023-04-20 13:00" },
|
||||
{ title: "准入品类", dataIndex: "categoryName", align: "center" },
|
||||
{ title: "最近一次评价", dataIndex: "lastEval", align: "center" },
|
||||
{ title: "评价时间", dataIndex: "lastEvalDate", align: "center" },
|
||||
{
|
||||
title: "操作",
|
||||
dataIndex: "option",
|
||||
align: "center",
|
||||
render: (_: any, record: Supplier) => (
|
||||
<Button type="link" onClick={() => removeSupplier(record.id)}>移除</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="供应商退出"
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
width={900}
|
||||
footer={null}
|
||||
destroyOnClose
|
||||
bodyStyle={{ padding: "36px 36px 16px" }}
|
||||
>
|
||||
<Button type="primary" style={{ marginBottom: 18 }} onClick={() => setSelectVisible(true)}>
|
||||
选择供应商
|
||||
</Button>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={suppliers}
|
||||
rowKey="id"
|
||||
bordered
|
||||
pagination={false}
|
||||
/>
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<Form
|
||||
form={form}
|
||||
labelCol={{ flex: '100px' }} // label 固定宽度(也可写 'none')
|
||||
wrapperCol={{ flex: 1 }} // 输入框区域自适应剩余空间
|
||||
layout="horizontal"
|
||||
>
|
||||
<Form.Item
|
||||
label="申请主题"
|
||||
name="exitTheme"
|
||||
rules={[{ required: true, message: '请输入申请主题' }]}
|
||||
>
|
||||
<Input placeholder="请输入申请主题" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="发起部门"
|
||||
name="deptId"
|
||||
rules={[{ required: true, message: '请选择发起部门' }]}
|
||||
>
|
||||
<Select placeholder="请选择发起部门">
|
||||
{DEPT_OPTIONS.map((opt) => (
|
||||
<Option key={opt.value} value={opt.value}>{opt.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="退出原因"
|
||||
name="exitReason"
|
||||
rules={[{ required: true, message: '请输入退出原因' }]}
|
||||
>
|
||||
<Input.TextArea placeholder="请输入退出原因" rows={4} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 28, textAlign: "center" }}>
|
||||
<Button type="primary" style={{ marginRight: 24 }} onClick={handleSubmit}>提交审批</Button>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<SupplierSelectModal
|
||||
visible={selectVisible}
|
||||
selectedSuppliers={suppliers}
|
||||
onOk={handleSelectOk}
|
||||
onCancel={() => setSelectVisible(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateBlacklistModal;
|
@ -0,0 +1,226 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
Modal, Table, Button, Checkbox, Row, Col, Input, Select, Form, Space, message
|
||||
} from "antd";
|
||||
import { getSupplierCategoryPage } from '../services';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
interface Supplier {
|
||||
id: number;
|
||||
supplierId: string;
|
||||
categoryId: string;
|
||||
supplierName: string;
|
||||
|
||||
unit: string; // 准入部门
|
||||
accessTime: string; // 准入时间
|
||||
categoryName: string; // 准入品类
|
||||
lastEval: string; // 最近一次评价
|
||||
lastEvalDate: string; // 评价时间
|
||||
|
||||
}
|
||||
|
||||
interface SupplierSelectModalProps {
|
||||
visible: boolean;
|
||||
selectedSuppliers: Supplier[];
|
||||
onOk: (suppliers: Supplier[]) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
|
||||
const SupplierSelectModal: React.FC<SupplierSelectModalProps> = ({
|
||||
visible,
|
||||
selectedSuppliers,
|
||||
onOk,
|
||||
onCancel,
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [leftSelected, setLeftSelected] = useState<number[]>([]);
|
||||
const [rightSelected, setRightSelected] = useState<number[]>([]);
|
||||
const [rightData, setRightData] = useState<Supplier[]>([]);
|
||||
const [data, setData] = useState<Supplier[]>([]);
|
||||
const [pagination, setPagination] = useState({ current: 1, pageSize: 5, total: 0 });
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setRightData(selectedSuppliers || []);
|
||||
fetchData();
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
const fetchData = async (params: any = {}, pageNo = 1, pageSize = 5) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await getSupplierCategoryPage({ ...params, pageNo, pageSize });
|
||||
if (res.code === 200) {
|
||||
setData(res.data.records);
|
||||
setPagination({ current: pageNo, pageSize: pageSize, total: res.data.total });
|
||||
} else {
|
||||
message.error('获取数据失败');
|
||||
}
|
||||
} catch {
|
||||
message.error('获取数据异常');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
const values = form.getFieldsValue();
|
||||
fetchData(values, 1, pagination.pageSize);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
fetchData({}, 1, pagination.pageSize);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
const added = data.filter(s => leftSelected.includes(s.id));
|
||||
const combined = [...rightData, ...added.filter(i => !rightData.some(r => r.id === i.id))];
|
||||
setRightData(combined);
|
||||
setLeftSelected([]);
|
||||
};
|
||||
|
||||
const handleRemove = () => {
|
||||
setRightData(rightData.filter(i => !rightSelected.includes(i.id)));
|
||||
setRightSelected([]);
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
onOk(rightData);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: <Checkbox
|
||||
checked={leftSelected.length === data.length && data.length > 0}
|
||||
indeterminate={leftSelected.length > 0 && leftSelected.length < data.length}
|
||||
onChange={e => {
|
||||
setLeftSelected(e.target.checked ? data.map(i => i.id) : []);
|
||||
}}
|
||||
>全选</Checkbox>,
|
||||
dataIndex: "select",
|
||||
width: 80,
|
||||
render: (_: any, record: Supplier) => (
|
||||
<Checkbox
|
||||
checked={leftSelected.includes(record.id)}
|
||||
onChange={e => {
|
||||
setLeftSelected(e.target.checked
|
||||
? [...leftSelected, record.id]
|
||||
: leftSelected.filter(id => id !== record.id));
|
||||
}}
|
||||
>选择</Checkbox>
|
||||
)
|
||||
},
|
||||
{ title: "供应商名称", dataIndex: "supplierName" },
|
||||
{ title: "准入单位", dataIndex: "deptId" },
|
||||
{ title: "准入部门", dataIndex: "deptId" },
|
||||
{ title: "准入品类", dataIndex: "categoryName" }
|
||||
];
|
||||
|
||||
const rightColumns = [
|
||||
{
|
||||
title: <Checkbox
|
||||
checked={rightSelected.length === rightData.length && rightData.length > 0}
|
||||
indeterminate={rightSelected.length > 0 && rightSelected.length < rightData.length}
|
||||
onChange={e => {
|
||||
setRightSelected(e.target.checked ? rightData.map(i => i.id) : []);
|
||||
}}
|
||||
>全选</Checkbox>,
|
||||
dataIndex: "select",
|
||||
width: 80,
|
||||
render: (_: any, record: Supplier) => (
|
||||
<Checkbox
|
||||
checked={rightSelected.includes(record.id)}
|
||||
onChange={e => {
|
||||
setRightSelected(e.target.checked
|
||||
? [...rightSelected, record.id]
|
||||
: rightSelected.filter(id => id !== record.id));
|
||||
}}
|
||||
>选择</Checkbox>
|
||||
)
|
||||
},
|
||||
{ title: "供应商名称", dataIndex: "supplierName" },
|
||||
{ title: "准入单位", dataIndex: "deptId" },
|
||||
{ title: "准入部门", dataIndex: "deptId" },
|
||||
{ title: "准入品类", dataIndex: "categoryName" }
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="选择供应商"
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
onOk={handleOk}
|
||||
width={1000}
|
||||
destroyOnClose
|
||||
okText="确认"
|
||||
cancelText="取消"
|
||||
bodyStyle={{ padding: 24 }}
|
||||
>
|
||||
<Form layout="inline" form={form} style={{ marginBottom: 16 }}>
|
||||
<Form.Item name="name" label="供应商名称">
|
||||
<Input style={{ width: 180 }} allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="evalLevel" label="最近一次评价等级">
|
||||
<Select style={{ width: 140 }} allowClear>
|
||||
{['A', 'B', 'C', 'D'].map(level => (
|
||||
<Option key={level} value={level}>{level}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="category" label="准入品类">
|
||||
<Select style={{ width: 140 }} allowClear>
|
||||
{['燃油', '润滑油', '备件'].map(cat => (
|
||||
<Option key={cat} value={cat}>{cat}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" onClick={handleSearch}>查询</Button>
|
||||
<Button onClick={handleReset}>重置</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<Row gutter={32}>
|
||||
<Col span={11}>
|
||||
<div style={{ marginBottom: 8 }}>备选供应商</div>
|
||||
<Table
|
||||
rowKey="id"
|
||||
dataSource={data}
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
pagination={pagination}
|
||||
onChange={(pag) => {
|
||||
const values = form.getFieldsValue();
|
||||
fetchData(values, pag.current!, pag.pageSize!);
|
||||
}}
|
||||
size="small"
|
||||
bordered
|
||||
/>
|
||||
</Col>
|
||||
<Col span={2} style={{ textAlign: "center", display: "flex", flexDirection: "column", justifyContent: "center" }}>
|
||||
<Button type="primary" disabled={!leftSelected.length} onClick={handleAdd} style={{ marginBottom: 16 }}>{">"}</Button>
|
||||
<Button disabled={!rightSelected.length} onClick={handleRemove}>{"<"}</Button>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<div style={{ marginBottom: 8 }}>已选供应商</div>
|
||||
<Table
|
||||
rowKey="id"
|
||||
dataSource={rightData}
|
||||
columns={rightColumns}
|
||||
pagination={false}
|
||||
size="small"
|
||||
bordered
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupplierSelectModal;
|
@ -0,0 +1,113 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Modal, Button, Table, Descriptions, Spin, message } from "antd";
|
||||
import {coscoSupplierexit } from "../services"; // 假设的接口
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
|
||||
interface Supplier {
|
||||
id: number; // 作为 rowKey 用于唯一标识
|
||||
supplierName: string; // 供应商名称
|
||||
unit: string; // 准入部门
|
||||
accessTime: string; // 准入时间
|
||||
categoryName: string; // 准入品类
|
||||
lastEval: string; // 最近一次评价
|
||||
lastEvalDate: string; // 评价时间
|
||||
supplierId: string;
|
||||
categoryId: string;
|
||||
}
|
||||
interface DetailData {
|
||||
exitTheme: string;
|
||||
deptName: string;
|
||||
deptId: string;
|
||||
exitReason: string;
|
||||
}
|
||||
|
||||
interface ViewBlacklistModalProps {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
recordId: string; // 用于查询详情数据
|
||||
}
|
||||
|
||||
const ViewBlacklistModal: React.FC<ViewBlacklistModalProps> = ({
|
||||
visible,
|
||||
onCancel,
|
||||
recordId,
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [detail, setDetail] = useState<DetailData | null>(null);
|
||||
const [suppliers, setSuppliers] = useState<Supplier[]>([]);
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { code, data } = await coscoSupplierexit(recordId)
|
||||
if (code == 200) {
|
||||
setDetail(data.coscoSupplierexit);
|
||||
} else {
|
||||
message.error("获取详情失败");
|
||||
}
|
||||
|
||||
if (code == 200) {
|
||||
setSuppliers(data.coscoSupplierexitSupplierCategoryList || []);
|
||||
} else {
|
||||
message.error("获取供应商列表失败");
|
||||
}
|
||||
} catch (error) {
|
||||
message.error("获取数据异常");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (visible && recordId) {
|
||||
fetchData();
|
||||
}
|
||||
}, [visible, recordId]);
|
||||
|
||||
const columns:ColumnsType<Supplier> = [
|
||||
{ title: "供应商名称", dataIndex: "supplierName", align: "center" },
|
||||
{ title: "准入部门", dataIndex: "unit", align: "center" },
|
||||
{ title: "准入时间", dataIndex: "accessTime", align: "center" },
|
||||
{ title: "准入品类", dataIndex: "categoryName", align: "center" },
|
||||
{ title: "最近一次评价", dataIndex: "lastEval", align: "center" },
|
||||
{ title: "评价时间", dataIndex: "lastEvalDate", align: "center" },
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="供应商退出详情"
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
width={900}
|
||||
footer={<Button onClick={onCancel}>关闭</Button>}
|
||||
destroyOnClose
|
||||
bodyStyle={{ padding: "36px 36px 16px" }}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
{detail && (
|
||||
<Descriptions
|
||||
title="退出信息"
|
||||
column={1}
|
||||
labelStyle={{ width: 120 }}
|
||||
bordered
|
||||
style={{ marginBottom: 24 }}
|
||||
>
|
||||
<Descriptions.Item label="申请主题">{detail.exitTheme}</Descriptions.Item>
|
||||
<Descriptions.Item label="发起部门">{detail.deptId}</Descriptions.Item>
|
||||
<Descriptions.Item label="退出原因" >{detail.exitReason}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
)}
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={suppliers}
|
||||
rowKey="id"
|
||||
bordered
|
||||
pagination={false}
|
||||
/>
|
||||
</Spin>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewBlacklistModal;
|
@ -1,167 +1,131 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Form, Button, Table, Select, Input, Space, DatePicker, message } from "antd";
|
||||
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
// 类型定义
|
||||
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
|
||||
|
||||
// Mock数据
|
||||
const MOCK_DATA = Array.from({ length: 188 }).map((_, idx) => ({
|
||||
id: idx + 1,
|
||||
subject: `${idx % 2 ? '中山市合创展包装材料有限公司' : '上海硕建建筑技术工程有限公司'}等黑灰名单申请`,
|
||||
unit: idx % 3 === 0 ? '中远海运特运' : idx % 3 === 1 ? '中远海运散运' : '中远海运广州公司',
|
||||
dept: '采购部',
|
||||
applyTime: `2024-10-${String(26 - (idx % 5)).padStart(2, "0")} ${idx % 2 ? '9:00' : '10:00'}`,
|
||||
status: idx < 3 ? '未开始' : idx < 7 ? '进行中' : '已结束',
|
||||
result: idx < 8 ? '' : (idx % 2 ? '通过' : '驳回'),
|
||||
}));
|
||||
|
||||
// 状态、结果选项
|
||||
const statusOptions = [
|
||||
{ label: "请选择", value: "" },
|
||||
{ label: "未开始", value: "未开始" },
|
||||
{ label: "进行中", value: "进行中" },
|
||||
{ label: "已结束", value: "已结束" },
|
||||
];
|
||||
const resultOptions = [
|
||||
{ label: "请选择", value: "" },
|
||||
{ label: "通过", value: "通过" },
|
||||
{ label: "驳回", value: "驳回" },
|
||||
];
|
||||
|
||||
// 列表类型
|
||||
interface DataType {
|
||||
id: number;
|
||||
subject: string;
|
||||
unit: string;
|
||||
dept: string;
|
||||
applyTime: string;
|
||||
status: string;
|
||||
result: string;
|
||||
import { Form, Button, Table, Select, Input, DatePicker, Space, message } from "antd";
|
||||
import { SearchOutlined, ReloadOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import CreateBlacklistModal from './components/CreateBlacklistModal'
|
||||
import ViewBlacklistModal from './components/ViewBlacklistModal';
|
||||
import { getPage } from './services'
|
||||
interface ExitRecord {
|
||||
exitTheme: string; // 申请主题
|
||||
deptId: string; // 发起单位/部门(两列共用)
|
||||
createTime: string; // 发起时间
|
||||
accessType: string; // 审批记录状态
|
||||
}
|
||||
// mock 数据与服务(实际请换为接口)
|
||||
const orgOptions = [
|
||||
{ label: "请选择", value: "" },
|
||||
{ label: "中远海运特运", value: "中远海运特运" },
|
||||
{ label: "中远海运散运", value: "中远海运散运" },
|
||||
{ label: "中远海运物流", value: "中远海运物流" },
|
||||
{ label: "中远海运广州公司", value: "中远海运广州公司" },
|
||||
];
|
||||
|
||||
// 查询记录状态
|
||||
const recordStatusOptions = [
|
||||
{ label: "请选择", value: "" },
|
||||
{ label: "未开始", value: "未开始" },
|
||||
{ label: "进行中", value: "进行中" },
|
||||
{ label: "已结束", value: "已结束" },
|
||||
];
|
||||
|
||||
const supplierExitAudit: React.FC = () => {
|
||||
// 搜索表单
|
||||
const [form] = Form.useForm();
|
||||
// 列表数据
|
||||
const [data, setData] = useState<DataType[]>([]);
|
||||
// 加载
|
||||
const [loading, setLoading] = useState(false);
|
||||
// 分页
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: MOCK_DATA.length,
|
||||
});
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
|
||||
const [createVisible, setCreateVisible] = useState(false);
|
||||
const [viewVisible, setViewVisible] = useState(false);
|
||||
const [selectedRecordId, setSelectedRecordId] = useState<string | null>(null);
|
||||
// 查询接口
|
||||
const getList = async (pageNo = 1, pageSize = 10) => {
|
||||
setLoading(true);
|
||||
// 可传查询条件 form.getFieldsValue()
|
||||
const { code, data } = await getPage({ pageNo, pageSize });
|
||||
if (code === 200) {
|
||||
setData(data.records);
|
||||
setPagination(prev => ({ ...prev, current: pageNo, pageSize, total: data.total }));
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleSearch = () => {
|
||||
setPagination({ ...pagination, current: 1 });
|
||||
getList(1, pagination.pageSize);
|
||||
};
|
||||
|
||||
// 重置
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
setPagination({ ...pagination, current: 1 });
|
||||
getList(1, pagination.pageSize);
|
||||
};
|
||||
|
||||
// 获取列表(模拟接口,支持分页和部分筛选)
|
||||
const getList = async (page: number = 1, pageSize: number = 10) => {
|
||||
setLoading(true);
|
||||
setTimeout(() => {
|
||||
let filtered = MOCK_DATA;
|
||||
const values = form.getFieldsValue();
|
||||
if (values.subject) {
|
||||
filtered = filtered.filter(item => item.subject.includes(values.subject));
|
||||
}
|
||||
if (values.unit) {
|
||||
filtered = filtered.filter(item => item.unit === values.unit);
|
||||
}
|
||||
if (values.status) {
|
||||
filtered = filtered.filter(item => item.status === values.status);
|
||||
}
|
||||
if (values.result) {
|
||||
filtered = filtered.filter(item => item.result === values.result);
|
||||
}
|
||||
// 日期筛选略
|
||||
setData(filtered.slice((page - 1) * pageSize, page * pageSize));
|
||||
setPagination({ current: page, pageSize, total: filtered.length });
|
||||
setLoading(false);
|
||||
}, 300);
|
||||
// 分页
|
||||
const handleTableChange = (pageConf: any) => {
|
||||
getList(pageConf.current, pageConf.pageSize);
|
||||
};
|
||||
|
||||
// 初始化
|
||||
useEffect(() => {
|
||||
// 发起申请
|
||||
const handleApply = () => {
|
||||
setCreateVisible(true)
|
||||
};
|
||||
const handleSelectOk = () => {
|
||||
getList();
|
||||
}, []);
|
||||
setCreateVisible(false);
|
||||
};
|
||||
useEffect(() => {
|
||||
getList();
|
||||
}, []);
|
||||
|
||||
// 列
|
||||
const columns: ColumnsType<DataType> = [
|
||||
const columns: ColumnsType<ExitRecord> = [
|
||||
{
|
||||
title: "序号",
|
||||
dataIndex: "index",
|
||||
key: "index",
|
||||
align: "center",
|
||||
width: 60,
|
||||
render: (_: any, __: any, idx: number) =>
|
||||
(pagination.current! - 1) * pagination.pageSize! + idx + 1,
|
||||
render: (_: any, __: any, idx: number) => idx + 1,
|
||||
},
|
||||
{ title: "申请主题", dataIndex: "subject", key: "subject", align: "center", ellipsis: true },
|
||||
{ title: "发起单位", dataIndex: "unit", key: "unit", align: "center" },
|
||||
{ title: "发起部门", dataIndex: "dept", key: "dept", align: "center" },
|
||||
{ title: "申请时间", dataIndex: "applyTime", key: "applyTime", align: "center" },
|
||||
{ title: "流程状态", dataIndex: "status", key: "status", align: "center" },
|
||||
{ title: "审批结果", dataIndex: "result", key: "result", align: "center" },
|
||||
{ title: "申请主题", dataIndex: "exitTheme", key: "exitTheme", align: "center" },
|
||||
{ title: "发起单位", dataIndex: "deptId", key: "deptId", align: "center" },
|
||||
{ title: "发起部门", dataIndex: "deptId", key: "deptId", align: "center" },
|
||||
{ title: "发起时间", dataIndex: "createTime", key: "createTime", align: "center" },
|
||||
{ title: "审批记录状态", dataIndex: "approveStatusText", key: "approveStatusText", align: "center" },
|
||||
{
|
||||
title: "操作",
|
||||
key: "option",
|
||||
align: "center",
|
||||
render: (record: DataType) => (
|
||||
<Space>
|
||||
<a style={{ color: "#1677ff" }}>审批</a>
|
||||
<a style={{ color: "#1677ff" }}>审批记录</a>
|
||||
</Space>
|
||||
render: (record: any) => (
|
||||
<a
|
||||
onClick={() => {
|
||||
setSelectedRecordId(record.id);
|
||||
setViewVisible(true);
|
||||
}}
|
||||
>
|
||||
查看
|
||||
</a>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// 单元下拉(mock示例)
|
||||
const unitOptions = [
|
||||
{ label: "请选择", value: "" },
|
||||
{ label: "中远海运特运", value: "中远海运特运" },
|
||||
{ label: "中远海运散运", value: "中远海运散运" },
|
||||
{ label: "中远海运广州公司", value: "中远海运广州公司" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* 搜索区 */}
|
||||
<>
|
||||
|
||||
{/* 查询表单 */}
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 12 }}>
|
||||
<Form.Item name="subject" label="申请主题">
|
||||
<Input placeholder="请输入供应商名称关键字" style={{ width: 220 }} allowClear />
|
||||
<Form.Item name="topic" label="申请主题">
|
||||
<Input placeholder="请输入供应商名称关键字" style={{ width: 180 }} />
|
||||
</Form.Item>
|
||||
<Form.Item name="unit" label="发起单位">
|
||||
<Select style={{ width: 160 }}>
|
||||
{unitOptions.map(opt => <Select.Option value={opt.value} key={opt.value}>{opt.label}</Select.Option>)}
|
||||
</Select>
|
||||
<Form.Item name="org" label="发起单位">
|
||||
<Select style={{ width: 150 }} allowClear options={orgOptions} />
|
||||
</Form.Item>
|
||||
<Form.Item name="applyTime" label="申请时间">
|
||||
<Form.Item name="status" label="审批记录状态">
|
||||
<Select style={{ width: 130 }} allowClear options={recordStatusOptions} />
|
||||
</Form.Item>
|
||||
<Form.Item name="time" label="发起时间">
|
||||
<DatePicker.RangePicker style={{ width: 220 }} />
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="流程状态">
|
||||
<Select style={{ width: 120 }}>
|
||||
{statusOptions.map(opt => <Select.Option value={opt.value} key={opt.value}>{opt.label}</Select.Option>)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="result" label="审批结果">
|
||||
<Select style={{ width: 120 }}>
|
||||
{resultOptions.map(opt => <Select.Option value={opt.value} key={opt.value}>{opt.label}</Select.Option>)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" icon={<SearchOutlined />} onClick={handleSearch} style={{ marginRight: 8 }}>查询</Button>
|
||||
<Button icon={<ReloadOutlined />} onClick={handleReset}>重置</Button>
|
||||
<Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>查询</Button>
|
||||
<Button style={{ marginLeft: 8 }} icon={<ReloadOutlined />} onClick={handleReset}>重置</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
{/* 表格 */}
|
||||
@ -174,13 +138,19 @@ const supplierExitAudit: React.FC = () => {
|
||||
...pagination,
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
showTotal: t => `共${t}条`,
|
||||
onChange: (current, pageSize) => getList(current, pageSize),
|
||||
showTotal: total => `共${total}条`,
|
||||
}}
|
||||
onChange={handleTableChange}
|
||||
bordered
|
||||
style={{ background: '#fff' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CreateBlacklistModal visible={createVisible} onCancel={() => setCreateVisible(false)} onOk={handleSelectOk} />
|
||||
<ViewBlacklistModal
|
||||
visible={viewVisible}
|
||||
recordId={selectedRecordId || ''}
|
||||
onCancel={() => setViewVisible(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,64 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 品类准入管理列表
|
||||
*/
|
||||
interface getPageData {
|
||||
pageNo: number;
|
||||
pageSize: number;
|
||||
deptId?: string;
|
||||
accessType?: string;
|
||||
reviewStatus?: string;
|
||||
approveStatus?: string;
|
||||
categoryId?: string;
|
||||
}
|
||||
export const getPage = (data: getPageData) => request.post('/coscoSupplierexit/getPage', { data });
|
||||
/**
|
||||
* 供应商分页列表查询
|
||||
*/
|
||||
interface getSupplierCategoryPageData {
|
||||
pageNo: number;
|
||||
pageSize: number;
|
||||
supplierName?: number;
|
||||
categoryId?: string;
|
||||
reviewResult?: string;
|
||||
}
|
||||
|
||||
export const getSupplierCategoryPage = (data: getSupplierCategoryPageData) => request.post('/coscoSupplierexit/getSupplierCategoryPage', { data });
|
||||
|
||||
|
||||
/**
|
||||
* 的供应商分页列表查询
|
||||
*/
|
||||
export interface addInterface {
|
||||
coscoSupplierexit: CoscoSupplierexit;
|
||||
coscoSupplierexitSupplierCategoryList: CoscoSupplierexitSupplierCategoryList[];
|
||||
supplierIdList: string[];
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
export interface CoscoSupplierexit {
|
||||
deptId: string;
|
||||
exitReason: string;
|
||||
exitTheme: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
export interface CoscoSupplierexitSupplierCategoryList {
|
||||
categoryId: string;
|
||||
supplierId: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
export const add = (data: addInterface) => request.post('/coscoSupplierexit/add', { data });
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 退出详情页(没分页)
|
||||
*/
|
||||
export const coscoSupplierexit = (id: string) => request.get(`/coscoSupplierexit/${id}`);
|
||||
|
||||
|
||||
|
||||
|
@ -1,15 +1,19 @@
|
||||
import React, { useState } from "react";
|
||||
import { Modal, Button, Select, Input, Table, message } from "antd";
|
||||
import SupplierSelectModal from "./SupplierSelectModal"; // 上面生成的组件
|
||||
|
||||
import React, { useEffect , useState } from "react";
|
||||
import { Modal, Button, Select, Input, Table, message, Form } from "antd";
|
||||
import SupplierSelectModal from "./SupplierSelectModal";
|
||||
import { add } from "../services";
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
const { Option } = Select;
|
||||
|
||||
interface Supplier {
|
||||
id: number;
|
||||
name: string;
|
||||
unit: string;
|
||||
dept: string;
|
||||
category: string;
|
||||
id: number; // 作为 rowKey 用于唯一标识
|
||||
supplierName: string; // 供应商名称
|
||||
unit: string; // 准入部门
|
||||
accessTime: string; // 准入时间
|
||||
categoryName: string; // 准入品类
|
||||
lastEval: string; // 最近一次评价
|
||||
lastEvalDate: string; // 评价时间
|
||||
supplierId: string;
|
||||
categoryId: string;
|
||||
}
|
||||
|
||||
interface CreateBlacklistModalProps {
|
||||
@ -18,77 +22,89 @@ interface CreateBlacklistModalProps {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const initialSuppliers: Supplier[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: "中山市合创展包装材料有限公司",
|
||||
unit: "采购部",
|
||||
dept: "采购部",
|
||||
category: "船用燃油",
|
||||
lastEval: "D",
|
||||
lastEvalDate: "2025-02-20",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "广东振兴塑胶机械有限公司",
|
||||
unit: "采购部",
|
||||
dept: "采购部",
|
||||
category: "船用燃油",
|
||||
lastEval: "D",
|
||||
lastEvalDate: "2025-02-20",
|
||||
}
|
||||
const DEPT_OPTIONS = [
|
||||
{ value: "DEPT001", label: "部门A" },
|
||||
{ value: "DEPT002", label: "部门B" },
|
||||
];
|
||||
|
||||
const CreateBlacklistModal: React.FC<CreateBlacklistModalProps> = ({
|
||||
visible, onOk, onCancel
|
||||
visible,
|
||||
onOk,
|
||||
onCancel,
|
||||
}) => {
|
||||
const [selectVisible, setSelectVisible] = useState(false);
|
||||
const [suppliers, setSuppliers] = useState<Supplier[]>(initialSuppliers);
|
||||
const [type, setType] = useState("黑名单");
|
||||
const [reason, setReason] = useState("");
|
||||
const [suppliers, setSuppliers] = useState<Supplier[]>([]);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// 删除已选供应商
|
||||
const removeSupplier = (id: number) => {
|
||||
setSuppliers(suppliers.filter(s => s.id !== id));
|
||||
setSuppliers(suppliers.filter((s) => s.id !== id));
|
||||
};
|
||||
|
||||
// 选择供应商
|
||||
const handleSelectOk = (selected: Supplier[]) => {
|
||||
setSuppliers(selected);
|
||||
setSelectVisible(false);
|
||||
};
|
||||
|
||||
// 提交
|
||||
const handleSubmit = () => {
|
||||
if (!reason.trim()) {
|
||||
message.error("请输入黑灰名单原因!");
|
||||
return;
|
||||
}
|
||||
message.success("提交成功(此处为前端示例)");
|
||||
onOk?.();
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
if (suppliers.length === 0) {
|
||||
message.error("请至少选择一个供应商");
|
||||
return;
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{ title: "供应商名称", dataIndex: "name", align: "center" },
|
||||
const payload: {
|
||||
coscoSupplierexit: {
|
||||
exitTheme: string;
|
||||
exitReason: string;
|
||||
deptId: string;
|
||||
},
|
||||
supplierIdList: string[],
|
||||
coscoSupplierexitSupplierCategoryList: { supplierId: string; categoryId: string; }[];
|
||||
|
||||
} = {
|
||||
coscoSupplierexit: {...values},
|
||||
supplierIdList: suppliers.map((item) => item.supplierId),
|
||||
coscoSupplierexitSupplierCategoryList: suppliers.map((item) => ({ supplierId: item.supplierId, categoryId: item.categoryId }))
|
||||
}
|
||||
const res = await add(payload);
|
||||
if (res?.success) {
|
||||
message.success("提交成功");
|
||||
onOk();
|
||||
} else {
|
||||
message.error("提交失败");
|
||||
}
|
||||
} catch (err) {
|
||||
// 校验失败
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setSuppliers([]);
|
||||
form.resetFields();
|
||||
}
|
||||
}, [visible]);
|
||||
const columns: ColumnsType<Supplier> = [
|
||||
{ title: "供应商名称", dataIndex: "supplierName", align: "center" },
|
||||
{ title: "准入部门", dataIndex: "unit", align: "center" },
|
||||
{ title: "准入时间", dataIndex: "accessTime", align: "center", render: () => "2023-04-20 13:00" },
|
||||
{ title: "准入品类", dataIndex: "category", align: "center" },
|
||||
{ title: "准入品类", dataIndex: "categoryName", align: "center" },
|
||||
{ title: "最近一次评价", dataIndex: "lastEval", align: "center" },
|
||||
{ title: "评价时间", dataIndex: "lastEvalDate", align: "center" },
|
||||
{
|
||||
title: "选择",
|
||||
title: "操作",
|
||||
dataIndex: "option",
|
||||
align: "center",
|
||||
render: (_: any, record: Supplier) => (
|
||||
<Button type="link" onClick={() => removeSupplier(record.id)}>移除</Button>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title={<div style={{ textAlign: "center", fontWeight: 700, fontSize: 28 }}>供应商黑灰名单申请</div>}
|
||||
title="供应商退出"
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
width={900}
|
||||
@ -99,6 +115,7 @@ const CreateBlacklistModal: React.FC<CreateBlacklistModalProps> = ({
|
||||
<Button type="primary" style={{ marginBottom: 18 }} onClick={() => setSelectVisible(true)}>
|
||||
选择供应商
|
||||
</Button>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={suppliers}
|
||||
@ -107,21 +124,49 @@ const CreateBlacklistModal: React.FC<CreateBlacklistModalProps> = ({
|
||||
pagination={false}
|
||||
/>
|
||||
|
||||
|
||||
<div>
|
||||
<Input.TextArea
|
||||
placeholder="退出原因(必须填写)"
|
||||
rows={4}
|
||||
value={reason}
|
||||
onChange={e => setReason(e.target.value)}
|
||||
style={{ width: "100%", marginTop: 8 }}
|
||||
/>
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<Form
|
||||
form={form}
|
||||
labelCol={{ flex: '100px' }} // label 固定宽度(也可写 'none')
|
||||
wrapperCol={{ flex: 1 }} // 输入框区域自适应剩余空间
|
||||
layout="horizontal"
|
||||
>
|
||||
<Form.Item
|
||||
label="申请主题"
|
||||
name="exitTheme"
|
||||
rules={[{ required: true, message: '请输入申请主题' }]}
|
||||
>
|
||||
<Input placeholder="请输入申请主题" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="发起部门"
|
||||
name="deptId"
|
||||
rules={[{ required: true, message: '请选择发起部门' }]}
|
||||
>
|
||||
<Select placeholder="请选择发起部门">
|
||||
{DEPT_OPTIONS.map((opt) => (
|
||||
<Option key={opt.value} value={opt.value}>{opt.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="退出原因"
|
||||
name="exitReason"
|
||||
rules={[{ required: true, message: '请输入退出原因' }]}
|
||||
>
|
||||
<Input.TextArea placeholder="请输入退出原因" rows={4} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 28, textAlign: "center" }}>
|
||||
<Button type="primary" style={{ marginRight: 24 }} onClick={handleSubmit}>提交审批</Button>
|
||||
<Button onClick={onCancel}>取消</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<SupplierSelectModal
|
||||
visible={selectVisible}
|
||||
selectedSuppliers={suppliers}
|
||||
|
@ -1,91 +1,109 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Modal, Table, Button, Checkbox, Row, Col, Input, Select, Pagination, Form, Space } from "antd";
|
||||
import {
|
||||
Modal, Table, Button, Checkbox, Row, Col, Input, Select, Form, Space, message
|
||||
} from "antd";
|
||||
import { getSupplierCategoryPage } from '../services';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
// mock 数据
|
||||
const mockSuppliers = Array.from({ length: 25 }).map((_, i) => ({
|
||||
id: i + 1,
|
||||
name: `XXXX${i + 1}有限公司`,
|
||||
unit: "散运三级单位",
|
||||
dept: "采购部",
|
||||
category: ["燃油", "润滑油", "备件"][i % 3],
|
||||
evalLevel: ["A", "B", "C", "D"][i % 4],
|
||||
}));
|
||||
interface Supplier {
|
||||
id: number;
|
||||
supplierId: string;
|
||||
categoryId: string;
|
||||
supplierName: string;
|
||||
|
||||
const evalOptions = [
|
||||
{ value: "", label: "请选择" },
|
||||
{ value: "A", label: "A" },
|
||||
{ value: "B", label: "B" },
|
||||
{ value: "C", label: "C" },
|
||||
{ value: "D", label: "D" },
|
||||
];
|
||||
unit: string; // 准入部门
|
||||
accessTime: string; // 准入时间
|
||||
categoryName: string; // 准入品类
|
||||
lastEval: string; // 最近一次评价
|
||||
lastEvalDate: string; // 评价时间
|
||||
|
||||
}
|
||||
|
||||
const categoryOptions = [
|
||||
{ value: "", label: "请选择" },
|
||||
{ value: "燃油", label: "燃油" },
|
||||
{ value: "润滑油", label: "润滑油" },
|
||||
{ value: "备件", label: "备件" },
|
||||
];
|
||||
interface SupplierSelectModalProps {
|
||||
visible: boolean;
|
||||
selectedSuppliers: Supplier[];
|
||||
onOk: (suppliers: Supplier[]) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const PAGE_SIZE = 5;
|
||||
|
||||
const SupplierSelectModal = ({
|
||||
const SupplierSelectModal: React.FC<SupplierSelectModalProps> = ({
|
||||
visible,
|
||||
selectedSuppliers,
|
||||
onOk,
|
||||
onCancel,
|
||||
selectedSuppliers,
|
||||
}) => {
|
||||
// 查询表单
|
||||
const [form] = Form.useForm();
|
||||
// 选中
|
||||
const [leftSelected, setLeftSelected] = useState([]);
|
||||
const [rightSelected, setRightSelected] = useState([]);
|
||||
const [rightData, setRightData] = useState(selectedSuppliers || []);
|
||||
const [leftPage, setLeftPage] = useState(1);
|
||||
const [query, setQuery] = useState({});
|
||||
|
||||
// 数据过滤
|
||||
const filteredSuppliers = mockSuppliers.filter(item => {
|
||||
const { name = "", evalLevel = "", category = "" } = query;
|
||||
return (
|
||||
(!name || item.name.includes(name)) &&
|
||||
(!evalLevel || item.evalLevel === evalLevel) &&
|
||||
(!category || item.category === category)
|
||||
);
|
||||
});
|
||||
const pagedSuppliers = filteredSuppliers.slice((leftPage - 1) * PAGE_SIZE, leftPage * PAGE_SIZE);
|
||||
const [leftSelected, setLeftSelected] = useState<number[]>([]);
|
||||
const [rightSelected, setRightSelected] = useState<number[]>([]);
|
||||
const [rightData, setRightData] = useState<Supplier[]>([]);
|
||||
const [data, setData] = useState<Supplier[]>([]);
|
||||
const [pagination, setPagination] = useState({ current: 1, pageSize: 5, total: 0 });
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setRightData(selectedSuppliers || []);
|
||||
}, [selectedSuppliers, visible]);
|
||||
if (visible) {
|
||||
setRightData(selectedSuppliers || []);
|
||||
fetchData();
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
// 查询
|
||||
const handleSearch = () => {
|
||||
setQuery(form.getFieldsValue());
|
||||
setLeftPage(1);
|
||||
setLeftSelected([]);
|
||||
const fetchData = async (params: any = {}, pageNo = 1, pageSize = 5) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await getSupplierCategoryPage({ ...params, pageNo, pageSize });
|
||||
if (res.code === 200) {
|
||||
setData(res.data.records);
|
||||
setPagination({ current: pageNo, pageSize: pageSize, total: res.data.total });
|
||||
} else {
|
||||
message.error('获取数据失败');
|
||||
}
|
||||
} catch {
|
||||
message.error('获取数据异常');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// 重置
|
||||
|
||||
const handleSearch = () => {
|
||||
const values = form.getFieldsValue();
|
||||
fetchData(values, 1, pagination.pageSize);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
setQuery({});
|
||||
setLeftPage(1);
|
||||
fetchData({}, 1, pagination.pageSize);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
const added = data.filter(s => leftSelected.includes(s.id));
|
||||
const combined = [...rightData, ...added.filter(i => !rightData.some(r => r.id === i.id))];
|
||||
setRightData(combined);
|
||||
setLeftSelected([]);
|
||||
};
|
||||
|
||||
const leftColumns = [
|
||||
const handleRemove = () => {
|
||||
setRightData(rightData.filter(i => !rightSelected.includes(i.id)));
|
||||
setRightSelected([]);
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
onOk(rightData);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: <Checkbox
|
||||
checked={leftSelected.length === pagedSuppliers.length && pagedSuppliers.length > 0}
|
||||
indeterminate={leftSelected.length > 0 && leftSelected.length < pagedSuppliers.length}
|
||||
checked={leftSelected.length === data.length && data.length > 0}
|
||||
indeterminate={leftSelected.length > 0 && leftSelected.length < data.length}
|
||||
onChange={e => {
|
||||
setLeftSelected(e.target.checked ? pagedSuppliers.map(i => i.id) : []);
|
||||
setLeftSelected(e.target.checked ? data.map(i => i.id) : []);
|
||||
}}
|
||||
>全选</Checkbox>,
|
||||
dataIndex: "select",
|
||||
width: 60,
|
||||
render: (_: any, record) => (
|
||||
width: 80,
|
||||
render: (_: any, record: Supplier) => (
|
||||
<Checkbox
|
||||
checked={leftSelected.includes(record.id)}
|
||||
onChange={e => {
|
||||
@ -96,14 +114,12 @@ const SupplierSelectModal = ({
|
||||
>选择</Checkbox>
|
||||
)
|
||||
},
|
||||
{ title: "供应商名称", dataIndex: "name" },
|
||||
{ title: "准入单位", dataIndex: "unit" },
|
||||
{ title: "准入部门", dataIndex: "dept" },
|
||||
{ title: "准入品类", dataIndex: "category" },
|
||||
{ title: "最近一次评价等级", dataIndex: "evalLevel" }
|
||||
{ title: "供应商名称", dataIndex: "supplierName" },
|
||||
{ title: "准入单位", dataIndex: "deptId" },
|
||||
{ title: "准入部门", dataIndex: "deptId" },
|
||||
{ title: "准入品类", dataIndex: "categoryName" }
|
||||
];
|
||||
|
||||
// 已选表格
|
||||
const rightColumns = [
|
||||
{
|
||||
title: <Checkbox
|
||||
@ -114,8 +130,8 @@ const SupplierSelectModal = ({
|
||||
}}
|
||||
>全选</Checkbox>,
|
||||
dataIndex: "select",
|
||||
width: 60,
|
||||
render: (_: any, record) => (
|
||||
width: 80,
|
||||
render: (_: any, record: Supplier) => (
|
||||
<Checkbox
|
||||
checked={rightSelected.includes(record.id)}
|
||||
onChange={e => {
|
||||
@ -126,31 +142,12 @@ const SupplierSelectModal = ({
|
||||
>选择</Checkbox>
|
||||
)
|
||||
},
|
||||
{ title: "供应商名称", dataIndex: "name" },
|
||||
{ title: "准入单位", dataIndex: "unit" },
|
||||
{ title: "准入部门", dataIndex: "dept" },
|
||||
{ title: "准入品类", dataIndex: "category" }
|
||||
{ title: "供应商名称", dataIndex: "supplierName" },
|
||||
{ title: "准入单位", dataIndex: "deptId" },
|
||||
{ title: "准入部门", dataIndex: "deptId" },
|
||||
{ title: "准入品类", dataIndex: "categoryName" }
|
||||
];
|
||||
|
||||
// 添加
|
||||
const handleAdd = () => {
|
||||
const added = pagedSuppliers.filter(s => leftSelected.includes(s.id));
|
||||
const combined = [...rightData, ...added.filter(i => !rightData.some(r => r.id === i.id))];
|
||||
setRightData(combined);
|
||||
setLeftSelected([]);
|
||||
};
|
||||
|
||||
// 移除
|
||||
const handleRemove = () => {
|
||||
setRightData(rightData.filter(i => !rightSelected.includes(i.id)));
|
||||
setRightSelected([]);
|
||||
};
|
||||
|
||||
// 确认
|
||||
const handleOk = () => {
|
||||
onOk(rightData);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="选择供应商"
|
||||
@ -163,19 +160,22 @@ const SupplierSelectModal = ({
|
||||
cancelText="取消"
|
||||
bodyStyle={{ padding: 24 }}
|
||||
>
|
||||
{/* 查询区 */}
|
||||
<Form layout="inline" form={form} style={{ marginBottom: 16 }}>
|
||||
<Form.Item name="name" label="供应商名称">
|
||||
<Input style={{ width: 180 }} allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="evalLevel" label="最近一次评价等级">
|
||||
<Select style={{ width: 140 }}>
|
||||
{evalOptions.map(opt => <Option value={opt.value} key={opt.value}>{opt.label}</Option>)}
|
||||
<Select style={{ width: 140 }} allowClear>
|
||||
{['A', 'B', 'C', 'D'].map(level => (
|
||||
<Option key={level} value={level}>{level}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="category" label="准入品类">
|
||||
<Select style={{ width: 140 }}>
|
||||
{categoryOptions.map(opt => <Option value={opt.value} key={opt.value}>{opt.label}</Option>)}
|
||||
<Select style={{ width: 140 }} allowClear>
|
||||
{['燃油', '润滑油', '备件'].map(cat => (
|
||||
<Option key={cat} value={cat}>{cat}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
@ -185,26 +185,23 @@ const SupplierSelectModal = ({
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<Row gutter={32}>
|
||||
<Col span={11}>
|
||||
<div style={{ marginBottom: 8 }}>备选供应商 <span style={{ color: "#1890ff" }}>{filteredSuppliers.length}</span></div>
|
||||
<div style={{ marginBottom: 8 }}>备选供应商</div>
|
||||
<Table
|
||||
columns={leftColumns}
|
||||
dataSource={pagedSuppliers}
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
dataSource={data}
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
pagination={pagination}
|
||||
onChange={(pag) => {
|
||||
const values = form.getFieldsValue();
|
||||
fetchData(values, pag.current!, pag.pageSize!);
|
||||
}}
|
||||
size="small"
|
||||
bordered
|
||||
/>
|
||||
<div style={{ textAlign: "right", marginTop: 8 }}>
|
||||
<Pagination
|
||||
simple
|
||||
current={leftPage}
|
||||
pageSize={PAGE_SIZE}
|
||||
total={filteredSuppliers.length}
|
||||
onChange={setLeftPage}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={2} style={{ textAlign: "center", display: "flex", flexDirection: "column", justifyContent: "center" }}>
|
||||
<Button type="primary" disabled={!leftSelected.length} onClick={handleAdd} style={{ marginBottom: 16 }}>{">"}</Button>
|
||||
@ -213,9 +210,9 @@ const SupplierSelectModal = ({
|
||||
<Col span={11}>
|
||||
<div style={{ marginBottom: 8 }}>已选供应商</div>
|
||||
<Table
|
||||
columns={rightColumns}
|
||||
dataSource={rightData}
|
||||
rowKey="id"
|
||||
dataSource={rightData}
|
||||
columns={rightColumns}
|
||||
pagination={false}
|
||||
size="small"
|
||||
bordered
|
||||
|
@ -0,0 +1,113 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Modal, Button, Table, Descriptions, Spin, message } from "antd";
|
||||
import {coscoSupplierexit } from "../services"; // 假设的接口
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
|
||||
interface Supplier {
|
||||
id: number; // 作为 rowKey 用于唯一标识
|
||||
supplierName: string; // 供应商名称
|
||||
unit: string; // 准入部门
|
||||
accessTime: string; // 准入时间
|
||||
categoryName: string; // 准入品类
|
||||
lastEval: string; // 最近一次评价
|
||||
lastEvalDate: string; // 评价时间
|
||||
supplierId: string;
|
||||
categoryId: string;
|
||||
}
|
||||
interface DetailData {
|
||||
exitTheme: string;
|
||||
deptName: string;
|
||||
deptId: string;
|
||||
exitReason: string;
|
||||
}
|
||||
|
||||
interface ViewBlacklistModalProps {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
recordId: string; // 用于查询详情数据
|
||||
}
|
||||
|
||||
const ViewBlacklistModal: React.FC<ViewBlacklistModalProps> = ({
|
||||
visible,
|
||||
onCancel,
|
||||
recordId,
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [detail, setDetail] = useState<DetailData | null>(null);
|
||||
const [suppliers, setSuppliers] = useState<Supplier[]>([]);
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { code, data } = await coscoSupplierexit(recordId)
|
||||
if (code == 200) {
|
||||
setDetail(data.coscoSupplierexit);
|
||||
} else {
|
||||
message.error("获取详情失败");
|
||||
}
|
||||
|
||||
if (code == 200) {
|
||||
setSuppliers(data.coscoSupplierexitSupplierCategoryList || []);
|
||||
} else {
|
||||
message.error("获取供应商列表失败");
|
||||
}
|
||||
} catch (error) {
|
||||
message.error("获取数据异常");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (visible && recordId) {
|
||||
fetchData();
|
||||
}
|
||||
}, [visible, recordId]);
|
||||
|
||||
const columns:ColumnsType<Supplier> = [
|
||||
{ title: "供应商名称", dataIndex: "supplierName", align: "center" },
|
||||
{ title: "准入部门", dataIndex: "unit", align: "center" },
|
||||
{ title: "准入时间", dataIndex: "accessTime", align: "center" },
|
||||
{ title: "准入品类", dataIndex: "categoryName", align: "center" },
|
||||
{ title: "最近一次评价", dataIndex: "lastEval", align: "center" },
|
||||
{ title: "评价时间", dataIndex: "lastEvalDate", align: "center" },
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="供应商退出详情"
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
width={900}
|
||||
footer={<Button onClick={onCancel}>关闭</Button>}
|
||||
destroyOnClose
|
||||
bodyStyle={{ padding: "36px 36px 16px" }}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
{detail && (
|
||||
<Descriptions
|
||||
title="退出信息"
|
||||
column={1}
|
||||
labelStyle={{ width: 120 }}
|
||||
bordered
|
||||
style={{ marginBottom: 24 }}
|
||||
>
|
||||
<Descriptions.Item label="申请主题">{detail.exitTheme}</Descriptions.Item>
|
||||
<Descriptions.Item label="发起部门">{detail.deptId}</Descriptions.Item>
|
||||
<Descriptions.Item label="退出原因" >{detail.exitReason}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
)}
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={suppliers}
|
||||
rowKey="id"
|
||||
bordered
|
||||
pagination={false}
|
||||
/>
|
||||
</Spin>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewBlacklistModal;
|
@ -1,11 +1,16 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Form, Button, Table, Select, Input, DatePicker, Space, message } from "antd";
|
||||
import { SearchOutlined, ReloadOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import CreateBlacklistModal from './components/CreateBlacklistModal'
|
||||
|
||||
// mock 数据和选项省略(和上面一致)
|
||||
|
||||
import ViewBlacklistModal from './components/ViewBlacklistModal';
|
||||
import { getPage } from './services'
|
||||
interface ExitRecord {
|
||||
exitTheme: string; // 申请主题
|
||||
deptId: string; // 发起单位/部门(两列共用)
|
||||
createTime: string; // 发起时间
|
||||
accessType: string; // 审批记录状态
|
||||
}
|
||||
// mock 数据与服务(实际请换为接口)
|
||||
const orgOptions = [
|
||||
{ label: "请选择", value: "" },
|
||||
@ -14,37 +19,7 @@ const orgOptions = [
|
||||
{ label: "中远海运物流", value: "中远海运物流" },
|
||||
{ label: "中远海运广州公司", value: "中远海运广州公司" },
|
||||
];
|
||||
const statusOptions = [
|
||||
{ label: "请选择", value: "" },
|
||||
{ label: "未开始", value: "未开始" },
|
||||
{ label: "进行中", value: "进行中" },
|
||||
{ label: "已结束", value: "已结束" },
|
||||
];
|
||||
|
||||
// mock接口
|
||||
const mockGetList = async ({ page = 1, pageSize = 10 }) => {
|
||||
// 生成数据
|
||||
const list = Array.from({ length: pageSize }).map((_, i) => {
|
||||
const idx = (page - 1) * pageSize + i + 1;
|
||||
return {
|
||||
id: idx,
|
||||
topic: ["中山市合创展包装材料有限公司等黑灰名单申请", "深圳市欧阳华斯电源有限公司等黑灰名单申请", "广东振兴塑胶机械有限公司等黑灰名单申请", "上海硕建建筑技术工程有限公司等黑灰名单申请"][i % 4],
|
||||
org: ["中远海运特运", "中远海运散运", "中远海运广州公司"][i % 3],
|
||||
dept: "采购部",
|
||||
time: `2024-10-2${6 - (i % 3)} 9:00`,
|
||||
status: ["未开始", "进行中", "已结束"][i % 3],
|
||||
}
|
||||
});
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
code: 200,
|
||||
data: list,
|
||||
total: 188,
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
};
|
||||
|
||||
// 查询记录状态
|
||||
const recordStatusOptions = [
|
||||
{ label: "请选择", value: "" },
|
||||
@ -59,14 +34,16 @@ const supplierExitManage: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
|
||||
const [createVisible, setCreateVisible] = useState(false);
|
||||
const [viewVisible, setViewVisible] = useState(false);
|
||||
const [selectedRecordId, setSelectedRecordId] = useState<string | null>(null);
|
||||
// 查询接口
|
||||
const getList = async (page = 1, pageSize = 10) => {
|
||||
const getList = async (pageNo = 1, pageSize = 10) => {
|
||||
setLoading(true);
|
||||
// 可传查询条件 form.getFieldsValue()
|
||||
const { code, data: list, total } = await mockGetList({ page, pageSize });
|
||||
const { code, data } = await getPage({ pageNo, pageSize });
|
||||
if (code === 200) {
|
||||
setData(list);
|
||||
setPagination(prev => ({ ...prev, current: page, pageSize, total }));
|
||||
setData(data.records);
|
||||
setPagination(prev => ({ ...prev, current: pageNo, pageSize, total: data.total }));
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
@ -90,12 +67,15 @@ const supplierExitManage: React.FC = () => {
|
||||
const handleApply = () => {
|
||||
setCreateVisible(true)
|
||||
};
|
||||
|
||||
const handleSelectOk = () => {
|
||||
getList();
|
||||
setCreateVisible(false);
|
||||
};
|
||||
useEffect(() => {
|
||||
getList();
|
||||
}, []);
|
||||
|
||||
const columns = [
|
||||
const columns: ColumnsType<ExitRecord> = [
|
||||
{
|
||||
title: "序号",
|
||||
dataIndex: "index",
|
||||
@ -104,17 +84,24 @@ const supplierExitManage: React.FC = () => {
|
||||
width: 60,
|
||||
render: (_: any, __: any, idx: number) => idx + 1,
|
||||
},
|
||||
{ title: "申请主题", dataIndex: "topic", key: "topic", align: "center" },
|
||||
{ title: "发起单位", dataIndex: "org", key: "org", align: "center" },
|
||||
{ title: "发起部门", dataIndex: "dept", key: "dept", align: "center" },
|
||||
{ title: "发起时间", dataIndex: "time", key: "time", align: "center" },
|
||||
{ title: "审批记录状态", dataIndex: "status", key: "status", align: "center" },
|
||||
{ title: "申请主题", dataIndex: "exitTheme", key: "exitTheme", align: "center" },
|
||||
{ title: "发起单位", dataIndex: "deptId", key: "deptId", align: "center" },
|
||||
{ title: "发起部门", dataIndex: "deptId", key: "deptId", align: "center" },
|
||||
{ title: "发起时间", dataIndex: "createTime", key: "createTime", align: "center" },
|
||||
{ title: "审批记录状态", dataIndex: "approveStatusText", key: "approveStatusText", align: "center" },
|
||||
{
|
||||
title: "操作",
|
||||
key: "option",
|
||||
align: "center",
|
||||
render: (record: any) => (
|
||||
<a style={{ color: "#1677ff" }} onClick={() => message.error('123')}>查看</a>
|
||||
<a
|
||||
onClick={() => {
|
||||
setSelectedRecordId(record.id);
|
||||
setViewVisible(true);
|
||||
}}
|
||||
>
|
||||
查看
|
||||
</a>
|
||||
),
|
||||
},
|
||||
];
|
||||
@ -163,7 +150,12 @@ const supplierExitManage: React.FC = () => {
|
||||
bordered
|
||||
/>
|
||||
|
||||
<CreateBlacklistModal visible={createVisible} onCancel={() => setCreateVisible(false)} onOk={() => setCreateVisible(false)} />
|
||||
<CreateBlacklistModal visible={createVisible} onCancel={() => setCreateVisible(false)} onOk={handleSelectOk} />
|
||||
<ViewBlacklistModal
|
||||
visible={viewVisible}
|
||||
recordId={selectedRecordId || ''}
|
||||
onCancel={() => setViewVisible(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,64 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 品类准入管理列表
|
||||
*/
|
||||
interface getPageData {
|
||||
pageNo: number;
|
||||
pageSize: number;
|
||||
deptId?: string;
|
||||
accessType?: string;
|
||||
reviewStatus?: string;
|
||||
approveStatus?: string;
|
||||
categoryId?: string;
|
||||
}
|
||||
export const getPage = (data: getPageData) => request.post('/coscoSupplierexit/getPage', { data });
|
||||
/**
|
||||
* 供应商分页列表查询
|
||||
*/
|
||||
interface getSupplierCategoryPageData {
|
||||
pageNo: number;
|
||||
pageSize: number;
|
||||
supplierName?: number;
|
||||
categoryId?: string;
|
||||
reviewResult?: string;
|
||||
}
|
||||
|
||||
export const getSupplierCategoryPage = (data: getSupplierCategoryPageData) => request.post('/coscoSupplierexit/getSupplierCategoryPage', { data });
|
||||
|
||||
|
||||
/**
|
||||
* 的供应商分页列表查询
|
||||
*/
|
||||
export interface addInterface {
|
||||
coscoSupplierexit: CoscoSupplierexit;
|
||||
coscoSupplierexitSupplierCategoryList: CoscoSupplierexitSupplierCategoryList[];
|
||||
supplierIdList: string[];
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
export interface CoscoSupplierexit {
|
||||
deptId: string;
|
||||
exitReason: string;
|
||||
exitTheme: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
export interface CoscoSupplierexitSupplierCategoryList {
|
||||
categoryId: string;
|
||||
supplierId: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
export const add = (data: addInterface) => request.post('/coscoSupplierexit/add', { data });
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 退出详情页(没分页)
|
||||
*/
|
||||
export const coscoSupplierexit = (id: string) => request.get(`/coscoSupplierexit/${id}`);
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user