开发供应商评价模块
This commit is contained in:
@ -22,26 +22,41 @@ export default [
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
meta: {
|
||||
title: '首页',
|
||||
},
|
||||
redirect: '/index',
|
||||
},
|
||||
{
|
||||
name: 'index',
|
||||
path: '/index',
|
||||
meta: {
|
||||
title: '首页',
|
||||
},
|
||||
component: '@/pages/index',
|
||||
},
|
||||
{
|
||||
name: 'userManage',
|
||||
path: '/userManage',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
},
|
||||
component: '@/pages/userManage/userManage',
|
||||
},
|
||||
{
|
||||
name: 'downloadManage',
|
||||
path: '/downloadManage',
|
||||
meta: {
|
||||
title: '下载管理',
|
||||
},
|
||||
component: '@/pages/downloadManage/downloadManage',
|
||||
},
|
||||
{
|
||||
name: 'noticeManage',
|
||||
path: '/noticeManage',
|
||||
meta: {
|
||||
title: '公告管理',
|
||||
},
|
||||
component: '@/pages/noticeManage/noticeManage',
|
||||
},
|
||||
{
|
||||
@ -52,76 +67,153 @@ export default [
|
||||
{
|
||||
name: 'aboutManage',
|
||||
path: '/aboutManage',
|
||||
meta: {
|
||||
title: '关于我们',
|
||||
},
|
||||
component: '@/pages/aboutManage/aboutManage',
|
||||
},
|
||||
{
|
||||
name: 'helpManage',
|
||||
path: '/helpManage',
|
||||
meta: {
|
||||
title: '帮助中心',
|
||||
},
|
||||
component: '@/pages/helpManage/helpManage',
|
||||
},
|
||||
{
|
||||
name: 'readQuestionManage',
|
||||
path: '/readQuestionManage',
|
||||
meta: {
|
||||
title: '已读问题',
|
||||
},
|
||||
component: '@/pages/userQuestionManage/readQuestionManage',
|
||||
},
|
||||
{
|
||||
name: 'unreadQuestionManage',
|
||||
path: '/unreadQuestionManage',
|
||||
meta: {
|
||||
title: '未读问题',
|
||||
},
|
||||
component: '@/pages/userQuestionManage/unreadQuestionManage',
|
||||
},
|
||||
{
|
||||
name: 'friendLinkCategory',
|
||||
path: '/friendLinkCategory',
|
||||
meta: {
|
||||
title: '友情链接分类',
|
||||
},
|
||||
component: '@/pages/friendLinkManage/friendLinkCategory',
|
||||
},
|
||||
{
|
||||
name: 'friendLinkManage',
|
||||
path: '/friendLinkManage',
|
||||
meta: {
|
||||
title: '友情链接管理',
|
||||
},
|
||||
component: '@/pages/friendLinkManage/friendLinkManage',
|
||||
},
|
||||
{
|
||||
name: 'supplierTemplateManage',
|
||||
path: '/supplierTemplateManage',
|
||||
meta: {
|
||||
title: '供应商模板管理',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierTemplateManage/supplierTemplateManage',
|
||||
},
|
||||
{
|
||||
name: 'supplierTaskManage',
|
||||
path: '/supplierTaskManage',
|
||||
meta: {
|
||||
title: '供应商任务管理',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierTaskManage/supplierTaskManage',
|
||||
},
|
||||
{
|
||||
name: 'supplierEvaluateScore',
|
||||
path: '/supplierEvaluateScore',
|
||||
meta: {
|
||||
title: '供应商评分管理',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierEvaluateScore/supplierEvaluateScore',
|
||||
},
|
||||
{
|
||||
name: 'supplierEvaluateResult',
|
||||
path: '/supplierEvaluateResult',
|
||||
meta: {
|
||||
title: '供应商评价结果',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierEvaluateResult/supplierEvaluateResult',
|
||||
},
|
||||
{
|
||||
name: 'supplierEvaluateResultInfo',
|
||||
path: '/supplierEvaluateResult/supplierEvaluateResultInfo',
|
||||
meta: {
|
||||
title: '供应商评价结果详情',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierEvaluateResult/supplierEvaluateResultInfo',
|
||||
},
|
||||
{
|
||||
name: 'supplierEvaluateResultScoreDetail',
|
||||
path: '/supplierEvaluateResult/supplierEvaluateResultScoreDetail',
|
||||
meta: {
|
||||
title: '供应商评价结果得分明细',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierEvaluateResult/supplierEvaluateResultScoreDetail',
|
||||
},
|
||||
{
|
||||
name: 'supplierEvaluateResultScoreByList',
|
||||
path: '/supplierEvaluateResult/supplierEvaluateResultScoreByList',
|
||||
meta: {
|
||||
title: '供应商评价结果打分情况',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierEvaluateResult/supplierEvaluateResultScoreByList',
|
||||
},
|
||||
{
|
||||
name: 'supplierEvaluateResultByZb',
|
||||
path: '/supplierEvaluateResult/supplierEvaluateResultByZb',
|
||||
meta: {
|
||||
title: '评价结果详情',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierEvaluateResult/supplierEvaluateResultByZb',
|
||||
},
|
||||
{
|
||||
name: 'supplierEvaluateResultApproval',
|
||||
path: '/supplierEvaluateResultApproval',
|
||||
meta: {
|
||||
title: '供应商评价结果审批',
|
||||
},
|
||||
component: '@/pages/supplierEvaluateManage/supplierEvaluateResultApproval/supplierEvaluateResultApproval',
|
||||
},
|
||||
{
|
||||
name: 'supplierAnnualTemplateManage',
|
||||
path: '/supplierAnnualTemplateManage',
|
||||
meta: {
|
||||
title: '供应商年度模板管理',
|
||||
},
|
||||
component: '@/pages/supplierAnnualManage/supplierAnnualTemplateManage/supplierAnnualTemplateManage',
|
||||
},
|
||||
{
|
||||
name: 'supplierAnnualTaskManage',
|
||||
path: '/supplierAnnualTaskManage',
|
||||
meta: {
|
||||
title: '供应商年度任务管理',
|
||||
},
|
||||
component: '@/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage',
|
||||
},
|
||||
{
|
||||
name: 'supplierAnnualQuery',
|
||||
path: '/supplierAnnualQuery',
|
||||
meta: {
|
||||
title: '供应商年度查询',
|
||||
},
|
||||
component: '@/pages/supplierAnnualManage/supplierAnnualQuery/supplierAnnualQuery',
|
||||
},
|
||||
{
|
||||
name: 'supplierAnnualResult',
|
||||
path: '/supplierAnnualResult',
|
||||
meta: {
|
||||
title: '供应商年度结果',
|
||||
},
|
||||
component: '@/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult',
|
||||
},
|
||||
]
|
||||
|
43
src/dicts/supplierTemplateDict.ts
Normal file
43
src/dicts/supplierTemplateDict.ts
Normal file
@ -0,0 +1,43 @@
|
||||
// 模板类型 - 从任务字典导入
|
||||
import { TaskType, TaskTypeText } from './supplierTaskDict';
|
||||
|
||||
// 模板状态
|
||||
export const TemplateStatus = {
|
||||
ENABLED: 'enabled', // 启用
|
||||
DISABLED: 'disabled', // 禁用
|
||||
};
|
||||
|
||||
export const TemplateStatusText = {
|
||||
[TemplateStatus.ENABLED]: '已启用',
|
||||
[TemplateStatus.DISABLED]: '已禁用',
|
||||
};
|
||||
|
||||
export const TemplateStatusColor = {
|
||||
[TemplateStatus.ENABLED]: 'green',
|
||||
[TemplateStatus.DISABLED]: 'red',
|
||||
};
|
||||
|
||||
// 评价等级
|
||||
export const EvaluateLevel = {
|
||||
EXCELLENT: 'excellent', // 优秀
|
||||
GOOD: 'good', // 良好
|
||||
AVERAGE: 'average', // 一般
|
||||
POOR: 'poor', // 较差
|
||||
};
|
||||
|
||||
export const EvaluateLevelText = {
|
||||
[EvaluateLevel.EXCELLENT]: 'A',
|
||||
[EvaluateLevel.GOOD]: 'B',
|
||||
[EvaluateLevel.AVERAGE]: 'C',
|
||||
[EvaluateLevel.POOR]: 'D',
|
||||
};
|
||||
|
||||
export const EvaluateLevelColor = {
|
||||
[EvaluateLevel.EXCELLENT]: 'green',
|
||||
[EvaluateLevel.GOOD]: 'blue',
|
||||
[EvaluateLevel.AVERAGE]: 'orange',
|
||||
[EvaluateLevel.POOR]: 'red',
|
||||
};
|
||||
|
||||
// 重新导出任务类型
|
||||
export { TaskType, TaskTypeText };
|
@ -174,13 +174,11 @@ const DomesticForm: React.FC<DomesticFormProps> = ({
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
name="businessScope"
|
||||
label="经营范围"
|
||||
rules={[{ required: true, message: '请输入经营范围' }]}
|
||||
labelCol={{ span: 2 }}
|
||||
wrapperCol={{ span: 22 }}
|
||||
>
|
||||
<TextArea
|
||||
placeholder="金融专用产品、广告传媒"
|
||||
|
@ -182,7 +182,6 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
|
||||
<Form.Item
|
||||
label="注册资本"
|
||||
required
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Input.Group compact style={{ display: 'flex' }}>
|
||||
<Form.Item name="capitalCurrency" noStyle initialValue="USD">
|
||||
@ -208,17 +207,16 @@ const ForeignForm: React.FC<ForeignFormProps> = ({ form, countdown, handleGetCap
|
||||
</Input.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
name="businessScope"
|
||||
label="经营范围"
|
||||
labelCol={{ span: 2 }}
|
||||
wrapperCol={{ span: 22 }}
|
||||
rules={[{ required: true, message: '请输入经营范围' }]}
|
||||
>
|
||||
<TextArea placeholder="请输入经营范围" rows={2} maxLength={200} showCount />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}></Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
name="contactName"
|
||||
|
@ -0,0 +1,242 @@
|
||||
import React from 'react';
|
||||
import { Table, Progress, Typography, Card, Tag } from 'antd';
|
||||
import styles from './components.less';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
interface GeneralEvaluationProps {
|
||||
supplierName?: string;
|
||||
}
|
||||
|
||||
const GeneralEvaluation: React.FC<GeneralEvaluationProps> = ({ supplierName }) => {
|
||||
// 评价数据
|
||||
const evaluationData = [
|
||||
{
|
||||
key: '1',
|
||||
firstIndex: '基本情况(base case)',
|
||||
firstDescription: '企业基本资质与存续状态证明',
|
||||
firstScore: 4,
|
||||
secondIndex: '资质文件',
|
||||
secondScore: 1,
|
||||
averageScore: 3,
|
||||
rowSpan: 2,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
firstIndex: '基本情况(base case)',
|
||||
firstDescription: '企业基本资质与存续状态证明',
|
||||
firstScore: 4,
|
||||
secondIndex: 'XXX',
|
||||
secondScore: 3,
|
||||
averageScore: 1,
|
||||
rowSpan: 0, // 设为0表示不显示
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
firstIndex: '成本(cost)',
|
||||
firstDescription: '商品与服务的总拥有成本',
|
||||
firstScore: 7,
|
||||
secondIndex: 'XXX',
|
||||
secondScore: 7,
|
||||
averageScore: 6.5,
|
||||
rowSpan: 1,
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
firstIndex: '创新(innovate)',
|
||||
firstDescription: '为公司提供竞争优势,提高商业价值而持续提供创新性建议及解决方案',
|
||||
firstScore: 20,
|
||||
secondIndex: 'XXX',
|
||||
secondScore: 20,
|
||||
averageScore: 18.5,
|
||||
rowSpan: 1,
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
firstIndex: '服务 (service)',
|
||||
firstDescription: '为满足公司商业需求而提供的服务与支持',
|
||||
firstScore: 10,
|
||||
secondIndex: 'XXX',
|
||||
secondScore: 10,
|
||||
averageScore: 10,
|
||||
rowSpan: 1,
|
||||
},
|
||||
{
|
||||
key: '6',
|
||||
firstIndex: '质量(quality)',
|
||||
firstDescription: '提供满足需求(规格)的产品与服务',
|
||||
firstScore: 25,
|
||||
secondIndex: 'XXX',
|
||||
secondScore: 25,
|
||||
averageScore: 24,
|
||||
rowSpan: 1,
|
||||
},
|
||||
{
|
||||
key: '7',
|
||||
firstIndex: '绿色(environment protection)',
|
||||
firstDescription: '优秀的交付能力及与之相关的灵活性',
|
||||
firstScore: 25,
|
||||
secondIndex: 'XXX',
|
||||
secondScore: 25,
|
||||
averageScore: 24,
|
||||
rowSpan: 1,
|
||||
},
|
||||
{
|
||||
key: '8',
|
||||
firstIndex: '安全(safety)',
|
||||
firstDescription: '产品质量安全、环境保护、风险防控及商业道德约束',
|
||||
firstScore: 6,
|
||||
secondIndex: 'XXX',
|
||||
secondScore: 6,
|
||||
averageScore: 5,
|
||||
rowSpan: 1,
|
||||
},
|
||||
{
|
||||
key: '9',
|
||||
firstIndex: '廉洁(integrity)',
|
||||
firstDescription: '商业道德约束',
|
||||
firstScore: 3,
|
||||
secondIndex: '商业贿赂与道德风险',
|
||||
secondScore: 3,
|
||||
averageScore: 3,
|
||||
rowSpan: 1,
|
||||
},
|
||||
{
|
||||
key: '10',
|
||||
firstIndex: '合计',
|
||||
firstDescription: '',
|
||||
firstScore: 100,
|
||||
secondIndex: '',
|
||||
secondScore: 100,
|
||||
averageScore: 95,
|
||||
rowSpan: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
width: 50,
|
||||
render: (text: string, record: any, index: number) => index + 1,
|
||||
},
|
||||
{
|
||||
title: '一级指标',
|
||||
children: [
|
||||
{
|
||||
title: '*基本指标',
|
||||
dataIndex: 'firstIndex',
|
||||
key: 'firstIndex',
|
||||
width: 150,
|
||||
render: (text: string, record: any, index: number) => {
|
||||
return {
|
||||
children: text,
|
||||
props: {
|
||||
rowSpan: record.rowSpan,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '*指标说明',
|
||||
dataIndex: 'firstDescription',
|
||||
key: 'firstDescription',
|
||||
width: 220,
|
||||
render: (text: string, record: any) => {
|
||||
return {
|
||||
children: text,
|
||||
props: {
|
||||
rowSpan: record.rowSpan,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '分值',
|
||||
dataIndex: 'firstScore',
|
||||
key: 'firstScore',
|
||||
width: 80,
|
||||
render: (text: number, record: any) => {
|
||||
return {
|
||||
children: text,
|
||||
props: {
|
||||
rowSpan: record.rowSpan,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '二级指标',
|
||||
children: [
|
||||
{
|
||||
title: '*细分指标',
|
||||
dataIndex: 'secondIndex',
|
||||
key: 'secondIndex',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '分值',
|
||||
dataIndex: 'secondScore',
|
||||
key: 'secondScore',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '平均分',
|
||||
dataIndex: 'averageScore',
|
||||
key: 'averageScore',
|
||||
width: 80,
|
||||
render: (score: number) => (
|
||||
<span className={score < 60 ? styles.lowScore : ''}>
|
||||
{score}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// 计算总分
|
||||
const totalScore = evaluationData[evaluationData.length - 1].averageScore;
|
||||
const totalPossibleScore = evaluationData[evaluationData.length - 1].firstScore;
|
||||
const scorePercentage = (totalScore / totalPossibleScore) * 100;
|
||||
|
||||
return (
|
||||
<div className={styles.evaluationContainer}>
|
||||
<Card className={styles.summaryCard}>
|
||||
<div className={styles.scoreSummary}>
|
||||
<div className={styles.scoreCircle}>
|
||||
<Progress
|
||||
type="circle"
|
||||
percent={scorePercentage}
|
||||
format={() => `${totalScore}`}
|
||||
width={80}
|
||||
strokeColor={{
|
||||
'0%': '#108ee9',
|
||||
'100%': '#87d068',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.scoreInfo}>
|
||||
<Title level={4}>通用评价总分</Title>
|
||||
<Text type="secondary">满分: {totalPossibleScore}分</Text>
|
||||
{supplierName && <Text type="secondary">供应商: {supplierName}</Text>}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={evaluationData}
|
||||
pagination={false}
|
||||
className={styles.scoreTable}
|
||||
bordered
|
||||
size="middle"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralEvaluation;
|
@ -0,0 +1,265 @@
|
||||
import React from 'react';
|
||||
import { Table, Progress, Typography, Card, Tag } from 'antd';
|
||||
import styles from './components.less';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
interface TechnicalEvaluationProps {
|
||||
supplierName?: string;
|
||||
}
|
||||
|
||||
const TechnicalEvaluation: React.FC<TechnicalEvaluationProps> = ({ supplierName }) => {
|
||||
// 评价数据
|
||||
const evaluationData = [
|
||||
{
|
||||
key: '1',
|
||||
firstIndex: '技术实力',
|
||||
firstDescription: '技术团队规模与研发能力',
|
||||
firstScore: 20,
|
||||
secondIndex: '研发团队规模',
|
||||
secondScore: 10,
|
||||
averageScore: 9,
|
||||
rowSpan: 2,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
firstIndex: '技术实力',
|
||||
firstDescription: '技术团队规模与研发能力',
|
||||
firstScore: 20,
|
||||
secondIndex: '技术创新能力',
|
||||
secondScore: 10,
|
||||
averageScore: 8,
|
||||
rowSpan: 0, // 设为0表示不显示
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
firstIndex: '产品性能',
|
||||
firstDescription: '产品的技术指标与性能表现',
|
||||
firstScore: 25,
|
||||
secondIndex: '核心性能指标',
|
||||
secondScore: 15,
|
||||
averageScore: 14,
|
||||
rowSpan: 2,
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
firstIndex: '产品性能',
|
||||
firstDescription: '产品的技术指标与性能表现',
|
||||
firstScore: 25,
|
||||
secondIndex: '稳定性与可靠性',
|
||||
secondScore: 10,
|
||||
averageScore: 9.5,
|
||||
rowSpan: 0,
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
firstIndex: '技术支持',
|
||||
firstDescription: '提供的技术服务与支持能力',
|
||||
firstScore: 15,
|
||||
secondIndex: '响应速度',
|
||||
secondScore: 8,
|
||||
averageScore: 7.5,
|
||||
rowSpan: 2,
|
||||
},
|
||||
{
|
||||
key: '6',
|
||||
firstIndex: '技术支持',
|
||||
firstDescription: '提供的技术服务与支持能力',
|
||||
firstScore: 15,
|
||||
secondIndex: '问题解决能力',
|
||||
secondScore: 7,
|
||||
averageScore: 6.5,
|
||||
rowSpan: 0,
|
||||
},
|
||||
{
|
||||
key: '7',
|
||||
firstIndex: '技术文档',
|
||||
firstDescription: '技术文档的完整性与准确性',
|
||||
firstScore: 10,
|
||||
secondIndex: '文档完整性',
|
||||
secondScore: 5,
|
||||
averageScore: 4.5,
|
||||
rowSpan: 2,
|
||||
},
|
||||
{
|
||||
key: '8',
|
||||
firstIndex: '技术文档',
|
||||
firstDescription: '技术文档的完整性与准确性',
|
||||
firstScore: 10,
|
||||
secondIndex: '文档准确性',
|
||||
secondScore: 5,
|
||||
averageScore: 4,
|
||||
rowSpan: 0,
|
||||
},
|
||||
{
|
||||
key: '9',
|
||||
firstIndex: '技术培训',
|
||||
firstDescription: '提供的技术培训与知识转移',
|
||||
firstScore: 15,
|
||||
secondIndex: '培训质量',
|
||||
secondScore: 8,
|
||||
averageScore: 7,
|
||||
rowSpan: 2,
|
||||
},
|
||||
{
|
||||
key: '10',
|
||||
firstIndex: '技术培训',
|
||||
firstDescription: '提供的技术培训与知识转移',
|
||||
firstScore: 15,
|
||||
secondIndex: '培训频率',
|
||||
secondScore: 7,
|
||||
averageScore: 6,
|
||||
rowSpan: 0,
|
||||
},
|
||||
{
|
||||
key: '11',
|
||||
firstIndex: '兼容性',
|
||||
firstDescription: '与现有系统的兼容性',
|
||||
firstScore: 15,
|
||||
secondIndex: '系统兼容性评估',
|
||||
secondScore: 15,
|
||||
averageScore: 13.5,
|
||||
rowSpan: 1,
|
||||
},
|
||||
{
|
||||
key: '12',
|
||||
firstIndex: '合计',
|
||||
firstDescription: '',
|
||||
firstScore: 100,
|
||||
secondIndex: '',
|
||||
secondScore: 100,
|
||||
averageScore: 89.5,
|
||||
rowSpan: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
width: 50,
|
||||
render: (text: string, record: any, index: number) => index + 1,
|
||||
},
|
||||
{
|
||||
title: '一级指标',
|
||||
children: [
|
||||
{
|
||||
title: '*基本指标',
|
||||
dataIndex: 'firstIndex',
|
||||
key: 'firstIndex',
|
||||
width: 150,
|
||||
className: styles.requiredColumn,
|
||||
render: (text: string, record: any, index: number) => {
|
||||
return {
|
||||
children: text,
|
||||
props: {
|
||||
rowSpan: record.rowSpan,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '*指标说明',
|
||||
dataIndex: 'firstDescription',
|
||||
key: 'firstDescription',
|
||||
width: 220,
|
||||
className: styles.requiredColumn,
|
||||
render: (text: string, record: any) => {
|
||||
return {
|
||||
children: text,
|
||||
props: {
|
||||
rowSpan: record.rowSpan,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '分值',
|
||||
dataIndex: 'firstScore',
|
||||
key: 'firstScore',
|
||||
width: 80,
|
||||
render: (text: number, record: any) => {
|
||||
return {
|
||||
children: text,
|
||||
props: {
|
||||
rowSpan: record.rowSpan,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '二级指标',
|
||||
children: [
|
||||
{
|
||||
title: '*细分指标',
|
||||
dataIndex: 'secondIndex',
|
||||
key: 'secondIndex',
|
||||
width: 180,
|
||||
className: styles.requiredColumn,
|
||||
},
|
||||
{
|
||||
title: '分值',
|
||||
dataIndex: 'secondScore',
|
||||
key: 'secondScore',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '平均分',
|
||||
dataIndex: 'averageScore',
|
||||
key: 'averageScore',
|
||||
width: 80,
|
||||
render: (score: number) => (
|
||||
<span className={score < 60 ? styles.lowScore : ''}>
|
||||
{score}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// 计算总分
|
||||
const totalScore = evaluationData[evaluationData.length - 1].averageScore;
|
||||
const totalPossibleScore = evaluationData[evaluationData.length - 1].firstScore;
|
||||
const scorePercentage = (totalScore / totalPossibleScore) * 100;
|
||||
|
||||
return (
|
||||
<div className={styles.evaluationContainer}>
|
||||
<Card className={styles.summaryCard}>
|
||||
<div className={styles.scoreSummary}>
|
||||
<div className={styles.scoreCircle}>
|
||||
<Progress
|
||||
type="circle"
|
||||
percent={scorePercentage}
|
||||
format={() => `${totalScore}`}
|
||||
width={80}
|
||||
strokeColor={{
|
||||
'0%': '#108ee9',
|
||||
'100%': '#87d068',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.scoreInfo}>
|
||||
<Title level={4}>技术评价总分</Title>
|
||||
<Text type="secondary">满分: {totalPossibleScore}分</Text>
|
||||
{supplierName && <Text type="secondary">供应商: {supplierName}</Text>}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={evaluationData}
|
||||
pagination={false}
|
||||
className={styles.scoreTable}
|
||||
bordered
|
||||
size="middle"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TechnicalEvaluation;
|
@ -0,0 +1,80 @@
|
||||
.evaluationContainer {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.summaryCard {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.scoreSummary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.scoreCircle {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.scoreInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.scoreCell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
span {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.scoreTable {
|
||||
margin-top: 16px;
|
||||
|
||||
:global {
|
||||
.ant-table-thead > tr > th {
|
||||
background-color: #f5f5f5;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr > td {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 必填列样式 */
|
||||
.requiredColumn {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '*';
|
||||
color: #ff4d4f;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 低分警示 */
|
||||
.lowScore {
|
||||
color: #ff4d4f;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 表格嵌套表头样式 */
|
||||
:global {
|
||||
.ant-table-thead > tr > th.ant-table-cell-fix-left,
|
||||
.ant-table-thead > tr > th.ant-table-cell-fix-right {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/* 供应商评价结果模块公共样式 */
|
||||
|
||||
/* 详情项样式 */
|
||||
.detail-item {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.label {
|
||||
width: 120px;
|
||||
text-align: right;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-weight: 500;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
/* 页面标题行样式 */
|
||||
.headerRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* 标题区域样式 */
|
||||
.titleSection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 操作区域样式 */
|
||||
.actionSection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 页面标题样式 */
|
||||
.pageTitle {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* 页面头部样式 - 从supplierEvaluateResultInfo.less */
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
|
||||
h2 {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
/* 详情项样式 - 从supplierEvaluateResultInfo.less */
|
||||
.detailItem {
|
||||
margin-bottom: 8px;
|
||||
line-height: 22px;
|
||||
|
||||
.label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: inline-block;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
}
|
||||
|
||||
/* 得分详情模态框样式 - 从supplierEvaluateResultInfo.less */
|
||||
.scoreDetailModal {
|
||||
.scoreItem {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.scoreLabel {
|
||||
width: 120px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.scoreValue {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 打分情况模态框样式 - 从supplierEvaluateResultInfo.less */
|
||||
.scoringModal {
|
||||
.scoringItem {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.scoringLabel {
|
||||
width: 120px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.scoringValue {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,321 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Card,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Button,
|
||||
Table,
|
||||
Space,
|
||||
Tag,
|
||||
DatePicker,
|
||||
TablePaginationConfig,
|
||||
Modal,
|
||||
Row,
|
||||
Col,
|
||||
Tooltip,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
SearchOutlined,
|
||||
DeleteOutlined,
|
||||
EyeOutlined,
|
||||
PlusOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { TaskStatus, TaskStatusText, TaskStatusColor, TaskType, TaskTypeText } from '@/dicts/supplierTaskDict';
|
||||
import moment from 'moment';
|
||||
import styles from './supplierEvaluateResult.less';
|
||||
import { history } from 'umi';
|
||||
|
||||
const { Option } = Select;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
// 删除本地接口定义,使用全局定义
|
||||
|
||||
const SupplierEvaluateResult: React.FC = () => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [form] = Form.useForm();
|
||||
const [resultData, setResultData] = useState<SupplierEvaluate.EvaluateResultRecord[]>([]);
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
});
|
||||
const [searchParams, setSearchParams] = useState<SupplierEvaluate.EvaluateResultSearchParams>({});
|
||||
|
||||
// 品类数据
|
||||
const categoryOptions = [
|
||||
{ label: '食品', value: '食品' },
|
||||
{ label: '电子', value: '电子' },
|
||||
{ label: '机械', value: '机械' },
|
||||
{ label: '化工', value: '化工' },
|
||||
{ label: '医药', value: '医药' },
|
||||
];
|
||||
|
||||
// 创建单位数据
|
||||
const unitOptions = [
|
||||
{ label: '中山市合创展包装材料有限公司', value: '中山市合创展包装材料有限公司' },
|
||||
{ label: '广州市科技发展有限公司', value: '广州市科技发展有限公司' },
|
||||
{ label: '深圳市创新科技有限公司', value: '深圳市创新科技有限公司' },
|
||||
{ label: '东莞市制造业有限公司', value: '东莞市制造业有限公司' },
|
||||
];
|
||||
|
||||
// 模拟获取评价结果列表
|
||||
const fetchResultList = async (
|
||||
current = 1,
|
||||
pageSize = 10,
|
||||
params: SupplierEvaluate.EvaluateResultSearchParams = searchParams,
|
||||
) => {
|
||||
// 更新搜索参数状态
|
||||
if (params !== searchParams) {
|
||||
setSearchParams(params);
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 模拟数据
|
||||
const mockData: SupplierEvaluate.EvaluateResultRecord[] = Array.from({ length: 35 }).map((_, index) => {
|
||||
const id = `${index + 1}`;
|
||||
const status = Object.values(TaskStatus)[Math.floor(Math.random() * 3)];
|
||||
const taskType = Math.random() > 0.5 ? TaskType.REGULAR : TaskType.SPECIAL;
|
||||
|
||||
// 随机选择单位和品类
|
||||
const unitIndex = Math.floor(Math.random() * unitOptions.length);
|
||||
const categoryIndex = Math.floor(Math.random() * categoryOptions.length);
|
||||
|
||||
// 随机生成日期
|
||||
const startDate = moment().subtract(Math.floor(Math.random() * 60), 'days');
|
||||
const endDate = moment(startDate).add(Math.floor(Math.random() * 30) + 15, 'days');
|
||||
|
||||
return {
|
||||
id,
|
||||
key: id,
|
||||
evaluateTitle: `${TaskTypeText[taskType]}任务${index + 1}`,
|
||||
evaluateCategory: categoryOptions[categoryIndex].value,
|
||||
initiatingUnit: unitOptions[unitIndex].value,
|
||||
startTime: startDate.format('YYYY-MM-DD'),
|
||||
endTime: endDate.format('YYYY-MM-DD'),
|
||||
status,
|
||||
createTime: moment().subtract(Math.floor(Math.random() * 90), 'days').format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
});
|
||||
|
||||
// 根据搜索条件过滤
|
||||
let filteredData = [...mockData];
|
||||
if (params.evaluateTitle) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.evaluateTitle.includes(params.evaluateTitle || '')
|
||||
);
|
||||
}
|
||||
if (params.status) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.status === params.status
|
||||
);
|
||||
}
|
||||
if (params.timeRange && params.timeRange.length === 2) {
|
||||
const start = moment(params.timeRange[0]);
|
||||
const end = moment(params.timeRange[1]);
|
||||
filteredData = filteredData.filter(item => {
|
||||
const itemStart = moment(item.startTime);
|
||||
return itemStart.isBetween(start, end, null, '[]');
|
||||
});
|
||||
}
|
||||
|
||||
// 分页
|
||||
const startIndex = (current - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const paginatedData = filteredData.slice(startIndex, endIndex);
|
||||
|
||||
setResultData(paginatedData);
|
||||
setPagination({
|
||||
...pagination,
|
||||
current,
|
||||
pageSize,
|
||||
total: filteredData.length,
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('获取评价结果列表失败:', error);
|
||||
message.error('获取评价结果列表失败');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 首次加载获取数据
|
||||
useEffect(() => {
|
||||
fetchResultList(pagination.current, pagination.pageSize, {});
|
||||
}, []);
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleTableChange = (newPagination: TablePaginationConfig) => {
|
||||
fetchResultList(newPagination.current, newPagination.pageSize, searchParams);
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (values: any) => {
|
||||
const { timeRange, ...rest } = values;
|
||||
const params: SupplierEvaluate.EvaluateResultSearchParams = { ...rest };
|
||||
|
||||
if (timeRange && timeRange.length === 2) {
|
||||
params.timeRange = [timeRange[0].format('YYYY-MM-DD'), timeRange[1].format('YYYY-MM-DD')];
|
||||
}
|
||||
|
||||
fetchResultList(1, pagination.pageSize, params);
|
||||
};
|
||||
|
||||
// 处理重置
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
fetchResultList(1, pagination.pageSize, {});
|
||||
};
|
||||
|
||||
// 查看详情 - 修改为跳转到详情页
|
||||
const handleViewDetail = (record: SupplierEvaluate.EvaluateResultRecord) => {
|
||||
history.push({
|
||||
pathname: '/supplierEvaluateResult/supplierEvaluateResultInfo',
|
||||
state: { record }
|
||||
});
|
||||
};
|
||||
|
||||
// 获取状态标签
|
||||
const getStatusTag = (status: string) => {
|
||||
const color = TaskStatusColor[status as keyof typeof TaskStatusColor] || 'default';
|
||||
const text = TaskStatusText[status as keyof typeof TaskStatusText] || '未知状态';
|
||||
return <Tag color={color}>{text}</Tag>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
render: (_: any, __: SupplierEvaluate.EvaluateResultRecord, index: number) =>
|
||||
(pagination.current! - 1) * pagination.pageSize! + index + 1,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '评价主题',
|
||||
dataIndex: 'evaluateTitle',
|
||||
key: 'evaluateTitle',
|
||||
width: 200,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (evaluateTitle: string) => (
|
||||
<Tooltip placement="topLeft" title={evaluateTitle}>
|
||||
{evaluateTitle}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '评价品类',
|
||||
dataIndex: 'evaluateCategory',
|
||||
key: 'evaluateCategory',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '发起单位',
|
||||
dataIndex: 'initiatingUnit',
|
||||
key: 'initiatingUnit',
|
||||
width: 200,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (text: string) => (
|
||||
<Tooltip placement="topLeft" title={text}>
|
||||
{text}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '评价开始时间',
|
||||
dataIndex: 'startTime',
|
||||
key: 'startTime',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '评价结束时间',
|
||||
dataIndex: 'endTime',
|
||||
key: 'endTime',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '评价状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (status: string) => getStatusTag(status),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
align: 'center' as const,
|
||||
render: (_: unknown, record: SupplierEvaluate.EvaluateResultRecord) => (
|
||||
<Button type="link" onClick={() => handleViewDetail(record)}>
|
||||
查看
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card title="供应商评价结果">
|
||||
<div>供应商评价结果模块</div>
|
||||
</Card>
|
||||
<div className="common-container">
|
||||
<div className="filter-action-row">
|
||||
<Form
|
||||
form={form}
|
||||
layout="inline"
|
||||
onFinish={handleSearch}
|
||||
className="filter-form"
|
||||
>
|
||||
<Form.Item name="evaluateTitle" label="评价主题">
|
||||
<Input placeholder="请输入评价主题" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="timeRange" label="评价时间">
|
||||
<RangePicker
|
||||
placeholder={['开始日期', '结束日期']}
|
||||
format="YYYY-MM-DD"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="评价状态">
|
||||
<Select placeholder="请选择状态" allowClear style={{ width: 150 }}>
|
||||
<Option value={TaskStatus.DRAFT}>{TaskStatusText[TaskStatus.DRAFT]}</Option>
|
||||
<Option value={TaskStatus.PROCESSING}>{TaskStatusText[TaskStatus.PROCESSING]}</Option>
|
||||
<Option value={TaskStatus.COMPLETED}>{TaskStatusText[TaskStatus.COMPLETED]}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item className="filter-btns">
|
||||
<Button type="primary" icon={<SearchOutlined />} onClick={() => form.submit()}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={handleReset}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div className="content-area">
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={resultData}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={handleTableChange}
|
||||
scroll={{ x: 1100 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
// 评价结果详情
|
@ -0,0 +1,307 @@
|
||||
// 供应商评价结果详情
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Card,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Button,
|
||||
Table,
|
||||
Space,
|
||||
Tag,
|
||||
TablePaginationConfig,
|
||||
Modal,
|
||||
Row,
|
||||
Col,
|
||||
Tooltip,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
SearchOutlined,
|
||||
DeleteOutlined,
|
||||
ArrowLeftOutlined,
|
||||
FileTextOutlined,
|
||||
BarChartOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { history, useLocation } from 'umi';
|
||||
import moment from 'moment';
|
||||
import { EvaluateLevel, EvaluateLevelText, EvaluateLevelColor } from '@/dicts/supplierTemplateDict';
|
||||
import styles from './supplierEvaluateResult.less';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const SupplierEvaluateResultInfo: React.FC = () => {
|
||||
const location = useLocation<{ record: SupplierEvaluate.EvaluateResultRecord }>();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [form] = Form.useForm();
|
||||
const [resultData, setResultData] = useState<SupplierEvaluate.EvaluateResultDetailRecord[]>([]);
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
});
|
||||
const [searchParams, setSearchParams] =
|
||||
useState<SupplierEvaluate.EvaluateResultDetailSearchParams>({});
|
||||
const [parentRecord, setParentRecord] = useState<SupplierEvaluate.EvaluateResultRecord | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
// 品类数据
|
||||
const categoryOptions = [
|
||||
{ label: '食品', value: '食品' },
|
||||
{ label: '电子', value: '电子' },
|
||||
{ label: '机械', value: '机械' },
|
||||
{ label: '化工', value: '化工' },
|
||||
{ label: '医药', value: '医药' },
|
||||
];
|
||||
|
||||
// 评价等级选项
|
||||
const levelOptions = Object.entries(EvaluateLevelText).map(([value, label]) => ({
|
||||
label,
|
||||
value,
|
||||
}));
|
||||
|
||||
// 获取上级页面传递的数据
|
||||
useEffect(() => {
|
||||
if (location.state?.record) {
|
||||
setParentRecord(location.state.record);
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
// 模拟获取评价结果详情列表
|
||||
const fetchResultDetailList = async (
|
||||
current = 1,
|
||||
pageSize = 10,
|
||||
params: SupplierEvaluate.EvaluateResultDetailSearchParams = searchParams,
|
||||
) => {
|
||||
// 更新搜索参数状态
|
||||
if (params !== searchParams) {
|
||||
setSearchParams(params);
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 模拟数据
|
||||
const mockData: SupplierEvaluate.EvaluateResultDetailRecord[] = Array.from({
|
||||
length: 35,
|
||||
}).map((_, index) => {
|
||||
const id = `${index + 1}`;
|
||||
|
||||
// 随机选择品类
|
||||
const categoryIndex = Math.floor(Math.random() * categoryOptions.length);
|
||||
|
||||
// 随机生成得分和等级
|
||||
const score = Math.floor(Math.random() * 40) + 60; // 60-100分
|
||||
let level;
|
||||
if (score >= 90) {
|
||||
level = EvaluateLevel.EXCELLENT;
|
||||
} else if (score >= 80) {
|
||||
level = EvaluateLevel.GOOD;
|
||||
} else if (score >= 70) {
|
||||
level = EvaluateLevel.AVERAGE;
|
||||
} else {
|
||||
level = EvaluateLevel.POOR;
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
key: id,
|
||||
supplierName: `供应商${index + 1}`,
|
||||
category: categoryOptions[categoryIndex].value,
|
||||
score,
|
||||
level,
|
||||
};
|
||||
});
|
||||
|
||||
// 根据搜索条件过滤
|
||||
let filteredData = [...mockData];
|
||||
if (params.supplierName) {
|
||||
filteredData = filteredData.filter((item) =>
|
||||
item.supplierName.includes(params.supplierName || ''),
|
||||
);
|
||||
}
|
||||
if (params.level) {
|
||||
filteredData = filteredData.filter((item) => item.level === params.level);
|
||||
}
|
||||
|
||||
// 分页
|
||||
const startIndex = (current - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const paginatedData = filteredData.slice(startIndex, endIndex);
|
||||
|
||||
setResultData(paginatedData);
|
||||
setPagination({
|
||||
...pagination,
|
||||
current,
|
||||
pageSize,
|
||||
total: filteredData.length,
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('获取评价结果详情列表失败:', error);
|
||||
message.error('获取评价结果详情列表失败');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 首次加载获取数据
|
||||
useEffect(() => {
|
||||
fetchResultDetailList(pagination.current, pagination.pageSize, {});
|
||||
}, []);
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleTableChange = (newPagination: TablePaginationConfig) => {
|
||||
fetchResultDetailList(newPagination.current, newPagination.pageSize, searchParams);
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (values: any) => {
|
||||
fetchResultDetailList(1, pagination.pageSize, values);
|
||||
};
|
||||
|
||||
// 处理重置
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
fetchResultDetailList(1, pagination.pageSize, {});
|
||||
};
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
// 查看得分明细
|
||||
const handleViewScoreDetail = (record: SupplierEvaluate.EvaluateResultDetailRecord) => {
|
||||
history.push({
|
||||
pathname: '/supplierEvaluateResult/supplierEvaluateResultScoreDetail',
|
||||
state: { record }
|
||||
});
|
||||
};
|
||||
|
||||
// 查看打分情况
|
||||
const handleViewScoring = (record: SupplierEvaluate.EvaluateResultDetailRecord) => {
|
||||
message.info(`查看供应商 ${record.supplierName} 的打分情况`);
|
||||
};
|
||||
|
||||
// 获取等级标签
|
||||
const getLevelTag = (level: string) => {
|
||||
const text = EvaluateLevelText[level as keyof typeof EvaluateLevelText] || '未知等级';
|
||||
return text;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
render: (_: any, __: SupplierEvaluate.EvaluateResultDetailRecord, index: number) =>
|
||||
(pagination.current! - 1) * pagination.pageSize! + index + 1,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '供应商名称',
|
||||
dataIndex: 'supplierName',
|
||||
key: 'supplierName',
|
||||
width: 200,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (supplierName: string) => (
|
||||
<Tooltip placement="topLeft" title={supplierName}>
|
||||
{supplierName}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '品类',
|
||||
dataIndex: 'category',
|
||||
key: 'category',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '评价得分',
|
||||
dataIndex: 'score',
|
||||
key: 'score',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
title: '评价等级',
|
||||
dataIndex: 'level',
|
||||
key: 'level',
|
||||
width: 100,
|
||||
align: 'center' as const,
|
||||
render: (level: string) => getLevelTag(level),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
align: 'center' as const,
|
||||
render: (_: unknown, record: SupplierEvaluate.EvaluateResultDetailRecord) => (
|
||||
<Space size="middle">
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => handleViewScoreDetail(record)}
|
||||
>
|
||||
得分明细
|
||||
</Button>
|
||||
<Button type="link" onClick={() => handleViewScoring(record)}>
|
||||
打分情况
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="common-container">
|
||||
<div className="filter-action-row">
|
||||
<Form form={form} layout="inline" onFinish={handleSearch} className="filter-form">
|
||||
<Form.Item name="supplierName" label="供应商名称">
|
||||
<Input placeholder="请输入供应商名称" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="level" label="评价等级">
|
||||
<Select placeholder="请选择评价等级" allowClear style={{ width: 150 }}>
|
||||
{levelOptions.map((option) => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item className="filter-btns">
|
||||
<Button type="primary" icon={<SearchOutlined />} onClick={() => form.submit()}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button type="primary" danger icon={<DeleteOutlined />} onClick={handleReset}>
|
||||
重置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div className="right-buttons">
|
||||
<Button type="link" icon={<ArrowLeftOutlined />} onClick={handleBack}>
|
||||
返回
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="content-area">
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={resultData}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={handleTableChange}
|
||||
scroll={{ x: 1100 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupplierEvaluateResultInfo;
|
@ -0,0 +1 @@
|
||||
// 供应商评价结果打分情况
|
@ -0,0 +1,64 @@
|
||||
// 供应商评价结果得分明细
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Tabs, Button, Typography, Row, Col } from 'antd';
|
||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||
import { history, useLocation } from 'umi';
|
||||
import GeneralEvaluation from './components/GeneralEvaluation';
|
||||
import TechnicalEvaluation from './components/TechnicalEvaluation';
|
||||
import styles from './supplierEvaluateResult.less';
|
||||
|
||||
const { Title } = Typography;
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const SupplierEvaluateResultScoreDetail: React.FC = () => {
|
||||
const location = useLocation<{ record: SupplierEvaluate.EvaluateResultDetailRecord }>();
|
||||
const [supplierRecord, setSupplierRecord] = useState<SupplierEvaluate.EvaluateResultDetailRecord | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<string>('1');
|
||||
|
||||
// 获取上级页面传递的数据
|
||||
useEffect(() => {
|
||||
if (location.state?.record) {
|
||||
setSupplierRecord(location.state.record);
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
// 处理Tab切换
|
||||
const handleTabChange = (key: string) => {
|
||||
setActiveTab(key);
|
||||
};
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="common-container">
|
||||
<div className={styles.headerRow}>
|
||||
<div className={styles.titleSection}>
|
||||
<Title level={4} className={styles.pageTitle}>
|
||||
{supplierRecord?.supplierName || '供应商'} - 评价得分明细
|
||||
</Title>
|
||||
</div>
|
||||
<div className={styles.actionSection}>
|
||||
<Button type="link" icon={<ArrowLeftOutlined />} onClick={handleBack}>
|
||||
返回
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="content-area">
|
||||
<Tabs activeKey={activeTab} onChange={handleTabChange} type="card">
|
||||
<TabPane tab="通用评价" key="1">
|
||||
<GeneralEvaluation supplierName={supplierRecord?.supplierName} />
|
||||
</TabPane>
|
||||
<TabPane tab="技术评价" key="2">
|
||||
<TechnicalEvaluation supplierName={supplierRecord?.supplierName} />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupplierEvaluateResultScoreDetail;
|
@ -1,11 +1,385 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Card,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Button,
|
||||
Table,
|
||||
Space,
|
||||
Tag,
|
||||
DatePicker,
|
||||
TablePaginationConfig,
|
||||
Tooltip,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
SearchOutlined,
|
||||
DeleteOutlined,
|
||||
PlusOutlined,
|
||||
CheckOutlined,
|
||||
CloseOutlined,
|
||||
EyeOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { TaskStatus, TaskStatusText, TaskStatusColor } from '@/dicts/supplierTaskDict';
|
||||
import moment from 'moment';
|
||||
|
||||
const { Option } = Select;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
// 审批状态
|
||||
const ApprovalStatus = {
|
||||
PENDING: 'pending',
|
||||
APPROVED: 'approved',
|
||||
REJECTED: 'rejected',
|
||||
};
|
||||
|
||||
const ApprovalStatusText = {
|
||||
[ApprovalStatus.PENDING]: '待审批',
|
||||
[ApprovalStatus.APPROVED]: '已通过',
|
||||
[ApprovalStatus.REJECTED]: '已拒绝',
|
||||
};
|
||||
|
||||
const ApprovalStatusColor = {
|
||||
[ApprovalStatus.PENDING]: 'orange',
|
||||
[ApprovalStatus.APPROVED]: 'green',
|
||||
[ApprovalStatus.REJECTED]: 'red',
|
||||
};
|
||||
|
||||
// 定义搜索参数接口
|
||||
interface ApprovalSearchParams {
|
||||
evaluateTitle?: string;
|
||||
status?: string;
|
||||
timeRange?: [string, string];
|
||||
}
|
||||
|
||||
// 定义审批记录接口
|
||||
interface ApprovalRecord {
|
||||
id: string;
|
||||
evaluateTitle: string;
|
||||
evaluateCategory: string;
|
||||
initiatingUnit: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
status: string;
|
||||
createTime: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
const SupplierEvaluateResultApproval: React.FC = () => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [form] = Form.useForm();
|
||||
const [approvalData, setApprovalData] = useState<ApprovalRecord[]>([]);
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
});
|
||||
const [searchParams, setSearchParams] = useState<ApprovalSearchParams>({});
|
||||
|
||||
// 品类数据
|
||||
const categoryOptions = [
|
||||
{ label: '食品', value: '食品' },
|
||||
{ label: '电子', value: '电子' },
|
||||
{ label: '机械', value: '机械' },
|
||||
{ label: '化工', value: '化工' },
|
||||
{ label: '医药', value: '医药' },
|
||||
];
|
||||
|
||||
// 创建单位数据
|
||||
const unitOptions = [
|
||||
{ label: '中山市合创展包装材料有限公司', value: '中山市合创展包装材料有限公司' },
|
||||
{ label: '广州市科技发展有限公司', value: '广州市科技发展有限公司' },
|
||||
{ label: '深圳市创新科技有限公司', value: '深圳市创新科技有限公司' },
|
||||
{ label: '东莞市制造业有限公司', value: '东莞市制造业有限公司' },
|
||||
];
|
||||
|
||||
// 模拟获取审批列表
|
||||
const fetchApprovalList = async (
|
||||
current = 1,
|
||||
pageSize = 10,
|
||||
params: ApprovalSearchParams = searchParams,
|
||||
) => {
|
||||
// 更新搜索参数状态
|
||||
if (params !== searchParams) {
|
||||
setSearchParams(params);
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 模拟数据
|
||||
const mockData: ApprovalRecord[] = Array.from({ length: 35 }).map((_, index) => {
|
||||
const id = `${index + 1}`;
|
||||
const statusOptions = [ApprovalStatus.PENDING, ApprovalStatus.APPROVED, ApprovalStatus.REJECTED];
|
||||
const status = statusOptions[Math.floor(Math.random() * statusOptions.length)];
|
||||
|
||||
// 随机选择单位和品类
|
||||
const unitIndex = Math.floor(Math.random() * unitOptions.length);
|
||||
const categoryIndex = Math.floor(Math.random() * categoryOptions.length);
|
||||
|
||||
// 随机生成日期
|
||||
const startDate = moment().subtract(Math.floor(Math.random() * 60), 'days');
|
||||
const endDate = moment(startDate).add(Math.floor(Math.random() * 30) + 15, 'days');
|
||||
|
||||
return {
|
||||
id,
|
||||
key: id,
|
||||
evaluateTitle: `供应商评价任务${index + 1}`,
|
||||
evaluateCategory: categoryOptions[categoryIndex].value,
|
||||
initiatingUnit: unitOptions[unitIndex].value,
|
||||
startTime: startDate.format('YYYY-MM-DD'),
|
||||
endTime: endDate.format('YYYY-MM-DD'),
|
||||
status,
|
||||
createTime: moment().subtract(Math.floor(Math.random() * 90), 'days').format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
});
|
||||
|
||||
// 根据搜索条件过滤
|
||||
let filteredData = [...mockData];
|
||||
if (params.evaluateTitle) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.evaluateTitle.includes(params.evaluateTitle || '')
|
||||
);
|
||||
}
|
||||
if (params.status) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.status === params.status
|
||||
);
|
||||
}
|
||||
if (params.timeRange && params.timeRange.length === 2) {
|
||||
const start = moment(params.timeRange[0]);
|
||||
const end = moment(params.timeRange[1]);
|
||||
filteredData = filteredData.filter(item => {
|
||||
const itemStart = moment(item.startTime);
|
||||
return itemStart.isBetween(start, end, null, '[]');
|
||||
});
|
||||
}
|
||||
|
||||
// 分页
|
||||
const startIndex = (current - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const paginatedData = filteredData.slice(startIndex, endIndex);
|
||||
|
||||
setApprovalData(paginatedData);
|
||||
setPagination({
|
||||
...pagination,
|
||||
current,
|
||||
pageSize,
|
||||
total: filteredData.length,
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('获取审批列表失败:', error);
|
||||
message.error('获取审批列表失败');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 首次加载获取数据
|
||||
useEffect(() => {
|
||||
fetchApprovalList(pagination.current, pagination.pageSize, {});
|
||||
}, []);
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleTableChange = (newPagination: TablePaginationConfig) => {
|
||||
fetchApprovalList(newPagination.current, newPagination.pageSize, searchParams);
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (values: any) => {
|
||||
const { timeRange, ...rest } = values;
|
||||
const params: ApprovalSearchParams = { ...rest };
|
||||
|
||||
if (timeRange && timeRange.length === 2) {
|
||||
params.timeRange = [timeRange[0].format('YYYY-MM-DD'), timeRange[1].format('YYYY-MM-DD')];
|
||||
}
|
||||
|
||||
fetchApprovalList(1, pagination.pageSize, params);
|
||||
};
|
||||
|
||||
// 处理重置
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
fetchApprovalList(1, pagination.pageSize, {});
|
||||
};
|
||||
|
||||
// 处理审批通过
|
||||
const handleApprove = (record: ApprovalRecord) => {
|
||||
message.success(`已通过评价: ${record.evaluateTitle}`);
|
||||
fetchApprovalList(pagination.current, pagination.pageSize, searchParams);
|
||||
};
|
||||
|
||||
// 处理审批拒绝
|
||||
const handleReject = (record: ApprovalRecord) => {
|
||||
message.error(`已拒绝评价: ${record.evaluateTitle}`);
|
||||
fetchApprovalList(pagination.current, pagination.pageSize, searchParams);
|
||||
};
|
||||
|
||||
// 处理查看详情
|
||||
const handleViewDetail = (record: ApprovalRecord) => {
|
||||
message.info(`查看评价详情: ${record.evaluateTitle}`);
|
||||
};
|
||||
|
||||
// 获取状态标签
|
||||
const getStatusTag = (status: string) => {
|
||||
const color = ApprovalStatusColor[status as keyof typeof ApprovalStatusColor] || 'default';
|
||||
const text = ApprovalStatusText[status as keyof typeof ApprovalStatusText] || '未知状态';
|
||||
return <Tag color={color}>{text}</Tag>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
render: (_: any, __: ApprovalRecord, index: number) =>
|
||||
(pagination.current! - 1) * pagination.pageSize! + index + 1,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '评价主题',
|
||||
dataIndex: 'evaluateTitle',
|
||||
key: 'evaluateTitle',
|
||||
width: 200,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (evaluateTitle: string) => (
|
||||
<Tooltip placement="topLeft" title={evaluateTitle}>
|
||||
{evaluateTitle}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '评价品类',
|
||||
dataIndex: 'evaluateCategory',
|
||||
key: 'evaluateCategory',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '发起单位',
|
||||
dataIndex: 'initiatingUnit',
|
||||
key: 'initiatingUnit',
|
||||
width: 200,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (text: string) => (
|
||||
<Tooltip placement="topLeft" title={text}>
|
||||
{text}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '评价开始时间',
|
||||
dataIndex: 'startTime',
|
||||
key: 'startTime',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '评价结束时间',
|
||||
dataIndex: 'endTime',
|
||||
key: 'endTime',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '审批状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (status: string) => getStatusTag(status),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center' as const,
|
||||
render: (_: unknown, record: ApprovalRecord) => (
|
||||
<Space size="middle">
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EyeOutlined />}
|
||||
onClick={() => handleViewDetail(record)}
|
||||
>
|
||||
查看
|
||||
</Button>
|
||||
{record.status === ApprovalStatus.PENDING && (
|
||||
<>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<CheckOutlined />}
|
||||
onClick={() => handleApprove(record)}
|
||||
style={{ color: 'green' }}
|
||||
>
|
||||
通过
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => handleReject(record)}
|
||||
danger
|
||||
>
|
||||
拒绝
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card title="供应商评价结果审批">
|
||||
<div>供应商评价结果审批模块</div>
|
||||
</Card>
|
||||
<div className="common-container">
|
||||
<div className="filter-action-row">
|
||||
<Form form={form} layout="inline" onFinish={handleSearch} className="filter-form">
|
||||
<Form.Item name="evaluateTitle" label="评价主题">
|
||||
<Input placeholder="请输入评价主题" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="timeRange" label="评价时间">
|
||||
<RangePicker
|
||||
placeholder={['开始日期', '结束日期']}
|
||||
format="YYYY-MM-DD"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="审批状态">
|
||||
<Select placeholder="请选择状态" allowClear style={{ width: 150 }}>
|
||||
<Option value={ApprovalStatus.PENDING}>{ApprovalStatusText[ApprovalStatus.PENDING]}</Option>
|
||||
<Option value={ApprovalStatus.APPROVED}>{ApprovalStatusText[ApprovalStatus.APPROVED]}</Option>
|
||||
<Option value={ApprovalStatus.REJECTED}>{ApprovalStatusText[ApprovalStatus.REJECTED]}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item className="filter-btns">
|
||||
<Button type="primary" icon={<SearchOutlined />} onClick={() => form.submit()}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button type="primary" danger icon={<DeleteOutlined />} onClick={handleReset}>
|
||||
重置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div className="right-buttons">
|
||||
<Button type="primary" ghost icon={<PlusOutlined />} onClick={() => message.info('新增审批')}>
|
||||
新增
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="content-area">
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={approvalData}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={handleTableChange}
|
||||
scroll={{ x: 1200 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,11 +1,348 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Card,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Button,
|
||||
Table,
|
||||
Space,
|
||||
Tag,
|
||||
DatePicker,
|
||||
TablePaginationConfig,
|
||||
Tooltip,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
SearchOutlined,
|
||||
DeleteOutlined,
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
EyeOutlined,
|
||||
FormOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { TaskStatus, TaskStatusText, TaskStatusColor } from '@/dicts/supplierTaskDict';
|
||||
import moment from 'moment';
|
||||
|
||||
const { Option } = Select;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
// 定义搜索参数接口
|
||||
interface ScoreSearchParams {
|
||||
evaluateTitle?: string;
|
||||
supplierName?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
// 定义打分记录接口
|
||||
interface ScoreRecord {
|
||||
id: string;
|
||||
evaluateTitle: string;
|
||||
supplierName: string;
|
||||
category: string;
|
||||
status: string;
|
||||
scoreStatus: string; // 打分状态:未打分、已打分、已提交
|
||||
createTime: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
// 打分状态
|
||||
const ScoreStatus = {
|
||||
NOT_SCORED: 'not_scored',
|
||||
SCORED: 'scored',
|
||||
SUBMITTED: 'submitted',
|
||||
};
|
||||
|
||||
const ScoreStatusText = {
|
||||
[ScoreStatus.NOT_SCORED]: '未打分',
|
||||
[ScoreStatus.SCORED]: '已打分',
|
||||
[ScoreStatus.SUBMITTED]: '已提交',
|
||||
};
|
||||
|
||||
const ScoreStatusColor = {
|
||||
[ScoreStatus.NOT_SCORED]: 'orange',
|
||||
[ScoreStatus.SCORED]: 'blue',
|
||||
[ScoreStatus.SUBMITTED]: 'green',
|
||||
};
|
||||
|
||||
const SupplierEvaluateScore: React.FC = () => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [form] = Form.useForm();
|
||||
const [scoreData, setScoreData] = useState<ScoreRecord[]>([]);
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
});
|
||||
const [searchParams, setSearchParams] = useState<ScoreSearchParams>({});
|
||||
|
||||
// 品类数据
|
||||
const categoryOptions = [
|
||||
{ label: '食品', value: '食品' },
|
||||
{ label: '电子', value: '电子' },
|
||||
{ label: '机械', value: '机械' },
|
||||
{ label: '化工', value: '化工' },
|
||||
{ label: '医药', value: '医药' },
|
||||
];
|
||||
|
||||
// 模拟获取打分列表
|
||||
const fetchScoreList = async (
|
||||
current = 1,
|
||||
pageSize = 10,
|
||||
params: ScoreSearchParams = searchParams,
|
||||
) => {
|
||||
// 更新搜索参数状态
|
||||
if (params !== searchParams) {
|
||||
setSearchParams(params);
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 模拟数据
|
||||
const mockData: ScoreRecord[] = Array.from({ length: 35 }).map((_, index) => {
|
||||
const id = `${index + 1}`;
|
||||
const status = Object.values(TaskStatus)[Math.floor(Math.random() * 3)];
|
||||
const scoreStatus = Object.values(ScoreStatus)[Math.floor(Math.random() * 3)];
|
||||
|
||||
// 随机选择品类
|
||||
const categoryIndex = Math.floor(Math.random() * categoryOptions.length);
|
||||
|
||||
return {
|
||||
id,
|
||||
key: id,
|
||||
evaluateTitle: `供应商评价任务${index + 1}`,
|
||||
supplierName: `供应商${index + 1}`,
|
||||
category: categoryOptions[categoryIndex].value,
|
||||
status,
|
||||
scoreStatus,
|
||||
createTime: moment().subtract(Math.floor(Math.random() * 90), 'days').format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
});
|
||||
|
||||
// 根据搜索条件过滤
|
||||
let filteredData = [...mockData];
|
||||
if (params.evaluateTitle) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.evaluateTitle.includes(params.evaluateTitle || '')
|
||||
);
|
||||
}
|
||||
if (params.supplierName) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.supplierName.includes(params.supplierName || '')
|
||||
);
|
||||
}
|
||||
if (params.status) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.scoreStatus === params.status
|
||||
);
|
||||
}
|
||||
|
||||
// 分页
|
||||
const startIndex = (current - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const paginatedData = filteredData.slice(startIndex, endIndex);
|
||||
|
||||
setScoreData(paginatedData);
|
||||
setPagination({
|
||||
...pagination,
|
||||
current,
|
||||
pageSize,
|
||||
total: filteredData.length,
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('获取打分列表失败:', error);
|
||||
message.error('获取打分列表失败');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 首次加载获取数据
|
||||
useEffect(() => {
|
||||
fetchScoreList(pagination.current, pagination.pageSize, {});
|
||||
}, []);
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleTableChange = (newPagination: TablePaginationConfig) => {
|
||||
fetchScoreList(newPagination.current, newPagination.pageSize, searchParams);
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (values: any) => {
|
||||
fetchScoreList(1, pagination.pageSize, values);
|
||||
};
|
||||
|
||||
// 处理重置
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
fetchScoreList(1, pagination.pageSize, {});
|
||||
};
|
||||
|
||||
// 处理打分
|
||||
const handleScore = (record: ScoreRecord) => {
|
||||
message.info(`开始为 ${record.supplierName} 打分`);
|
||||
};
|
||||
|
||||
// 处理查看
|
||||
const handleView = (record: ScoreRecord) => {
|
||||
message.info(`查看 ${record.supplierName} 的打分情况`);
|
||||
};
|
||||
|
||||
// 处理提交
|
||||
const handleSubmit = (record: ScoreRecord) => {
|
||||
message.success(`已提交 ${record.supplierName} 的评分`);
|
||||
fetchScoreList(pagination.current, pagination.pageSize, searchParams);
|
||||
};
|
||||
|
||||
// 获取状态标签
|
||||
const getScoreStatusTag = (status: string) => {
|
||||
const color = ScoreStatusColor[status as keyof typeof ScoreStatusColor] || 'default';
|
||||
const text = ScoreStatusText[status as keyof typeof ScoreStatusText] || '未知状态';
|
||||
return <Tag color={color}>{text}</Tag>;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
render: (_: any, __: ScoreRecord, index: number) =>
|
||||
(pagination.current! - 1) * pagination.pageSize! + index + 1,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '评价主题',
|
||||
dataIndex: 'evaluateTitle',
|
||||
key: 'evaluateTitle',
|
||||
width: 200,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (evaluateTitle: string) => (
|
||||
<Tooltip placement="topLeft" title={evaluateTitle}>
|
||||
{evaluateTitle}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '供应商名称',
|
||||
dataIndex: 'supplierName',
|
||||
key: 'supplierName',
|
||||
width: 150,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (supplierName: string) => (
|
||||
<Tooltip placement="topLeft" title={supplierName}>
|
||||
{supplierName}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '品类',
|
||||
dataIndex: 'category',
|
||||
key: 'category',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '打分状态',
|
||||
dataIndex: 'scoreStatus',
|
||||
key: 'scoreStatus',
|
||||
width: 100,
|
||||
render: (status: string) => getScoreStatusTag(status),
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center' as const,
|
||||
render: (_: unknown, record: ScoreRecord) => (
|
||||
<Space size="middle">
|
||||
{record.scoreStatus === ScoreStatus.NOT_SCORED && (
|
||||
<Button
|
||||
type="link"
|
||||
icon={<FormOutlined />}
|
||||
onClick={() => handleScore(record)}
|
||||
>
|
||||
打分
|
||||
</Button>
|
||||
)}
|
||||
{record.scoreStatus === ScoreStatus.SCORED && (
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => handleSubmit(record)}
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EyeOutlined />}
|
||||
onClick={() => handleView(record)}
|
||||
>
|
||||
查看
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card title="供应商评价打分">
|
||||
<div>供应商评价打分模块</div>
|
||||
</Card>
|
||||
<div className="common-container">
|
||||
<div className="filter-action-row">
|
||||
<Form form={form} layout="inline" onFinish={handleSearch} className="filter-form">
|
||||
<Form.Item name="evaluateTitle" label="评价主题">
|
||||
<Input placeholder="请输入评价主题" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="supplierName" label="供应商名称">
|
||||
<Input placeholder="请输入供应商名称" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="打分状态">
|
||||
<Select placeholder="请选择状态" allowClear style={{ width: 150 }}>
|
||||
<Option value={ScoreStatus.NOT_SCORED}>{ScoreStatusText[ScoreStatus.NOT_SCORED]}</Option>
|
||||
<Option value={ScoreStatus.SCORED}>{ScoreStatusText[ScoreStatus.SCORED]}</Option>
|
||||
<Option value={ScoreStatus.SUBMITTED}>{ScoreStatusText[ScoreStatus.SUBMITTED]}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item className="filter-btns">
|
||||
<Button type="primary" icon={<SearchOutlined />} onClick={() => form.submit()}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button type="primary" danger icon={<DeleteOutlined />} onClick={handleReset}>
|
||||
重置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div className="right-buttons">
|
||||
<Button type="primary" ghost icon={<PlusOutlined />} onClick={() => message.info('新增打分任务')}>
|
||||
新增
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="content-area">
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={scoreData}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={handleTableChange}
|
||||
scroll={{ x: 1100 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -29,28 +29,7 @@ import styles from './supplierTaskManage.less';
|
||||
const { Option } = Select;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
// 任务记录类型
|
||||
interface TaskRecord {
|
||||
id: string;
|
||||
taskName: string;
|
||||
taskCode: string;
|
||||
taskType: string;
|
||||
templateName: string;
|
||||
status: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
createBy: string;
|
||||
createTime: string;
|
||||
updateBy?: string;
|
||||
updateTime?: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
interface SearchParams {
|
||||
taskName?: string;
|
||||
status?: string;
|
||||
dateRange?: [string, string];
|
||||
}
|
||||
// 删除本地接口定义,使用全局类型定义
|
||||
|
||||
const SupplierTaskManage: React.FC = () => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
@ -62,8 +41,8 @@ const SupplierTaskManage: React.FC = () => {
|
||||
const [currentId, setCurrentId] = useState<string>('');
|
||||
|
||||
// 查看详情数据
|
||||
const [viewData, setViewData] = useState<TaskRecord | null>(null);
|
||||
const [taskData, setTaskData] = useState<TaskRecord[]>([]);
|
||||
const [viewData, setViewData] = useState<SupplierEvaluate.TaskRecord | null>(null);
|
||||
const [taskData, setTaskData] = useState<SupplierEvaluate.TaskRecord[]>([]);
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
@ -72,13 +51,13 @@ const SupplierTaskManage: React.FC = () => {
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
});
|
||||
const [searchParams, setSearchParams] = useState<SearchParams>({});
|
||||
const [searchParams, setSearchParams] = useState<SupplierEvaluate.TaskSearchParams>({});
|
||||
|
||||
// 模拟获取任务列表
|
||||
const fetchTaskList = async (
|
||||
current = 1,
|
||||
pageSize = 10,
|
||||
params: SearchParams = searchParams,
|
||||
params: SupplierEvaluate.TaskSearchParams = searchParams,
|
||||
) => {
|
||||
// 更新搜索参数状态
|
||||
if (params !== searchParams) {
|
||||
@ -90,7 +69,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 模拟数据
|
||||
const mockData: TaskRecord[] = Array.from({ length: 35 }).map((_, index) => {
|
||||
const mockData: SupplierEvaluate.TaskRecord[] = Array.from({ length: 35 }).map((_, index) => {
|
||||
const id = `${index + 1}`;
|
||||
const status = Object.values(TaskStatus)[Math.floor(Math.random() * 3)];
|
||||
const taskType = Math.random() > 0.5 ? TaskType.REGULAR : TaskType.SPECIAL;
|
||||
@ -155,7 +134,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
}, []);
|
||||
|
||||
// 处理查看
|
||||
const handleView = (record: TaskRecord) => {
|
||||
const handleView = (record: SupplierEvaluate.TaskRecord) => {
|
||||
setCurrentId(record.id);
|
||||
setIsViewMode(true);
|
||||
setViewData(record);
|
||||
@ -163,7 +142,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
};
|
||||
|
||||
// 处理编辑
|
||||
const handleEdit = (record: TaskRecord) => {
|
||||
const handleEdit = (record: SupplierEvaluate.TaskRecord) => {
|
||||
setIsEdit(true);
|
||||
setIsViewMode(false);
|
||||
setCurrentId(record.id);
|
||||
@ -172,7 +151,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
};
|
||||
|
||||
// 处理删除
|
||||
const showDeleteConfirm = (record: TaskRecord) => {
|
||||
const showDeleteConfirm = (record: SupplierEvaluate.TaskRecord) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
@ -197,7 +176,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
};
|
||||
|
||||
// 处理发布/取消
|
||||
const handlePublishStatus = (record: TaskRecord) => {
|
||||
const handlePublishStatus = (record: SupplierEvaluate.TaskRecord) => {
|
||||
const isInProgress = record.status === TaskStatus.PROCESSING;
|
||||
const actionText = isInProgress ? '取消' : '开始';
|
||||
|
||||
@ -238,7 +217,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
render: (_: any, __: TaskRecord, index: number) =>
|
||||
render: (_: any, __: SupplierEvaluate.TaskRecord, index: number) =>
|
||||
(pagination.current! - 1) * pagination.pageSize! + index + 1,
|
||||
width: 80,
|
||||
},
|
||||
@ -286,7 +265,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
key: 'action',
|
||||
width: 180,
|
||||
align: 'center' as const,
|
||||
render: (_: unknown, record: TaskRecord) => (
|
||||
render: (_: unknown, record: SupplierEvaluate.TaskRecord) => (
|
||||
<Space size="middle">
|
||||
<Button type="link" onClick={() => handleView(record)}>
|
||||
查看
|
||||
@ -354,7 +333,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
// 处理搜索
|
||||
const handleSearch = (values: any) => {
|
||||
const { dateRange, ...rest } = values;
|
||||
const params: SearchParams = { ...rest };
|
||||
const params: SupplierEvaluate.TaskSearchParams = { ...rest };
|
||||
|
||||
if (dateRange && dateRange.length === 2) {
|
||||
params.dateRange = [dateRange[0].format('YYYY-MM-DD'), dateRange[1].format('YYYY-MM-DD')];
|
||||
@ -544,26 +523,23 @@ const SupplierTaskManage: React.FC = () => {
|
||||
<Form.Item name="dateRange" label="评价时间">
|
||||
<RangePicker />
|
||||
</Form.Item>
|
||||
<Form.Item className="filter-btns">
|
||||
<Button type="primary" htmlType="submit" icon={<SearchOutlined />} onClick={() => form.submit()}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => {
|
||||
form.resetFields();
|
||||
fetchTaskList(1, pagination.pageSize, {});
|
||||
}}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div className="button-row">
|
||||
<div className="left-buttons">
|
||||
<Button type="primary" htmlType="submit" icon={<SearchOutlined />} onClick={() => form.submit()}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => {
|
||||
form.resetFields();
|
||||
fetchTaskList(1, pagination.pageSize, {});
|
||||
}}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="right-buttons">
|
||||
<Button type="primary" ghost icon={<PlusOutlined />} onClick={handleAdd}>
|
||||
新增
|
||||
|
@ -0,0 +1,3 @@
|
||||
.supplierTemplateManageContainer {
|
||||
}
|
||||
|
@ -1,11 +1,666 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Table,
|
||||
Space,
|
||||
Modal,
|
||||
message,
|
||||
Input,
|
||||
Select,
|
||||
Form,
|
||||
Tooltip,
|
||||
Tag,
|
||||
TablePaginationConfig,
|
||||
DatePicker,
|
||||
Row,
|
||||
Col,
|
||||
} from 'antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
SearchOutlined,
|
||||
EditOutlined,
|
||||
EyeOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { TaskType, TaskTypeText, TemplateStatus, TemplateStatusText, TemplateStatusColor } from '@/dicts/supplierTemplateDict';
|
||||
|
||||
const { Option } = Select;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const SupplierTemplateManage: React.FC = () => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [form] = Form.useForm();
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
||||
const [isViewMode, setIsViewMode] = useState<boolean>(false);
|
||||
const [currentId, setCurrentId] = useState<string>('');
|
||||
|
||||
// 查看详情数据
|
||||
const [viewData, setViewData] = useState<SupplierEvaluate.TemplateRecord | null>(null);
|
||||
const [templateData, setTemplateData] = useState<SupplierEvaluate.TemplateRecord[]>([]);
|
||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条记录`,
|
||||
});
|
||||
const [searchParams, setSearchParams] = useState<SupplierEvaluate.TemplateSearchParams>({});
|
||||
|
||||
// 创建单位数据
|
||||
const companyOptions = [
|
||||
{ label: '中山市合创展包装材料有限公司', value: '中山市合创展包装材料有限公司' },
|
||||
{ label: '广州市科技发展有限公司', value: '广州市科技发展有限公司' },
|
||||
{ label: '深圳市创新科技有限公司', value: '深圳市创新科技有限公司' },
|
||||
{ label: '东莞市制造业有限公司', value: '东莞市制造业有限公司' },
|
||||
];
|
||||
|
||||
// 品类数据
|
||||
const categoryOptions = [
|
||||
{ label: '食品', value: '食品' },
|
||||
{ label: '电子', value: '电子' },
|
||||
{ label: '机械', value: '机械' },
|
||||
{ label: '化工', value: '化工' },
|
||||
{ label: '医药', value: '医药' },
|
||||
];
|
||||
|
||||
// 部门数据
|
||||
const departmentOptions = [
|
||||
{ label: '采购部', value: '采购部' },
|
||||
{ label: '技术部', value: '技术部' },
|
||||
{ label: '质量部', value: '质量部' },
|
||||
{ label: '生产部', value: '生产部' },
|
||||
];
|
||||
|
||||
// 模拟获取模板列表
|
||||
const fetchTemplateList = async (
|
||||
current = 1,
|
||||
pageSize = 10,
|
||||
params: SupplierEvaluate.TemplateSearchParams = searchParams,
|
||||
) => {
|
||||
// 更新搜索参数状态
|
||||
if (params !== searchParams) {
|
||||
setSearchParams(params);
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 模拟数据
|
||||
const mockData: SupplierEvaluate.TemplateRecord[] = Array.from({ length: 35 }).map((_, index) => {
|
||||
const id = `${index + 1}`;
|
||||
const status = Math.random() > 0.5 ? TemplateStatus.ENABLED : TemplateStatus.DISABLED;
|
||||
const taskType = Math.random() > 0.5 ? TaskType.REGULAR : TaskType.SPECIAL;
|
||||
|
||||
// 随机选择单位和品类
|
||||
const companyIndex = Math.floor(Math.random() * companyOptions.length);
|
||||
const categoryIndex = Math.floor(Math.random() * categoryOptions.length);
|
||||
const departmentIndex = Math.floor(Math.random() * departmentOptions.length);
|
||||
|
||||
return {
|
||||
id,
|
||||
key: id,
|
||||
templateName: `${TaskTypeText[taskType]}评价模板${index + 1}`,
|
||||
templateCode: `TEMP${String(index + 1).padStart(4, '0')}`,
|
||||
templateType: taskType,
|
||||
category: categoryOptions[categoryIndex].value,
|
||||
status,
|
||||
createBy: companyOptions[companyIndex].value,
|
||||
department: departmentOptions[departmentIndex].value,
|
||||
createTime: '2023-01-01 12:00:00',
|
||||
};
|
||||
});
|
||||
|
||||
// 根据搜索条件过滤
|
||||
let filteredData = [...mockData];
|
||||
if (params.templateName) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.templateName.includes(params.templateName || '')
|
||||
);
|
||||
}
|
||||
if (params.status) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.status === params.status
|
||||
);
|
||||
}
|
||||
if (params.createBy) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.createBy === params.createBy
|
||||
);
|
||||
}
|
||||
if (params.category) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.category === params.category
|
||||
);
|
||||
}
|
||||
|
||||
// 分页
|
||||
const startIndex = (current - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const paginatedData = filteredData.slice(startIndex, endIndex);
|
||||
|
||||
setTemplateData(paginatedData);
|
||||
setPagination({
|
||||
...pagination,
|
||||
current,
|
||||
pageSize,
|
||||
total: filteredData.length,
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('获取模板列表失败:', error);
|
||||
message.error('获取模板列表失败');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 首次加载获取数据
|
||||
useEffect(() => {
|
||||
fetchTemplateList(pagination.current, pagination.pageSize, {});
|
||||
}, []);
|
||||
|
||||
// 处理查看
|
||||
const handleView = (record: SupplierEvaluate.TemplateRecord) => {
|
||||
setCurrentId(record.id);
|
||||
setIsViewMode(true);
|
||||
setViewData(record);
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
// 处理编辑
|
||||
const handleEdit = (record: SupplierEvaluate.TemplateRecord) => {
|
||||
setIsEdit(true);
|
||||
setIsViewMode(false);
|
||||
setCurrentId(record.id);
|
||||
setViewData(record);
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
// 处理删除
|
||||
const showDeleteConfirm = (record: SupplierEvaluate.TemplateRecord) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
content: `确定要删除模板: ${record.templateName}?`,
|
||||
okText: '确定',
|
||||
okType: 'danger',
|
||||
cancelText: '取消',
|
||||
maskClosable: false,
|
||||
onOk: async () => {
|
||||
try {
|
||||
// 模拟删除请求
|
||||
setTimeout(() => {
|
||||
message.success('删除成功');
|
||||
fetchTemplateList(pagination.current, pagination.pageSize, searchParams);
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('删除模板失败:', error);
|
||||
message.error('删除模板失败');
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 处理启用/禁用
|
||||
const handleStatusToggle = (record: SupplierEvaluate.TemplateRecord) => {
|
||||
const isEnabled = record.status === TemplateStatus.ENABLED;
|
||||
const actionText = isEnabled ? '禁用' : '启用';
|
||||
|
||||
Modal.confirm({
|
||||
title: `确认${actionText}`,
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
content: `确定要${actionText}模板: ${record.templateName}?`,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
maskClosable: false,
|
||||
onOk: async () => {
|
||||
try {
|
||||
// 模拟请求
|
||||
setTimeout(() => {
|
||||
message.success(`${actionText}成功`);
|
||||
fetchTemplateList(pagination.current, pagination.pageSize, searchParams);
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error(`${actionText}失败:`, error);
|
||||
message.error(`${actionText}失败`);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 获取状态标签
|
||||
const getStatusTag = (status: string) => {
|
||||
const color = TemplateStatusColor[status as keyof typeof TemplateStatusColor] || 'default';
|
||||
const text = TemplateStatusText[status as keyof typeof TemplateStatusText] || '未知状态';
|
||||
return <Tag color={color}>{text}</Tag>;
|
||||
};
|
||||
|
||||
// 处理表格分页变化
|
||||
const handleTableChange = (newPagination: TablePaginationConfig) => {
|
||||
fetchTemplateList(newPagination.current, newPagination.pageSize, searchParams);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '序号',
|
||||
render: (_: any, __: SupplierEvaluate.TemplateRecord, index: number) =>
|
||||
(pagination.current! - 1) * pagination.pageSize! + index + 1,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '模板名称',
|
||||
dataIndex: 'templateName',
|
||||
key: 'templateName',
|
||||
width: 200,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (templateName: string) => (
|
||||
<Tooltip placement="topLeft" title={templateName}>
|
||||
{templateName}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '品类',
|
||||
dataIndex: 'category',
|
||||
key: 'category',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '创建单位',
|
||||
dataIndex: 'createBy',
|
||||
key: 'createBy',
|
||||
width: 180,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (text: string) => (
|
||||
<Tooltip placement="topLeft" title={text}>
|
||||
{text}
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '创建部门',
|
||||
dataIndex: 'department',
|
||||
key: 'department',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (status: string) => getStatusTag(status),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
align: 'center' as const,
|
||||
render: (_: unknown, record: SupplierEvaluate.TemplateRecord) => (
|
||||
<Space size="middle">
|
||||
<Button type="link" onClick={() => handleEdit(record)}>
|
||||
编辑
|
||||
</Button>
|
||||
<Button type="link" onClick={() => handleView(record)}>
|
||||
查看
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
|
||||
setSelectedRowKeys(newSelectedRowKeys);
|
||||
};
|
||||
|
||||
const rowSelection = {
|
||||
selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
|
||||
const hasSelected = selectedRowKeys.length > 0;
|
||||
|
||||
// 处理添加
|
||||
const handleAdd = () => {
|
||||
setIsEdit(false);
|
||||
setIsViewMode(false);
|
||||
setCurrentId('');
|
||||
setViewData(null);
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
// 处理批量删除
|
||||
const handleBatchDelete = () => {
|
||||
Modal.confirm({
|
||||
title: '确认批量删除',
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
content: '确定要删除选中的模板吗?此操作不可恢复。',
|
||||
okText: '删除',
|
||||
okType: 'danger',
|
||||
cancelText: '取消',
|
||||
maskClosable: false,
|
||||
onOk: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟批量删除
|
||||
setTimeout(() => {
|
||||
setSelectedRowKeys([]);
|
||||
message.success('批量删除成功');
|
||||
fetchTemplateList(pagination.current, pagination.pageSize, searchParams);
|
||||
setLoading(false);
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.error('批量删除失败:', error);
|
||||
message.error('批量删除失败');
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (values: any) => {
|
||||
const { dateRange, ...rest } = values;
|
||||
const params: SupplierEvaluate.TemplateSearchParams = { ...rest };
|
||||
|
||||
if (dateRange && dateRange.length === 2) {
|
||||
params.dateRange = [dateRange[0].format('YYYY-MM-DD'), dateRange[1].format('YYYY-MM-DD')];
|
||||
}
|
||||
|
||||
fetchTemplateList(1, pagination.pageSize, params);
|
||||
};
|
||||
|
||||
// 处理模态框取消
|
||||
const handleModalCancel = () => {
|
||||
setModalVisible(false);
|
||||
setIsViewMode(false);
|
||||
setViewData(null);
|
||||
};
|
||||
|
||||
// 渲染模板详情
|
||||
const renderTemplateDetail = () => {
|
||||
if (!viewData) return null;
|
||||
|
||||
return (
|
||||
<div >
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span >模板名称:</span>
|
||||
<span>{viewData.templateName}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span >模板编号:</span>
|
||||
<span >{viewData.templateCode}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div>
|
||||
<span >模板类型:</span>
|
||||
<span >
|
||||
{TaskTypeText[viewData.templateType as keyof typeof TaskTypeText] || '未知类型'}
|
||||
</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span >品类:</span>
|
||||
<span >{viewData.category}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span>状态:</span>
|
||||
<span >{getStatusTag(viewData.status)}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span >创建单位:</span>
|
||||
<span >{viewData.createBy}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span >创建部门:</span>
|
||||
<span >{viewData.department}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span >创建时间:</span>
|
||||
<span >{viewData.createTime}</span>
|
||||
</div>
|
||||
</Col>
|
||||
{viewData.updateBy && (
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span >更新人:</span>
|
||||
<span >{viewData.updateBy}</span>
|
||||
</div>
|
||||
</Col>
|
||||
)}
|
||||
{viewData.updateTime && (
|
||||
<Col span={12}>
|
||||
<div >
|
||||
<span >更新时间:</span>
|
||||
<span >{viewData.updateTime}</span>
|
||||
</div>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染模板表单
|
||||
const renderTemplateForm = () => {
|
||||
return (
|
||||
<Form
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
initialValues={viewData || {}}
|
||||
>
|
||||
<Form.Item
|
||||
label="模板名称"
|
||||
name="templateName"
|
||||
rules={[{ required: true, message: '请输入模板名称' }]}
|
||||
>
|
||||
<Input placeholder="请输入模板名称" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="模板编号"
|
||||
name="templateCode"
|
||||
rules={[{ required: true, message: '请输入模板编号' }]}
|
||||
>
|
||||
<Input placeholder="请输入模板编号" disabled={isEdit} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="模板类型"
|
||||
name="templateType"
|
||||
rules={[{ required: true, message: '请选择模板类型' }]}
|
||||
>
|
||||
<Select placeholder="请选择模板类型">
|
||||
<Option value={TaskType.REGULAR}>{TaskTypeText[TaskType.REGULAR]}</Option>
|
||||
<Option value={TaskType.SPECIAL}>{TaskTypeText[TaskType.SPECIAL]}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="品类"
|
||||
name="category"
|
||||
rules={[{ required: true, message: '请选择品类' }]}
|
||||
>
|
||||
<Select placeholder="请选择品类">
|
||||
{categoryOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>{option.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="创建单位"
|
||||
name="createBy"
|
||||
rules={[{ required: true, message: '请选择创建单位' }]}
|
||||
>
|
||||
<Select placeholder="请选择创建单位">
|
||||
{companyOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>{option.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="创建部门"
|
||||
name="department"
|
||||
rules={[{ required: true, message: '请选择创建部门' }]}
|
||||
>
|
||||
<Select placeholder="请选择创建部门">
|
||||
{departmentOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>{option.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="模板状态"
|
||||
name="status"
|
||||
rules={[{ required: true, message: '请选择模板状态' }]}
|
||||
>
|
||||
<Select placeholder="请选择模板状态">
|
||||
<Option value={TemplateStatus.ENABLED}>{TemplateStatusText[TemplateStatus.ENABLED]}</Option>
|
||||
<Option value={TemplateStatus.DISABLED}>{TemplateStatusText[TemplateStatus.DISABLED]}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
保存
|
||||
</Button>
|
||||
<Button style={{ marginLeft: 8 }} onClick={handleModalCancel}>
|
||||
取消
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card title="供应商模板管理">
|
||||
<div>供应商评价模板管理模块</div>
|
||||
</Card>
|
||||
<div className={`common-container`}>
|
||||
<div className="filter-action-row">
|
||||
<Form
|
||||
form={form}
|
||||
name="search"
|
||||
onFinish={handleSearch}
|
||||
layout="inline"
|
||||
className="filter-form"
|
||||
>
|
||||
<Form.Item name="templateName" label="模板名称">
|
||||
<Input placeholder="请输入模板名称" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="createBy" label="创建单位">
|
||||
<Select placeholder="请选择创建单位" allowClear style={{ width: 200 }}>
|
||||
{companyOptions.map(option => (
|
||||
<Option key={option.value} value={option.value}>{option.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="category" label="品类">
|
||||
<Select placeholder="请选择品类" allowClear style={{ width: 150 }}>
|
||||
{categoryOptions.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 />} onClick={() => form.submit()}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => {
|
||||
form.resetFields();
|
||||
fetchTemplateList(1, pagination.pageSize, {});
|
||||
}}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div className="right-buttons">
|
||||
<Button type="primary" ghost icon={<PlusOutlined />} onClick={handleAdd}>
|
||||
新增
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={handleBatchDelete}
|
||||
disabled={!hasSelected}
|
||||
loading={loading}
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="content-area">
|
||||
<Table
|
||||
rowSelection={rowSelection}
|
||||
columns={columns}
|
||||
dataSource={templateData}
|
||||
pagination={pagination}
|
||||
loading={loading}
|
||||
onChange={handleTableChange}
|
||||
scroll={{ x: 1200 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 新增/编辑/查看模态框 */}
|
||||
<Modal
|
||||
title={isViewMode
|
||||
? "查看模板详情"
|
||||
: isEdit
|
||||
? "编辑模板"
|
||||
: "新增模板"}
|
||||
visible={modalVisible}
|
||||
onCancel={handleModalCancel}
|
||||
width={800}
|
||||
maskClosable={false}
|
||||
destroyOnClose
|
||||
footer={
|
||||
isViewMode
|
||||
? [
|
||||
<Button key="close" onClick={handleModalCancel}>
|
||||
关闭
|
||||
</Button>,
|
||||
]
|
||||
: null
|
||||
}
|
||||
>
|
||||
{isViewMode ? renderTemplateDetail() : renderTemplateForm()}
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
150
src/typings.d.ts
vendored
150
src/typings.d.ts
vendored
@ -43,3 +43,153 @@ declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
|
||||
|
||||
declare const REQUEST_BASE: string;
|
||||
declare const UPLOAD_URL: string;
|
||||
|
||||
declare namespace API {
|
||||
// ... 其他类型定义
|
||||
|
||||
// 通用响应类型
|
||||
type Result<T = any> = {
|
||||
code: number;
|
||||
msg: string;
|
||||
data: T;
|
||||
};
|
||||
|
||||
// 分页查询参数
|
||||
type PageParams = {
|
||||
current?: number;
|
||||
pageSize?: number;
|
||||
};
|
||||
|
||||
// 分页响应数据
|
||||
type PageResult<T = any> = {
|
||||
list: T[];
|
||||
pagination: {
|
||||
total: number;
|
||||
pageSize: number;
|
||||
current: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
declare namespace SupplierEvaluate {
|
||||
// 模板管理相关类型
|
||||
type TemplateRecord = {
|
||||
id: string;
|
||||
key: string;
|
||||
templateName: string;
|
||||
templateCode: string;
|
||||
templateType: string;
|
||||
category: string;
|
||||
status: string;
|
||||
createBy: string;
|
||||
department?: string;
|
||||
createTime: string;
|
||||
updateBy?: string;
|
||||
updateTime?: string;
|
||||
};
|
||||
|
||||
type TemplateSearchParams = {
|
||||
templateName?: string;
|
||||
status?: string;
|
||||
createBy?: string;
|
||||
category?: string;
|
||||
dateRange?: string[];
|
||||
};
|
||||
|
||||
// 任务管理相关类型
|
||||
type TaskRecord = {
|
||||
id: string;
|
||||
key: string;
|
||||
taskName: string;
|
||||
taskCode: string;
|
||||
taskType: string;
|
||||
templateName: string;
|
||||
status: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
createBy: string;
|
||||
createTime: string;
|
||||
updateBy?: string;
|
||||
updateTime?: string;
|
||||
};
|
||||
|
||||
type TaskSearchParams = {
|
||||
taskName?: string;
|
||||
status?: string;
|
||||
dateRange?: string[];
|
||||
};
|
||||
|
||||
// 评价结果相关类型
|
||||
type EvaluateResultRecord = {
|
||||
id: string;
|
||||
key: string;
|
||||
taskName: string;
|
||||
taskCode: string;
|
||||
taskType: string;
|
||||
status: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
createBy: string;
|
||||
createTime: string;
|
||||
updateBy?: string;
|
||||
updateTime?: string;
|
||||
supplierCount?: number;
|
||||
approvalStatus?: string;
|
||||
};
|
||||
|
||||
type EvaluateResultSearchParams = {
|
||||
taskName?: string;
|
||||
status?: string;
|
||||
approvalStatus?: string;
|
||||
dateRange?: string[];
|
||||
};
|
||||
|
||||
// 评价结果详情相关类型
|
||||
type EvaluateResultDetailRecord = {
|
||||
id: string;
|
||||
key: string;
|
||||
supplierName: string;
|
||||
category: string;
|
||||
score: number;
|
||||
level: string;
|
||||
};
|
||||
|
||||
type EvaluateResultDetailSearchParams = {
|
||||
supplierName?: string;
|
||||
level?: string;
|
||||
};
|
||||
|
||||
// 评价得分明细相关类型
|
||||
type ScoreDetailItem = {
|
||||
key: string;
|
||||
index: number;
|
||||
itemName: string;
|
||||
weight: number;
|
||||
score: number;
|
||||
weightedScore: number;
|
||||
remark?: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare module '*.css';
|
||||
declare module '*.less';
|
||||
declare module '*.png';
|
||||
declare module '*.jpg';
|
||||
declare module '*.jpeg';
|
||||
declare module '*.gif';
|
||||
declare module '*.bmp';
|
||||
declare module '*.tiff';
|
||||
declare module '*.svg' {
|
||||
export function ReactComponent(props: React.SVGProps<SVGSVGElement>): React.ReactElement;
|
||||
const url: string;
|
||||
export default url;
|
||||
}
|
||||
|
||||
declare module 'slash2';
|
||||
declare module '*.json';
|
||||
declare module 'react-copy-to-clipboard';
|
||||
declare module 'react-fittext';
|
||||
declare module '@antv/data-set';
|
||||
declare module 'nzh/cn';
|
||||
declare module 'webpack-theme-color-replacer';
|
||||
declare module 'webpack-theme-color-replacer/client';
|
||||
|
Reference in New Issue
Block a user