封装选择供应商组件; 丰富选择品类库 说明文档
This commit is contained in:
388
src/components/SupplierSelector/SupplierSelector.tsx
Normal file
388
src/components/SupplierSelector/SupplierSelector.tsx
Normal file
@ -0,0 +1,388 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Input, Select, Row, Col, Table, Button, Form, Tooltip, message } from 'antd';
|
||||
import { RightOutlined, LeftOutlined } from '@ant-design/icons';
|
||||
import { getSupplierPage } from '@/servers/api/supplier';
|
||||
import './SupplierSelector.less';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
/**
|
||||
* SupplierSelector 组件的属性接口
|
||||
* @interface SupplierSelectorProps
|
||||
* @property {Function} [onSelect] - 当选择供应商发生变化时的回调函数
|
||||
* @property {any[]} [selectedSuppliers] - 初始已选择的供应商列表
|
||||
* @property {string[]} [deptList] - 部门列表,用于筛选供应商
|
||||
* @property {boolean} [showDeptFilter] - 是否显示部门筛选,默认显示
|
||||
* @property {boolean} [showCompanyFilter] - 是否显示公司名称筛选,默认不显示
|
||||
*/
|
||||
interface SupplierSelectorProps {
|
||||
onSelect?: (selected: any[]) => void;
|
||||
selectedSuppliers?: any[];
|
||||
deptList?: string[];
|
||||
showDeptFilter?: boolean;
|
||||
showCompanyFilter?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 供应商数据项接口,对应后端SupplierPageVo
|
||||
* @interface SupplierItem
|
||||
* @property {string} id - 供应商ID
|
||||
* @property {string} name - 供应商名称
|
||||
* @property {string} [deptId] - 部门ID
|
||||
* @property {string} [deptName] - 部门名称
|
||||
* @property {string} [companyName] - 公司名称
|
||||
* @property {string} [categoryName] - 所属品类
|
||||
* @property {string} [levelName] - 最新评价等级
|
||||
* @property {Date} [admissionTime] - 准入时间
|
||||
* @property {Date} [evaluationTime] - 最新评价时间
|
||||
* @property {any} [key: string] - 其他属性
|
||||
*/
|
||||
interface SupplierItem {
|
||||
id: string;
|
||||
name: string;
|
||||
deptId?: string;
|
||||
deptName?: string;
|
||||
companyName?: string;
|
||||
categoryName?: string;
|
||||
levelName?: string;
|
||||
admissionTime?: Date;
|
||||
evaluationTime?: Date;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 供应商选择器组件
|
||||
* 用于从系统中选择供应商数据,支持搜索过滤、分页加载、双列表展示
|
||||
*
|
||||
* @param {SupplierSelectorProps} props - 组件属性
|
||||
* @returns {React.ReactElement} 供应商选择器组件
|
||||
*/
|
||||
const SupplierSelector: React.FC<SupplierSelectorProps> = ({
|
||||
onSelect,
|
||||
selectedSuppliers: initialSelectedSuppliers = [],
|
||||
deptList = [],
|
||||
showDeptFilter = true,
|
||||
showCompanyFilter = false
|
||||
}) => {
|
||||
// 表单实例,用于管理查询条件
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// 待选供应商数据列表
|
||||
const [tableListData, setTableListData] = useState<SupplierItem[]>([]);
|
||||
|
||||
// 分页信息
|
||||
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
|
||||
|
||||
// 加载状态
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 左侧待选列表中已选中的项的ID
|
||||
const [leftSelected, setLeftSelected] = useState<React.Key[]>([]);
|
||||
|
||||
// 右侧已选列表中选中的项的ID
|
||||
const [rightSelected, setRightSelected] = useState<React.Key[]>([]);
|
||||
|
||||
// 最终已选择的供应商列表
|
||||
const [chosenSuppliers, setChosenSuppliers] = useState<SupplierItem[]>(initialSelectedSuppliers);
|
||||
|
||||
/**
|
||||
* 监听初始已选供应商变化,更新内部状态
|
||||
*/
|
||||
useEffect(() => {
|
||||
setChosenSuppliers(initialSelectedSuppliers);
|
||||
}, [initialSelectedSuppliers]);
|
||||
|
||||
/**
|
||||
* 合并供应商列表并去重
|
||||
* @param {SupplierItem[]} suppliers - 原有的供应商列表
|
||||
* @param {SupplierItem[]} selected - 新选择的供应商列表
|
||||
* @returns {SupplierItem[]} 合并后的供应商列表,去除重复项
|
||||
*/
|
||||
const filteredData = (suppliers: SupplierItem[], selected: SupplierItem[]) => {
|
||||
// 创建已有供应商ID的集合,用于快速查找
|
||||
const ids = new Set(suppliers.map((item: SupplierItem) => item.id));
|
||||
// 过滤出未在原列表中出现的供应商
|
||||
const newSelected = selected.filter((item: SupplierItem) => !ids.has(item.id));
|
||||
// 合并列表
|
||||
return [...suppliers, ...newSelected];
|
||||
};
|
||||
|
||||
/**
|
||||
* 将左侧选中的供应商移动到右侧已选列表
|
||||
*/
|
||||
const moveToRight = () => {
|
||||
// 获取左侧选中的供应商数据
|
||||
const selected = tableListData.filter((item: SupplierItem) => leftSelected.includes(item.id));
|
||||
// 合并到已选列表并去重
|
||||
const chosenSuppliersNew = filteredData(chosenSuppliers, selected);
|
||||
// 更新已选列表
|
||||
setChosenSuppliers(chosenSuppliersNew);
|
||||
// 清空左侧选择状态
|
||||
setLeftSelected([]);
|
||||
// 触发外部回调
|
||||
onSelect?.(chosenSuppliersNew);
|
||||
};
|
||||
|
||||
/**
|
||||
* 将右侧选中的供应商从已选列表中移除
|
||||
*/
|
||||
const moveToLeft = () => {
|
||||
// 过滤掉右侧选中的供应商
|
||||
const remaining = chosenSuppliers.filter((item: SupplierItem) => !rightSelected.includes(item.id));
|
||||
// 更新已选列表
|
||||
setChosenSuppliers(remaining);
|
||||
// 清空右侧选择状态
|
||||
setRightSelected([]);
|
||||
// 触发外部回调
|
||||
onSelect?.(remaining);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取供应商列表数据
|
||||
* @param {any} values - 查询条件
|
||||
* @param {number} pageNo - 页码
|
||||
* @param {number} pageSize - 每页数量
|
||||
*/
|
||||
const getTableList = async (values: any = {}, pageNo: number = 1, pageSize: number = 10) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 整理查询参数
|
||||
const params = {
|
||||
...values,
|
||||
pageNo,
|
||||
pageSize,
|
||||
deptList: deptList.length > 0 ? deptList : undefined
|
||||
};
|
||||
|
||||
// 调用API获取供应商列表
|
||||
const response = await getSupplierPage(params);
|
||||
|
||||
if (response && response.code === 200) {
|
||||
// 请求成功,更新数据和分页信息
|
||||
setTableListData(response.data.records || []);
|
||||
setPagination({
|
||||
current: pageNo,
|
||||
pageSize,
|
||||
total: response.data.total || 0
|
||||
});
|
||||
} else {
|
||||
// 请求失败,显示错误信息
|
||||
message.error(response?.message || '获取供应商列表失败');
|
||||
|
||||
// 使用mock数据作为备用
|
||||
setTableListData([
|
||||
{ id: '1', name: '供应商A', deptName: '采购部', companyName: '公司A', categoryName: '润滑油' },
|
||||
{ id: '2', name: '供应商B', deptName: '技术部', companyName: '公司B', categoryName: '燃油' },
|
||||
{ id: '3', name: '供应商C', deptName: '质量部', companyName: '公司C', categoryName: '备件' },
|
||||
{ id: '4', name: '供应商D', deptName: '采购部', companyName: '公司D', categoryName: '计算机' },
|
||||
{ id: '5', name: '供应商E', deptName: '技术部', companyName: '公司E', categoryName: '通信设备' },
|
||||
]);
|
||||
setPagination({ current: 1, pageSize: 10, total: 5 });
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理异常情况
|
||||
console.error('获取供应商列表出错:', error);
|
||||
message.error('获取供应商列表失败,请稍后重试');
|
||||
|
||||
// 使用mock数据作为备用
|
||||
setTableListData([
|
||||
{ id: '1', name: '供应商A', deptName: '采购部', companyName: '公司A', categoryName: '润滑油' },
|
||||
{ id: '2', name: '供应商B', deptName: '技术部', companyName: '公司B', categoryName: '燃油' },
|
||||
{ id: '3', name: '供应商C', deptName: '质量部', companyName: '公司C', categoryName: '备件' },
|
||||
{ id: '4', name: '供应商D', deptName: '采购部', companyName: '公司D', categoryName: '计算机' },
|
||||
{ id: '5', name: '供应商E', deptName: '技术部', companyName: '公司E', categoryName: '通信设备' },
|
||||
]);
|
||||
setPagination({ current: 1, pageSize: 10, total: 5 });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 组件初始化时加载数据
|
||||
*/
|
||||
useEffect(() => {
|
||||
const values = form.getFieldsValue();
|
||||
getTableList(values, 1, 10);
|
||||
}, [deptList]);
|
||||
|
||||
/**
|
||||
* 处理查询表单提交
|
||||
* @param {any} values - 表单值
|
||||
*/
|
||||
const handleSearch = (values: any) => {
|
||||
getTableList(values, 1, pagination.pageSize);
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置查询表单并重新加载数据
|
||||
*/
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
const values = form.getFieldsValue();
|
||||
getTableList(values, 1, pagination.pageSize);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理表格分页变化
|
||||
* @param {any} paginationInfo - 分页信息
|
||||
*/
|
||||
const handleTableChange = (paginationInfo: any) => {
|
||||
const values = form.getFieldsValue();
|
||||
getTableList(values, paginationInfo.current, paginationInfo.pageSize);
|
||||
};
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: '供应商名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
render: (name: string) => (
|
||||
<Tooltip placement="topLeft" title={name}>
|
||||
{name}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '所属品类',
|
||||
dataIndex: 'categoryName',
|
||||
ellipsis: true,
|
||||
render: (categoryName: string) => (
|
||||
<Tooltip placement="topLeft" title={categoryName}>
|
||||
{categoryName || '-'}
|
||||
</Tooltip>
|
||||
),
|
||||
}
|
||||
];
|
||||
|
||||
// 如果显示部门筛选,添加部门列
|
||||
if (showDeptFilter) {
|
||||
columns.push({
|
||||
title: '部门',
|
||||
dataIndex: 'deptName',
|
||||
ellipsis: true,
|
||||
render: (deptName: string) => (
|
||||
<Tooltip placement="topLeft" title={deptName}>
|
||||
{deptName || '-'}
|
||||
</Tooltip>
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
// 如果显示公司名称筛选,添加公司名称列
|
||||
if (showCompanyFilter) {
|
||||
columns.push({
|
||||
title: '公司名称',
|
||||
dataIndex: 'companyName',
|
||||
ellipsis: true,
|
||||
render: (companyName: string) => (
|
||||
<Tooltip placement="topLeft" title={companyName}>
|
||||
{companyName || '-'}
|
||||
</Tooltip>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="supplier-selector">
|
||||
{/* 查询表单 */}
|
||||
<Form layout="inline" form={form} onFinish={handleSearch} style={{ marginBottom: 16 }}>
|
||||
<Form.Item name="name" label="供应商名称">
|
||||
<Input placeholder="供应商名称" allowClear />
|
||||
</Form.Item>
|
||||
|
||||
{showDeptFilter && (
|
||||
<Form.Item name="deptId" label="部门">
|
||||
<Select placeholder="请选择部门" allowClear style={{ width: 150 }}>
|
||||
<Option value="1">采购部</Option>
|
||||
<Option value="2">技术部</Option>
|
||||
<Option value="3">质量部</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{showCompanyFilter && (
|
||||
<Form.Item name="companyName" label="公司名称">
|
||||
<Input placeholder="公司名称" allowClear />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item name="categoryName" label="所属品类">
|
||||
<Input placeholder="所属品类" allowClear />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">查询</Button>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button onClick={handleReset}>重置</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
{/* 供应商选择区域 */}
|
||||
<Row gutter={16} className="supplier-lists">
|
||||
{/* 左侧待选列表 */}
|
||||
<Col span={10}>
|
||||
<div className="list-title">
|
||||
待选供应商
|
||||
<span className="search-count">{pagination.total}项</span>
|
||||
</div>
|
||||
<Table
|
||||
rowSelection={{
|
||||
type: 'checkbox',
|
||||
onChange: setLeftSelected,
|
||||
selectedRowKeys: leftSelected
|
||||
}}
|
||||
rowKey="id"
|
||||
dataSource={tableListData}
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
pagination={pagination}
|
||||
onChange={handleTableChange}
|
||||
scroll={{ y: 300 }}
|
||||
size="middle"
|
||||
/>
|
||||
</Col>
|
||||
|
||||
{/* 中间操作按钮 */}
|
||||
<Col span={4} className="transfer-buttons">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<RightOutlined />}
|
||||
onClick={moveToRight}
|
||||
disabled={leftSelected.length === 0}
|
||||
/>
|
||||
<Button
|
||||
icon={<LeftOutlined />}
|
||||
onClick={moveToLeft}
|
||||
disabled={rightSelected.length === 0}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
{/* 右侧已选列表 */}
|
||||
<Col span={10}>
|
||||
<div className="list-title">
|
||||
已选供应商
|
||||
<span className="search-count">{chosenSuppliers.length}项</span>
|
||||
</div>
|
||||
<Table
|
||||
rowSelection={{
|
||||
type: 'checkbox',
|
||||
onChange: setRightSelected,
|
||||
selectedRowKeys: rightSelected
|
||||
}}
|
||||
columns={columns}
|
||||
dataSource={chosenSuppliers}
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
scroll={{ y: 300 }}
|
||||
size="middle"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupplierSelector;
|
Reference in New Issue
Block a user