供应商准入情况统计

This commit is contained in:
孙景学
2025-07-10 10:04:24 +08:00
parent eefc97e553
commit 6e1276fcc9
6 changed files with 356 additions and 17 deletions

View File

@ -152,6 +152,11 @@ export default [
icon: 'icon-shujutongji',
routes: [
{
name: '供应商准入情况统计',
path: '/dataStatistics/supplierAdmissionStatistics',
icon: 'icon-pingjia',
component: '@/pages/dataStatistics/supplierAdmissionStatistics/supplierAdmissionStatistics',
},{
name: '供应商评价情况统计',
path: '/dataStatistics/supplierEvaluateStatistics',
icon: 'icon-pingjia',

View File

@ -21,6 +21,7 @@ export default {
// 数据统计模块
'menu.数据统计': '数据统计',
'menu.供应商准入情况统计': '供应商准入情况统计',
'menu.供应商评价情况统计': '供应商评价情况统计',
'menu.供应商年审情况统计': '供应商年审情况统计',
'menu.供应商资质预警统计': '供应商资质预警统计',

View File

@ -0,0 +1,309 @@
import React, { useState, useEffect } from 'react';
import { Button, Table, Input, Select, Form, Tooltip, Tag, message, DatePicker } from 'antd';
import type { TablePaginationConfig } from 'antd';
import { SearchOutlined, DeleteOutlined, ExportOutlined } from '@ant-design/icons';
import { useIntl } from 'umi';
import { AnnualReviewResultText, AnnualReviewResultColor } from '@/dicts/dataStatistics';
import { getSupplierAdmissionStatistics } from '@/servers/api/dataStatistics';
import { downloadFile } from '@/utils/download';
import moment from 'moment';
const { Option } = Select;
const SupplierAnnualStatistics: React.FC = () => {
const intl = useIntl();
const [loading, setLoading] = useState<boolean>(false);
const [form] = Form.useForm();
const [statisticsData, setStatisticsData] = useState<
DataStatistics.AnnualReviewStatisticsRecord[]
>([]);
const [pagination, setPagination] = useState<TablePaginationConfig>({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => intl.formatMessage({ id: 'dataStatistics.common.total' }, { total }),
});
const [searchParams, setSearchParams] =
useState<DataStatistics.AnnualReviewStatisticsSearchParams>({});
// 准入单位下拉选项 - 假数据
const companyOptions = [
{ label: '中山市合创展包装材料有限公司', value: '中山市合创展包装材料有限公司' },
{ label: '广州市科技发展有限公司', value: '广州市科技发展有限公司' },
{ label: '深圳市创新科技有限公司', value: '深圳市创新科技有限公司' },
{ label: '东莞市制造业有限公司', value: '东莞市制造业有限公司' },
];
// 年审结果选项
const annualResultOptions = Object.entries(AnnualReviewResultText).map(([key, value]) => ({
label: value,
value: key,
}));
// 获取数据
const fetchStatisticsData = async (
current = 1,
pageSize = 10,
params: DataStatistics.AnnualReviewStatisticsSearchParams = searchParams,
) => {
if (params !== searchParams) {
setSearchParams(params);
}
setLoading(true);
try {
// 构建请求参数
const requestParams: DataStatistics.AnnualReviewStatisticsRequest = {
basePageRequest: {
pageNo: current,
pageSize: pageSize,
},
...params,
};
// 调用接口
const response = await getSupplierAdmissionStatistics(requestParams);
if (response.success && response.data) {
setStatisticsData(response.data.records);
setPagination({
...pagination,
current: response.data.current,
pageSize: response.data.size,
total: response.data.total,
});
} else {
message.error(
response.message || intl.formatMessage({ id: 'dataStatistics.annual.getDataFailed' }),
);
}
} catch (error) {
console.error('获取年审统计数据失败:', error);
message.error(intl.formatMessage({ id: 'dataStatistics.annual.getDataFailed' }));
} finally {
setLoading(false);
}
};
// 首次加载获取数据
useEffect(() => {
fetchStatisticsData(pagination.current, pagination.pageSize, {});
}, []);
// 处理表格分页变化
const handleTableChange = (newPagination: TablePaginationConfig) => {
fetchStatisticsData(newPagination.current, newPagination.pageSize, searchParams);
};
// 获取年审结果标签
const getResultTag = (result: string) => {
const color =
AnnualReviewResultColor[result as keyof typeof AnnualReviewResultColor] || 'default';
const text =
result === '1'
? intl.formatMessage({ id: 'dataStatistics.annual.qualified' })
: intl.formatMessage({ id: 'dataStatistics.annual.unqualified' });
return <Tag color={color}>{text}</Tag>;
};
const columns = [
{
title: intl.formatMessage({ id: 'dataStatistics.common.serialNumber' }),
render: (_: any, __: DataStatistics.AnnualReviewStatisticsRecord, index: number) =>
(pagination.current! - 1) * pagination.pageSize! + index + 1,
width: 80,
},
{
title: intl.formatMessage({ id: 'dataStatistics.common.supplierName' }),
dataIndex: 'supplierName',
key: 'supplierName',
width: 180,
ellipsis: {
showTitle: false,
},
render: (text: string) => (
<Tooltip placement="topLeft" title={text}>
{text}
</Tooltip>
),
},
{
title: intl.formatMessage({ id: 'dataStatistics.common.area' }),
dataIndex: 'area',
key: 'area',
width: 100,
},
{
title: intl.formatMessage({ id: 'dataStatistics.common.category' }),
dataIndex: 'categoryName',
key: 'categoryName',
width: 120,
},
{
title: intl.formatMessage({ id: 'dataStatistics.common.accessUnit' }),
dataIndex: 'accessUnit',
key: 'accessUnit',
width: 180,
ellipsis: {
showTitle: false,
},
render: (text: string) => (
<Tooltip placement="topLeft" title={text}>
{text}
</Tooltip>
),
},
{
title: intl.formatMessage({ id: 'dataStatistics.common.accessDept' }),
dataIndex: 'accessDept',
key: 'accessDept',
width: 120,
},
{
title: intl.formatMessage({ id: 'dataStatistics.common.year' }),
dataIndex: 'annualreviewYear',
key: 'annualreviewYear',
width: 100,
render: (year: string) =>
intl.formatMessage({ id: 'dataStatistics.common.yearFormat' }, { year }),
},
{
title: intl.formatMessage({ id: 'dataStatistics.annual.annualStatisticsResult' }),
dataIndex: 'annualStatisticsResult',
key: 'annualStatisticsResult',
width: 100,
render: (result: string) => getResultTag(result),
},
];
// 处理搜索
const handleSearch = (values: any) => {
fetchStatisticsData(1, pagination.pageSize, values);
};
// 导出功能
const handleExport = () => {
const values = form.getFieldsValue();
downloadFile('/dataStatistics/exportSupplierAnnualReviewStatistics', 'GET', values);
};
return (
<div className="common-container supplier-annual-statistics">
<div className="filter-action-row">
<Form
form={form}
name="search"
onFinish={handleSearch}
layout="inline"
className="filter-form"
>
<Form.Item
name="supplierName"
label={intl.formatMessage({ id: 'dataStatistics.common.supplierName' })}
>
<Input
placeholder={
intl.formatMessage({ id: 'dataStatistics.common.pleaseInput' }) +
intl.formatMessage({ id: 'dataStatistics.common.supplierName' })
}
allowClear
/>
</Form.Item>
<Form.Item
name="annualreviewYear"
getValueProps={(value) => ({
value: value ? moment(value) : undefined,
})}
normalize={(value) => value && value.format('YYYY')}
label={intl.formatMessage({ id: 'dataStatistics.annual.annualYear' })}
>
<DatePicker
style={{ width: '100%' }}
format="YYYY"
placeholder={
intl.formatMessage({ id: 'dataStatistics.common.pleaseSelect' }) +
intl.formatMessage({ id: 'dataStatistics.common.year' })
}
picker="year"
/>
</Form.Item>
<Form.Item
name="accessUnit"
label={intl.formatMessage({ id: 'dataStatistics.common.accessUnit' })}
>
<Select
placeholder={
intl.formatMessage({ id: 'dataStatistics.common.pleaseSelect' }) +
intl.formatMessage({ id: 'dataStatistics.common.accessUnit' })
}
allowClear
style={{ width: 200 }}
>
{companyOptions.map((option) => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
))}
</Select>
</Form.Item>
<Form.Item
name="annualStatisticsResult"
label={intl.formatMessage({ id: 'dataStatistics.annual.annualResult' })}
>
<Select
placeholder={
intl.formatMessage({ id: 'dataStatistics.common.pleaseSelect' }) +
intl.formatMessage({ id: 'dataStatistics.annual.annualResult' })
}
allowClear
style={{ width: 120 }}
>
{annualResultOptions.map((option) => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
))}
</Select>
</Form.Item>
<Form.Item className="filter-btns">
<Button type="primary" htmlType="submit" icon={<SearchOutlined />}>
{intl.formatMessage({ id: 'dataStatistics.common.search' })}
</Button>
<Button
type="primary"
danger
icon={<DeleteOutlined />}
onClick={() => {
form.resetFields();
fetchStatisticsData(1, pagination.pageSize, {});
}}
>
{intl.formatMessage({ id: 'dataStatistics.common.reset' })}
</Button>
</Form.Item>
</Form>
<div className="right-buttons">
<Button type="primary" ghost icon={<ExportOutlined />} onClick={handleExport}>
{intl.formatMessage({ id: 'dataStatistics.common.export' })}
</Button>
</div>
</div>
<Table
className="data-table"
rowKey="id"
columns={columns}
dataSource={statisticsData}
pagination={pagination}
loading={loading}
onChange={handleTableChange}
scroll={{ x: 1200 }}
/>
</div>
);
};
export default SupplierAnnualStatistics;

View File

@ -2,25 +2,29 @@ import React, { useEffect, useState } from "react";
import { Table, Form, Input, Button, Select, Modal, Descriptions } from 'antd';
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons';
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
//字典与接口
import { getPage, update } from './services';
import { getDictList } from '@/servers/api/dicts'
const messageTypeOptions = [
{ label: '请选择', value: '' },
{ label: '供应商变更', value: '供应商变更' },
{ label: '供应商准入', value: '供应商准入' },
{ label: '供应商评价', value: '供应商评价' },
{ label: '供应商评审', value: '供应商评审' },
{ label: '供应商退出', value: '供应商退出' },
{ label: '供应商黑名单', value: '供应商黑名单' },
];
interface CategoryOption {
code: string;
dicName: string;
}
const SupplierMessage: React.FC = () => {
//搜索
const [form] = Form.useForm();
//列表渲染
const [data, setData] = useState<any[]>([]);
//列表加载
const [loading, setLoading] = useState(false);
//列表分页
const [pagination, setPagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 10, total: 0 });
//查看信息与组件
const [viewRecord, setViewRecord] = useState<any>(null); // 当前查看的消息
const [viewVisible, setViewVisible] = useState(false); // 弹窗显隐
//下拉数据
const [categoryOptions, setCategoryOptions] = useState<CategoryOption[]>([]);
// 获取数据
const getList = async (pageNo = 1, pageSize = 10) => {
@ -53,8 +57,13 @@ const SupplierMessage: React.FC = () => {
};
useEffect(() => {
getDictList('message_type').then((res: any) => {
const { code, data } = res;
if (code == 200) {
setCategoryOptions(data)
}
})
getList();
// eslint-disable-next-line
}, []);
// 表头
@ -76,8 +85,8 @@ const SupplierMessage: React.FC = () => {
},
{
title: '业务类型',
dataIndex: 'type',
key: 'type',
dataIndex: 'typeCn',
key: 'typeCn',
align: 'center',
},
{
@ -93,7 +102,7 @@ const SupplierMessage: React.FC = () => {
key: 'read',
align: 'center',
width: 120,
render: (_, record) => ( <span>{ record.read === '0'? '否':'是' }</span> )
render: (_, record) => (<span>{record.read === '0' ? '否' : '是'}</span>)
},
{
title: '操作',
@ -104,7 +113,7 @@ const SupplierMessage: React.FC = () => {
<a onClick={() => {
setViewRecord(record);
setViewVisible(true);
update({ id:record.id })
update({ id: record.id })
getList()
}}></a>
),
@ -119,8 +128,8 @@ const SupplierMessage: React.FC = () => {
</Form.Item>
<Form.Item name="type" label="业务类型">
<Select style={{ width: 200 }} placeholder="请选择业务类型" allowClear>
{messageTypeOptions.map(opt => (
<Select.Option key={opt.value} value={opt.value}>{opt.label}</Select.Option>
{categoryOptions.map(opt => (
<Select.Option key={opt.code} value={opt.code}>{opt.dicName}</Select.Option>
))}
</Select>
</Form.Item>
@ -140,7 +149,6 @@ const SupplierMessage: React.FC = () => {
showSizeChanger: true,
}}
onChange={handleTableChange}
bordered
/>
<Modal

View File

@ -3,6 +3,14 @@ import { getAllEvaluateRules } from './supplierEvaluate';
// 数据统计模块api
// 评价情况统计列表查询
export async function getSupplierAdmissionStatistics(params: DataStatistics.SupplierAdmissionStatistics) {
return request('/api/coscoAccessSupplier/getPage', {
method: 'POST',
data: params,
});
}
// 评价情况统计列表查询
export async function getEvaluateStatisticsList(params: DataStatistics.EvaluateStatisticsRequest) {
return request('/api/dataStatistics/evaluateStatistics/list', {

View File

@ -23,6 +23,14 @@ declare namespace DataStatistics {
pages?: number;
}
export interface SupplierAdmissionStatistics {
pageNo: number;
pageSize: number;
// supplierName: string;
// accessType: string;
// updateYear: number;
}
// 评价情况统计 - 记录
export interface EvaluateStatisticsRecord {
id: string;