评价打分
This commit is contained in:
@ -5,5 +5,7 @@ export default defineConfig({
|
|||||||
define: {
|
define: {
|
||||||
UPLOAD_URL: '/upload',
|
UPLOAD_URL: '/upload',
|
||||||
REQUEST_BASE: '/api',
|
REQUEST_BASE: '/api',
|
||||||
|
// 下载文件时用到,直接window.open 拼上接口地址+参数
|
||||||
|
SERVER_BASE: 'http://10.0.0.10:18012',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -5,5 +5,7 @@ export default defineConfig({
|
|||||||
define:{
|
define:{
|
||||||
UPLOAD_URL: '/upload',
|
UPLOAD_URL: '/upload',
|
||||||
REQUEST_BASE: '/api',
|
REQUEST_BASE: '/api',
|
||||||
|
// 下载文件时用到,直接window.open 拼上接口地址+参数
|
||||||
|
SERVER_BASE: 'http://10.0.0.10:18012',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -122,6 +122,16 @@ export default [
|
|||||||
},
|
},
|
||||||
component: '@/pages/supplierEvaluateManage/supplierEvaluateScore/supplierEvaluateScore',
|
component: '@/pages/supplierEvaluateManage/supplierEvaluateScore/supplierEvaluateScore',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'supplierEvaluateScoreDetail',
|
||||||
|
path: 'supplierEvaluateScoreDetail',
|
||||||
|
meta: {
|
||||||
|
title: '评价打分详情',
|
||||||
|
hide: true,
|
||||||
|
icon: 'icon-liebiaomoshi',
|
||||||
|
},
|
||||||
|
component: '@/pages/supplierEvaluateManage/supplierEvaluateScore/supplierEvaluateScoreDetail',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'supplierEvaluateResult',
|
name: 'supplierEvaluateResult',
|
||||||
path: 'supplierEvaluateResult',
|
path: 'supplierEvaluateResult',
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
"dva": "2.4.1",
|
"dva": "2.4.1",
|
||||||
"echarts": "^5.2.2",
|
"echarts": "^5.2.2",
|
||||||
"echarts-for-react": "^3.0.2",
|
"echarts-for-react": "^3.0.2",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"omit.js": "2.0.2",
|
"omit.js": "2.0.2",
|
||||||
|
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
@ -59,6 +59,9 @@ dependencies:
|
|||||||
echarts-for-react:
|
echarts-for-react:
|
||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: 3.0.2(echarts@5.6.0)(react@16.14.0)
|
version: 3.0.2(echarts@5.6.0)(react@16.14.0)
|
||||||
|
file-saver:
|
||||||
|
specifier: ^2.0.5
|
||||||
|
version: 2.0.5
|
||||||
lodash:
|
lodash:
|
||||||
specifier: 4.17.21
|
specifier: 4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@ -7936,6 +7939,10 @@ packages:
|
|||||||
flat-cache: 3.2.0
|
flat-cache: 3.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/file-saver@2.0.5:
|
||||||
|
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/file-uri-to-path@1.0.0:
|
/file-uri-to-path@1.0.0:
|
||||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
@ -69,7 +69,7 @@ const ScoreEvaluationTable: React.FC<ScoreEvaluationTableProps> = ({
|
|||||||
// 将API数据转换为表格数据
|
// 将API数据转换为表格数据
|
||||||
const convertApiDataToTableData = (apiData: any[]): TableRowItem[] => {
|
const convertApiDataToTableData = (apiData: any[]): TableRowItem[] => {
|
||||||
// 检查数据是否已经是扁平化的表格数据格式
|
// 检查数据是否已经是扁平化的表格数据格式
|
||||||
if (apiData.length > 0 && 'subIndicator' in apiData[0]) {
|
if (apiData.length > 0 && 'subIndicator' in apiData[0] && !('indicatorNdList' in apiData[0])) {
|
||||||
return apiData as TableRowItem[];
|
return apiData as TableRowItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ const ScoreEvaluationTable: React.FC<ScoreEvaluationTableProps> = ({
|
|||||||
key: `${stItem.id || stIndex}-0`,
|
key: `${stItem.id || stIndex}-0`,
|
||||||
stId: stItem.id,
|
stId: stItem.id,
|
||||||
baseIndicator: stItem.baseIndicator || '',
|
baseIndicator: stItem.baseIndicator || '',
|
||||||
descIndicator: stItem.descIndicator || '',
|
descIndicator: stItem.indicatorDesc || '',
|
||||||
stScore: stItem.score || '0',
|
stScore: stItem.score || '0',
|
||||||
subIndicator: '',
|
subIndicator: '',
|
||||||
ndScore: '0',
|
ndScore: '0',
|
||||||
@ -94,19 +94,22 @@ const ScoreEvaluationTable: React.FC<ScoreEvaluationTableProps> = ({
|
|||||||
} else {
|
} else {
|
||||||
// 处理二级指标
|
// 处理二级指标
|
||||||
stItem.indicatorNdList.forEach((ndItem: any, ndIndex: number) => {
|
stItem.indicatorNdList.forEach((ndItem: any, ndIndex: number) => {
|
||||||
flattenedData.push({
|
// 确保ndItem是一个对象,而不是直接渲染
|
||||||
key: `${stItem.id || stIndex}-${ndItem.id || ndIndex}`,
|
if (typeof ndItem === 'object' && ndItem !== null) {
|
||||||
stId: stItem.id,
|
flattenedData.push({
|
||||||
ndId: ndItem.id,
|
key: `${stItem.id || stIndex}-${ndItem.id || ndIndex}`,
|
||||||
baseIndicator: stItem.baseIndicator || '',
|
stId: stItem.id,
|
||||||
descIndicator: stItem.descIndicator || '',
|
ndId: ndItem.id,
|
||||||
stScore: stItem.score || '0',
|
baseIndicator: stItem.baseIndicator || '',
|
||||||
subIndicator: ndItem.subIndicator || '',
|
descIndicator: stItem.indicatorDesc || '',
|
||||||
ndScore: ndItem.score || '0',
|
stScore: stItem.score || '0',
|
||||||
isStar: ndItem.isStar || '',
|
subIndicator: ndItem.subIndicator || '',
|
||||||
score: ndItem.actualScore || '',
|
ndScore: ndItem.subScore || '0',
|
||||||
remark: ndItem.remark || '',
|
isStar: ndItem.starIndicator || '',
|
||||||
});
|
score: ndItem.scoreNum || '',
|
||||||
|
remark: ndItem.remark || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -138,15 +141,15 @@ const ScoreEvaluationTable: React.FC<ScoreEvaluationTableProps> = ({
|
|||||||
return {
|
return {
|
||||||
id: firstItem.stId || `temp-st-${stIndex}`,
|
id: firstItem.stId || `temp-st-${stIndex}`,
|
||||||
baseIndicator: firstItem.baseIndicator || '',
|
baseIndicator: firstItem.baseIndicator || '',
|
||||||
descIndicator: firstItem.descIndicator || '',
|
indicatorDesc: firstItem.descIndicator || '',
|
||||||
score: firstItem.stScore || '0',
|
score: firstItem.stScore || '0',
|
||||||
indicatorNdList: level1Items.map((item, ndIndex) => {
|
indicatorNdList: level1Items.map((item, ndIndex) => {
|
||||||
return {
|
return {
|
||||||
id: item.ndId || `temp-nd-${stIndex}-${ndIndex}`,
|
id: item.ndId || `temp-nd-${stIndex}-${ndIndex}`,
|
||||||
subIndicator: item.subIndicator || '',
|
subIndicator: item.subIndicator || '',
|
||||||
score: item.ndScore || '0',
|
subScore: item.ndScore || '0',
|
||||||
isStar: item.isStar || '',
|
starIndicator: item.isStar || '0',
|
||||||
actualScore: item.score || '',
|
scoreNum: item.score || '',
|
||||||
remark: item.remark || ''
|
remark: item.remark || ''
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -44,14 +44,7 @@ const SupplierEvaluateResultInfo: React.FC = () => {
|
|||||||
);
|
);
|
||||||
const [evaluateRules, setEvaluateRules] = useState<API.EvaluateRuleItem[]>([]);
|
const [evaluateRules, setEvaluateRules] = useState<API.EvaluateRuleItem[]>([]);
|
||||||
|
|
||||||
// 品类数据
|
|
||||||
const categoryOptions = [
|
|
||||||
{ label: '食品', value: '食品' },
|
|
||||||
{ label: '电子', value: '电子' },
|
|
||||||
{ label: '机械', value: '机械' },
|
|
||||||
{ label: '化工', value: '化工' },
|
|
||||||
{ label: '医药', value: '医药' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// 获取上级页面传递的数据
|
// 获取上级页面传递的数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
.titleSection {
|
.titleSection {
|
||||||
flex: 1;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.pageTitle {
|
.pageTitle {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@ -15,7 +16,41 @@
|
|||||||
|
|
||||||
.actionSection {
|
.actionSection {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.submitButtonContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.filter-action-row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.filter-form {
|
||||||
|
.filter-btns {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-area {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add styles for the tables */
|
||||||
|
:global(.ant-table) {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add styles for the tabs */
|
||||||
|
:global(.ant-tabs-nav) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
@ -1,203 +1,453 @@
|
|||||||
// 供应商评价打分
|
// 供应商评价打分
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Button, Card, Form, message, Typography, Spin, Space } from 'antd';
|
import {
|
||||||
import { ArrowLeftOutlined, SaveOutlined } from '@ant-design/icons';
|
Button,
|
||||||
import { history, useLocation } from 'umi';
|
Form,
|
||||||
import ScoreEvaluationTable from '@/components/ScoreEvaluationTable';
|
message,
|
||||||
|
Typography,
|
||||||
|
Space,
|
||||||
|
Tabs,
|
||||||
|
Input,
|
||||||
|
DatePicker,
|
||||||
|
Select,
|
||||||
|
Table,
|
||||||
|
} from 'antd';
|
||||||
|
import { SearchOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||||
|
import { history } from 'umi';
|
||||||
import styles from './supplierEvaluateScore.less';
|
import styles from './supplierEvaluateScore.less';
|
||||||
import { getSupplierScoreDetail, submitEvaluateScore } from '@/servers/api/supplierEvaluate';
|
import {
|
||||||
|
getSupplierDimension,
|
||||||
|
getTaskPage,
|
||||||
|
} from '@/servers/api/supplierEvaluate';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
const SupplierEvaluateScore: React.FC = () => {
|
const SupplierEvaluateScore: React.FC = () => {
|
||||||
const [form] = Form.useForm();
|
const [filterForm] = Form.useForm();
|
||||||
const location = useLocation<{
|
// 新增状态
|
||||||
record: API.EvaluateSupplierRecord;
|
const [activeTab, setActiveTab] = useState<string>('supplier');
|
||||||
parentRecord: API.EvaluateTaskRecord;
|
const [supplierTableData, setSupplierTableData] = useState<
|
||||||
}>();
|
supplierEvaluateScore.SupplierDimensionRecord[]
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
>([]);
|
||||||
const [submitting, setSubmitting] = useState<boolean>(false);
|
const [taskTableData, setTaskTableData] = useState<supplierEvaluateScore.TaskPageRecord[]>([]);
|
||||||
const [supplierRecord, setSupplierRecord] = useState<API.EvaluateSupplierRecord | null>(null);
|
const [supplierTableLoading, setSupplierTableLoading] = useState<boolean>(false);
|
||||||
const [parentRecord, setParentRecord] = useState<API.EvaluateTaskRecord | null>(null);
|
const [taskTableLoading, setTaskTableLoading] = useState<boolean>(false);
|
||||||
const [scoreDetail, setScoreDetail] = useState<API.EvaluateScoreDetailData | null>(null);
|
const [pagination, setPagination] = useState({
|
||||||
const [scoreData, setScoreData] = useState<any[]>([]);
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
// 获取上级页面传递的数据
|
// 构建查询参数
|
||||||
useEffect(() => {
|
const buildQueryParams = () => {
|
||||||
if (location.state?.record) {
|
const values = filterForm.getFieldsValue();
|
||||||
setSupplierRecord(location.state.record);
|
|
||||||
}
|
|
||||||
if (location.state?.parentRecord) {
|
|
||||||
setParentRecord(location.state.parentRecord);
|
|
||||||
}
|
|
||||||
}, [location]);
|
|
||||||
|
|
||||||
// 将API数据转换为ScoreEvaluationTable组件所需的格式
|
if (activeTab === 'supplier') {
|
||||||
const formatDataForScoreTable = (data: API.EvaluateScoreDetailData | null) => {
|
const params: supplierEvaluateScore.SupplierDimensionRequest = {
|
||||||
if (!data?.taskIndicatorVo) return [];
|
basePageRequest: {
|
||||||
|
pageNo: pagination.current,
|
||||||
return data.taskIndicatorVo.map(indicator => {
|
pageSize: pagination.pageSize,
|
||||||
return {
|
},
|
||||||
baseIndicator: indicator.baseIndicator,
|
keyword: values.keyword || undefined,
|
||||||
descIndicator: indicator.indicatorDesc,
|
status: values.status || undefined,
|
||||||
score: indicator.score,
|
|
||||||
indicatorNdList: indicator.subIndicator?.map(subItem => {
|
|
||||||
return {
|
|
||||||
subIndicator: subItem.subIndicator,
|
|
||||||
score: subItem.subScore,
|
|
||||||
isStar: subItem.starIndicator,
|
|
||||||
id: subItem.id,
|
|
||||||
actualScore: subItem.scoreNum || '',
|
|
||||||
remark: subItem.remark || ''
|
|
||||||
};
|
|
||||||
}) || []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理时间范围
|
||||||
|
if (values.evaluationTime && values.evaluationTime.length === 2) {
|
||||||
|
params.startTime = values.evaluationTime[0].format('YYYY-MM-DD');
|
||||||
|
params.endTime = values.evaluationTime[1].format('YYYY-MM-DD');
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
} else {
|
||||||
|
const params: supplierEvaluateScore.TaskPageRequest = {
|
||||||
|
basePageRequest: {
|
||||||
|
pageNo: pagination.current,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
},
|
||||||
|
keyword: values.keyword || undefined,
|
||||||
|
status: values.status || undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理时间范围
|
||||||
|
if (values.evaluationTime && values.evaluationTime.length === 2) {
|
||||||
|
params.startTime = values.evaluationTime[0].format('YYYY-MM-DD');
|
||||||
|
params.endTime = values.evaluationTime[1].format('YYYY-MM-DD');
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取供应商维度列表
|
||||||
|
const fetchSupplierDimensionList = async (params: any) => {
|
||||||
|
setSupplierTableLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await getSupplierDimension(params);
|
||||||
|
|
||||||
|
if (response.success && response.data) {
|
||||||
|
setSupplierTableData(response.data.records || []);
|
||||||
|
setPagination({
|
||||||
|
...pagination,
|
||||||
|
current: response.data.current || 1,
|
||||||
|
total: response.data.total || 0,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error(response.message || '获取供应商列表失败');
|
||||||
|
setSupplierTableData([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取供应商列表失败:', error);
|
||||||
|
message.error('获取供应商列表失败');
|
||||||
|
setSupplierTableData([]);
|
||||||
|
} finally {
|
||||||
|
setSupplierTableLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取任务列表
|
||||||
|
const fetchTaskList = async (params: any) => {
|
||||||
|
setTaskTableLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await getTaskPage(params);
|
||||||
|
|
||||||
|
if (response.success && response.data) {
|
||||||
|
setTaskTableData(response.data.records || []);
|
||||||
|
setPagination({
|
||||||
|
...pagination,
|
||||||
|
current: response.data.current || 1,
|
||||||
|
total: response.data.total || 0,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error(response.message || '获取任务列表失败');
|
||||||
|
setTaskTableData([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取任务列表失败:', error);
|
||||||
|
message.error('获取任务列表失败');
|
||||||
|
setTaskTableData([]);
|
||||||
|
} finally {
|
||||||
|
setTaskTableLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理搜索按钮点击
|
||||||
|
const handleSearch = async () => {
|
||||||
|
const params = buildQueryParams();
|
||||||
|
|
||||||
|
if (activeTab === 'supplier') {
|
||||||
|
fetchSupplierDimensionList(params);
|
||||||
|
} else {
|
||||||
|
fetchTaskList(params);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化加载数据
|
||||||
|
useEffect(() => {
|
||||||
|
handleSearch();
|
||||||
|
}, [activeTab]);
|
||||||
|
|
||||||
|
// 处理标签页切换
|
||||||
|
const handleTabChange = (key: string) => {
|
||||||
|
setActiveTab(key);
|
||||||
|
setPagination({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
filterForm.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理重置按钮点击
|
||||||
|
const handleReset = () => {
|
||||||
|
filterForm.resetFields();
|
||||||
|
setPagination({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
handleSearch();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理分页变化
|
||||||
|
const handleTableChange = (page: any) => {
|
||||||
|
setPagination({
|
||||||
|
...pagination,
|
||||||
|
current: page.current,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const params = buildQueryParams();
|
||||||
|
params.basePageRequest.pageNo = page.current;
|
||||||
|
params.basePageRequest.pageSize = page.pageSize;
|
||||||
|
|
||||||
|
if (activeTab === 'supplier') {
|
||||||
|
fetchSupplierDimensionList(params);
|
||||||
|
} else {
|
||||||
|
fetchTaskList(params);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到评分页面
|
||||||
|
const goToScoring = (record: any, recordType: 'supplier' | 'task', mode: 'view' | 'score') => {
|
||||||
|
// 构建路由状态参数
|
||||||
|
const state: {
|
||||||
|
record: any;
|
||||||
|
mode: 'view' | 'score';
|
||||||
|
recordType: 'supplier' | 'task';
|
||||||
|
} = {
|
||||||
|
record: record,
|
||||||
|
mode,
|
||||||
|
recordType: recordType,
|
||||||
|
};
|
||||||
|
if (recordType === 'task') {
|
||||||
|
state.record.evaluateTaskId = record.id;
|
||||||
|
history.push({
|
||||||
|
pathname: '/supplierEvaluate/supplierEvaluateResultInfo',
|
||||||
|
state: {record},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
history.push({
|
||||||
|
pathname: '/supplierEvaluate/supplierEvaluateScoreDetail',
|
||||||
|
state: state,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取得分明细数据
|
// 导出功能
|
||||||
const fetchScoreDetail = async () => {
|
const handleExport = async (record: any) => {
|
||||||
if (!supplierRecord?.id) {
|
if (!record?.id) {
|
||||||
message.error('缺少必要参数,无法获取数据');
|
message.error('缺少必要参数,无法导出');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
try {
|
||||||
const response = await getSupplierScoreDetail(supplierRecord.id);
|
window.open(
|
||||||
|
`${SERVER_BASE}/coscoEvaluate/supplier/export?evaluateTaskId=${record.id}`,
|
||||||
if (response.success && response.data) {
|
'_blank',
|
||||||
setScoreDetail(response.data);
|
'noopener,noreferrer',
|
||||||
|
);
|
||||||
// 转换数据格式
|
|
||||||
const formattedData = formatDataForScoreTable(response.data);
|
|
||||||
setScoreData(formattedData);
|
|
||||||
} else {
|
|
||||||
message.error(response.message || '获取评价得分明细失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取评价得分明细失败:', error);
|
console.error('导出失败:', error);
|
||||||
message.error('获取评价得分明细失败');
|
message.error('导出失败,请稍后再试');
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 当供应商记录加载完成后,获取得分明细
|
// 供应商Tab的表格列
|
||||||
useEffect(() => {
|
const supplierColumns = [
|
||||||
if (supplierRecord?.id) {
|
{
|
||||||
fetchScoreDetail();
|
title: '序号',
|
||||||
}
|
dataIndex: 'index',
|
||||||
}, [supplierRecord]);
|
key: 'index',
|
||||||
|
width: 80,
|
||||||
|
render: (_: any, __: any, index: number) =>
|
||||||
|
(pagination.current - 1) * pagination.pageSize + index + 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '供应商名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '评价主题',
|
||||||
|
dataIndex: 'evaluateTheme',
|
||||||
|
key: 'evaluateTheme',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '发起单位',
|
||||||
|
dataIndex: 'tenantName',
|
||||||
|
key: 'tenantName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '评价开始时间',
|
||||||
|
dataIndex: 'startTime',
|
||||||
|
key: 'startTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '评价结束时间',
|
||||||
|
dataIndex: 'endTime',
|
||||||
|
key: 'endTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '评价状态',
|
||||||
|
dataIndex: 'statusName',
|
||||||
|
key: 'statusName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '提交状态',
|
||||||
|
dataIndex: 'submissionStatus',
|
||||||
|
key: 'submissionStatus',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '提交时间',
|
||||||
|
dataIndex: 'submissionTime',
|
||||||
|
key: 'submissionTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
render: (text: string, record: any) => (
|
||||||
|
<Space>
|
||||||
|
<Button type="link" onClick={() => goToScoring(record, 'supplier', 'view')}>
|
||||||
|
查看
|
||||||
|
</Button>
|
||||||
|
{record.status === '1' && ( // 只有待评分状态才显示打分按钮
|
||||||
|
<Button type="link" onClick={() => goToScoring(record, 'supplier', 'score')}>
|
||||||
|
打分
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
// 返回上一页
|
// 评价任务Tab的表格列
|
||||||
const handleBack = () => {
|
const taskColumns = [
|
||||||
history.goBack();
|
{
|
||||||
};
|
title: '序号',
|
||||||
|
dataIndex: 'index',
|
||||||
|
key: 'index',
|
||||||
|
width: 80,
|
||||||
|
render: (_: any, __: any, index: number) =>
|
||||||
|
(pagination.current - 1) * pagination.pageSize + index + 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '评价主题',
|
||||||
|
dataIndex: 'evaluateTheme',
|
||||||
|
key: 'evaluateTheme',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '发起单位',
|
||||||
|
dataIndex: 'deptName',
|
||||||
|
key: 'deptName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '评价开始时间',
|
||||||
|
dataIndex: 'startTime',
|
||||||
|
key: 'startTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '评价结束时间',
|
||||||
|
dataIndex: 'endTime',
|
||||||
|
key: 'endTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '评价状态',
|
||||||
|
dataIndex: 'statusName',
|
||||||
|
key: 'statusName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
render: (text: string, record: any) => (
|
||||||
|
<Space>
|
||||||
|
<Button type="link" onClick={() => goToScoring(record, 'task', 'view')}>
|
||||||
|
查看
|
||||||
|
</Button>
|
||||||
|
<Button type="link" onClick={() => handleExport(record)}>
|
||||||
|
导出
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
// 处理评分数据变更
|
// 筛选表单
|
||||||
const handleScoreDataChange = (newData: any[]) => {
|
const renderFilterForm = () => (
|
||||||
setScoreData(newData);
|
<div className="filter-action-row">
|
||||||
};
|
<div className="filter-form">
|
||||||
|
<Form form={filterForm} layout="inline">
|
||||||
|
<Form.Item name="keyword" label="关键字">
|
||||||
|
<Input
|
||||||
|
placeholder={
|
||||||
|
activeTab === 'supplier' ? '请输入供应商名称或评价主题' : '请输入评价主题'
|
||||||
|
}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
// 提交评分
|
<Form.Item name="evaluationTime" label="评价时间">
|
||||||
const handleSubmit = async () => {
|
<RangePicker />
|
||||||
if (!supplierRecord?.id) {
|
</Form.Item>
|
||||||
message.error('缺少必要参数,无法提交');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证所有二级指标是否都已评分
|
<Form.Item name="status" label="评价状态">
|
||||||
const hasEmptyScore = scoreData.some(item =>
|
<Select placeholder="请选择" style={{ width: 150 }} allowClear>
|
||||||
item.indicatorNdList.some((subItem: any) =>
|
<Option value="1">待评分</Option>
|
||||||
!subItem.actualScore && subItem.actualScore !== 0
|
<Option value="2">已评分</Option>
|
||||||
)
|
<Option value="3">进行中</Option>
|
||||||
);
|
<Option value="4">已完成</Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
if (hasEmptyScore) {
|
<Form.Item className="filter-btns">
|
||||||
message.warning('请为所有指标填写评分');
|
<Space>
|
||||||
return;
|
<Button type="primary" onClick={handleSearch}>
|
||||||
}
|
<SearchOutlined /> 查询
|
||||||
|
</Button>
|
||||||
setSubmitting(true);
|
<Button onClick={handleReset} type="primary" danger>
|
||||||
try {
|
<DeleteOutlined /> 重置
|
||||||
// 构建提交数据
|
</Button>
|
||||||
const submitData = {
|
</Space>
|
||||||
id: supplierRecord.id,
|
</Form.Item>
|
||||||
taskIndicatorVo: scoreData.map(item => ({
|
</Form>
|
||||||
baseIndicator: item.baseIndicator,
|
</div>
|
||||||
indicatorDesc: item.descIndicator,
|
</div>
|
||||||
score: item.score,
|
);
|
||||||
subIndicator: item.indicatorNdList.map((subItem: any) => ({
|
|
||||||
id: subItem.id,
|
|
||||||
subIndicator: subItem.subIndicator,
|
|
||||||
subScore: subItem.score,
|
|
||||||
starIndicator: subItem.isStar,
|
|
||||||
scoreNum: subItem.actualScore,
|
|
||||||
remark: subItem.remark
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await submitEvaluateScore(submitData);
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
message.success('评分提交成功');
|
|
||||||
// 提交成功后返回列表页
|
|
||||||
history.goBack();
|
|
||||||
} else {
|
|
||||||
message.error(response.message || '评分提交失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('评分提交失败:', error);
|
|
||||||
message.error('评分提交失败');
|
|
||||||
} finally {
|
|
||||||
setSubmitting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="common-container">
|
<div className="common-container">
|
||||||
<div className={styles.headerRow}>
|
<div className={styles.headerRow}>
|
||||||
<div className={styles.titleSection}>
|
<div className={styles.titleSection}>
|
||||||
<Title level={4} className={styles.pageTitle}>
|
<Title level={4} className={styles.pageTitle}>
|
||||||
{supplierRecord?.supplierName || '供应商'} - 评价打分
|
供应商评价打分
|
||||||
</Title>
|
</Title>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.actionSection}>
|
|
||||||
<Space>
|
|
||||||
<Button type="link" icon={<ArrowLeftOutlined />} onClick={handleBack}>
|
|
||||||
返回
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
icon={<SaveOutlined />}
|
|
||||||
onClick={handleSubmit}
|
|
||||||
loading={submitting}
|
|
||||||
>
|
|
||||||
提交
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 筛选条件区域 */}
|
||||||
|
{renderFilterForm()}
|
||||||
|
|
||||||
<div className="content-area">
|
<div className="content-area">
|
||||||
<Card bordered={false}>
|
<Tabs activeKey={activeTab} onChange={handleTabChange}>
|
||||||
{loading ? (
|
<TabPane tab="按供应商" key="supplier">
|
||||||
<div className="loading-container" style={{ textAlign: 'center', padding: '50px' }}>
|
<Table
|
||||||
<Spin tip="加载中..." />
|
columns={supplierColumns}
|
||||||
</div>
|
dataSource={supplierTableData}
|
||||||
) : (
|
rowKey="id"
|
||||||
<Form form={form} layout="vertical">
|
loading={supplierTableLoading}
|
||||||
<ScoreEvaluationTable
|
pagination={{
|
||||||
value={scoreData}
|
current: pagination.current,
|
||||||
onChange={handleScoreDataChange}
|
pageSize: pagination.pageSize,
|
||||||
isDetail={false}
|
total: pagination.total,
|
||||||
loading={loading}
|
showSizeChanger: true,
|
||||||
/>
|
showQuickJumper: true,
|
||||||
</Form>
|
showTotal: (total) => `共 ${total} 条`,
|
||||||
)}
|
}}
|
||||||
</Card>
|
onChange={handleTableChange}
|
||||||
|
/>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tab="按评价任务" key="task">
|
||||||
|
<Table
|
||||||
|
columns={taskColumns}
|
||||||
|
dataSource={taskTableData}
|
||||||
|
rowKey="id"
|
||||||
|
loading={taskTableLoading}
|
||||||
|
pagination={{
|
||||||
|
current: pagination.current,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
total: pagination.total,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (total) => `共 ${total} 条`,
|
||||||
|
}}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
/>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,302 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { history, useLocation } from 'umi';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Descriptions,
|
||||||
|
Divider,
|
||||||
|
Spin,
|
||||||
|
message,
|
||||||
|
Typography,
|
||||||
|
Empty,
|
||||||
|
Space,
|
||||||
|
Form,
|
||||||
|
Modal,
|
||||||
|
} from 'antd';
|
||||||
|
import { ArrowLeftOutlined, SaveOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { getSupplierScoreDetail, saveEvaluateScore } from '@/servers/api/supplierEvaluate';
|
||||||
|
import ScoreEvaluationTable from '@/components/ScoreEvaluationTable';
|
||||||
|
import styles from './supplierEvaluateScore.less';
|
||||||
|
|
||||||
|
const { Title } = Typography;
|
||||||
|
const { confirm } = Modal;
|
||||||
|
|
||||||
|
const SupplierEvaluateScoreDetail: React.FC = () => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const location = useLocation<{
|
||||||
|
record: supplierEvaluateScore.SupplierDimensionRecord | supplierEvaluateScore.TaskPageRecord;
|
||||||
|
mode?: 'view' | 'score'; // 查看模式或打分模式
|
||||||
|
recordType: 'supplier' | 'task'; // 记录类型:供应商或任务
|
||||||
|
}>();
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [submitting, setSubmitting] = useState<boolean>(false);
|
||||||
|
const [saving, setSaving] = useState<boolean>(false);
|
||||||
|
const [scoreDetail, setScoreDetail] = useState<supplierEvaluateScore.ScoreDetailData | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
const [scoreData, setScoreData] = useState<any[]>([]);
|
||||||
|
const [record, setRecord] = useState<any>(null);
|
||||||
|
const [mode, setMode] = useState<'view' | 'score'>('view'); // 默认为查看模式
|
||||||
|
const [recordType, setRecordType] = useState<'supplier' | 'task'>('supplier'); // 默认为供应商类型
|
||||||
|
|
||||||
|
// 从路由获取传递的记录
|
||||||
|
useEffect(() => {
|
||||||
|
if (location.state?.record) {
|
||||||
|
setRecord(location.state.record);
|
||||||
|
|
||||||
|
// 设置记录类型
|
||||||
|
if (location.state.recordType) {
|
||||||
|
setRecordType(location.state.recordType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置模式
|
||||||
|
if (location.state.mode) {
|
||||||
|
setMode(location.state.mode);
|
||||||
|
} else {
|
||||||
|
// 如果没有传递mode,则根据状态判断
|
||||||
|
setMode(location.state.record.status === '1' ? 'score' : 'view');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.error('缺少必要参数,无法获取详情');
|
||||||
|
history.goBack();
|
||||||
|
}
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
|
// 将API数据转换为ScoreEvaluationTable组件所需的格式
|
||||||
|
const formatDataForScoreTable = (data: supplierEvaluateScore.ScoreDetailData) => {
|
||||||
|
if (!data?.taskIndicatorVo) return [];
|
||||||
|
|
||||||
|
return data.taskIndicatorVo.map((indicator) => {
|
||||||
|
return {
|
||||||
|
baseIndicator: indicator.baseIndicator,
|
||||||
|
indicatorDesc: indicator.indicatorDesc,
|
||||||
|
score: indicator.score,
|
||||||
|
// 为ScoreEvaluationTable组件添加额外字段
|
||||||
|
indicatorNdList:
|
||||||
|
indicator.subIndicator
|
||||||
|
?.map((subItem) => {
|
||||||
|
// 确保subItem是一个对象,而不是直接渲染
|
||||||
|
if (typeof subItem !== 'object' || subItem === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: subItem.id,
|
||||||
|
subIndicator: subItem.subIndicator,
|
||||||
|
score: subItem.subScore,
|
||||||
|
isStar: subItem.starIndicator,
|
||||||
|
actualScore: subItem.scoreNum || '',
|
||||||
|
remark: subItem.remark || '',
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean) || [],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取评价打分详情
|
||||||
|
const fetchScoreDetail = async () => {
|
||||||
|
if (!record?.id) {
|
||||||
|
message.error('缺少必要参数,无法获取数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await getSupplierScoreDetail(record.id);
|
||||||
|
|
||||||
|
if (response.success && response.data) {
|
||||||
|
setScoreDetail(response.data);
|
||||||
|
|
||||||
|
// 转换数据格式
|
||||||
|
const formattedData = formatDataForScoreTable(response.data);
|
||||||
|
setScoreData(formattedData);
|
||||||
|
} else {
|
||||||
|
message.error(response.message || '获取评价得分明细失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取评价得分明细失败:', error);
|
||||||
|
message.error('获取评价得分明细失败');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 当记录加载完成后,获取得分明细
|
||||||
|
useEffect(() => {
|
||||||
|
if (record?.id) {
|
||||||
|
fetchScoreDetail();
|
||||||
|
}
|
||||||
|
}, [record]);
|
||||||
|
|
||||||
|
// 返回上一页
|
||||||
|
const handleBack = () => {
|
||||||
|
history.goBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理评分数据变更
|
||||||
|
const handleScoreDataChange = (newData: any[]) => {
|
||||||
|
setScoreData(newData);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交评分
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!record?.id) {
|
||||||
|
message.error('缺少必要参数,无法提交');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证所有二级指标是否都已评分
|
||||||
|
const hasEmptyScore = scoreData.some((item) =>
|
||||||
|
item.indicatorNdList.some((subItem: any) => !subItem.scoreNum && subItem.scoreNum !== 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasEmptyScore) {
|
||||||
|
message.warning('请为所有指标填写评分');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示确认对话框
|
||||||
|
confirm({
|
||||||
|
title: '提交确认',
|
||||||
|
icon: <ExclamationCircleOutlined />,
|
||||||
|
content: '评分提交后将不可修改,确定要提交吗?',
|
||||||
|
okText: '确定',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: async () => {
|
||||||
|
setSubmitting(true);
|
||||||
|
try {
|
||||||
|
// 构建提交数据
|
||||||
|
const submitData: supplierEvaluateScore.ScoreSaveRequest = {
|
||||||
|
id: record.id,
|
||||||
|
scoreVoList: scoreData.flatMap((item) => {
|
||||||
|
return item.indicatorNdList.map((subItem: any) => ({
|
||||||
|
id: item.id,
|
||||||
|
remark: subItem.remark || item.remark || '',
|
||||||
|
score: String(subItem.scoreNum || item.score || ''),
|
||||||
|
}));
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const response = await saveEvaluateScore(submitData);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
message.success('评分保存成功');
|
||||||
|
history.goBack();
|
||||||
|
} else {
|
||||||
|
message.error(response.message || '评分保存失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('评分提交失败:', error);
|
||||||
|
message.error('评分提交失败');
|
||||||
|
} finally {
|
||||||
|
setSubmitting(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取评价状态文本
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
const statusMap: { [key: string]: string } = {
|
||||||
|
'1': '待评分',
|
||||||
|
'2': '已评分',
|
||||||
|
'3': '进行中',
|
||||||
|
'4': '已完成',
|
||||||
|
};
|
||||||
|
return statusMap[status] || '未知状态';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取供应商名称
|
||||||
|
const getSupplierName = () => {
|
||||||
|
if (recordType === 'supplier') {
|
||||||
|
return record?.supplierName || record?.name || scoreDetail?.name || '-';
|
||||||
|
} else {
|
||||||
|
return scoreDetail?.name || '-';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取评价主题
|
||||||
|
const getEvaluateTheme = () => {
|
||||||
|
return record?.evaluateTheme || '-';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取发起单位
|
||||||
|
const getUnitName = () => {
|
||||||
|
return record?.tenantName || record?.deptName || '-';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 判断是否可以编辑(只有打分模式才可编辑)
|
||||||
|
const canEdit = mode === 'score';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="common-container">
|
||||||
|
<div className={styles.headerRow}>
|
||||||
|
<div className={styles.titleSection}>
|
||||||
|
<Title level={4} className={styles.pageTitle}>
|
||||||
|
{getSupplierName()} - 评价打分
|
||||||
|
</Title>
|
||||||
|
</div>
|
||||||
|
<div className={styles.actionSection}>
|
||||||
|
<Button type="link" icon={<ArrowLeftOutlined />} onClick={handleBack}>
|
||||||
|
返回
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
{scoreDetail ? (
|
||||||
|
<div className="content-area">
|
||||||
|
<Card title="基本信息" bordered={false}>
|
||||||
|
<Descriptions column={2} bordered>
|
||||||
|
<Descriptions.Item label="供应商名称">{getSupplierName()}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="品类">{scoreDetail.category || '-'}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="评价主题">{getEvaluateTheme()}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="评价状态">
|
||||||
|
{getStatusText(record?.status || '') || '-'}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="评价开始时间">
|
||||||
|
{record?.startTime || '-'}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="评价结束时间">{record?.endTime || '-'}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="发起单位">{getUnitName()}</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Card title="评价打分" bordered={false}>
|
||||||
|
{scoreData.length > 0 ? (
|
||||||
|
<ScoreEvaluationTable
|
||||||
|
value={scoreData}
|
||||||
|
onChange={handleScoreDataChange}
|
||||||
|
isDetail={!canEdit} // 如果不可编辑,则以只读方式显示
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Empty description="暂无评分数据" />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 只在打分模式下显示提交按钮,并移至底部 */}
|
||||||
|
{canEdit && (
|
||||||
|
<div className={styles.submitButtonContainer}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<SaveOutlined />}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
loading={submitting}
|
||||||
|
style={{ marginTop: 24 }}
|
||||||
|
>
|
||||||
|
提交评分
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
!loading && <Empty description="暂无评分数据" />
|
||||||
|
)}
|
||||||
|
</Spin>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SupplierEvaluateScoreDetail;
|
@ -21,7 +21,7 @@ import type {
|
|||||||
EvaluateRuleUpdateRequest,
|
EvaluateRuleUpdateRequest,
|
||||||
TaskAddRequest,
|
TaskAddRequest,
|
||||||
} from '@/servers/dao/supplierEvaluateTask';
|
} from '@/servers/dao/supplierEvaluateTask';
|
||||||
|
import '@/servers/dao/supplierEvaluateScore.d.ts';
|
||||||
/**
|
/**
|
||||||
* 获取所有模板列表
|
* 获取所有模板列表
|
||||||
* @returns 所有模板列表
|
* @returns 所有模板列表
|
||||||
@ -291,19 +291,19 @@ export async function deleteEvaluateRule(id: string) {
|
|||||||
* @returns Promise
|
* @returns Promise
|
||||||
*/
|
*/
|
||||||
export async function getSupplierScoreDetail(id: string) {
|
export async function getSupplierScoreDetail(id: string) {
|
||||||
return request<API.APIResponse<EvaluateScoreDetailData>>(`/coscoEvaluate/supplier/getScoreResult`, {
|
return request<supplierEvaluateScore.ScoreDetailResponse>(`/coscoEvaluate/user/getIndicator`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: { id }
|
params: { id }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交供应商评价得分
|
* 保存评分数据
|
||||||
* @param params 评价得分数据
|
* @param params 保存评分参数
|
||||||
* @returns
|
* @returns Promise
|
||||||
*/
|
*/
|
||||||
export async function submitEvaluateScore(params: any) {
|
export async function saveEvaluateScore(params: supplierEvaluateScore.ScoreSaveRequest) {
|
||||||
return request('/api/supplier/evaluate/score/submit', {
|
return request<supplierEvaluateScore.ScoreSaveResponse>(`/coscoEvaluate/user/getScore`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
data: params,
|
||||||
});
|
});
|
||||||
@ -320,3 +320,42 @@ export async function getIndicator(id: string) {
|
|||||||
params: { id },
|
params: { id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取供应商维度列表
|
||||||
|
* @param params 查询参数
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
export async function getSupplierDimension(params: supplierEvaluateScore.SupplierDimensionRequest) {
|
||||||
|
return request<supplierEvaluateScore.SupplierDimensionResponse>(`/coscoEvaluate/supplier/getSupplierDimension`, {
|
||||||
|
method: 'POST',
|
||||||
|
data: params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取任务列表
|
||||||
|
* @param params 查询参数
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
export async function getTaskPage(params: supplierEvaluateScore.TaskPageRequest) {
|
||||||
|
return request<supplierEvaluateScore.TaskPageResponse>(`/coscoEvaluate/task/getTaskPage`, {
|
||||||
|
method: 'POST',
|
||||||
|
data: params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出供应商评价数据
|
||||||
|
* @param evaluateTaskId 评价任务ID
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
export async function exportSupplierEvaluate(evaluateTaskId: string) {
|
||||||
|
const params: supplierEvaluateScore.ExportRequest = { evaluateTaskId };
|
||||||
|
return request(`/coscoEvaluate/supplier/export`, {
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
responseType: 'arrayBuffer',
|
||||||
|
getResponse: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
205
src/servers/dao/supplierEvaluateScore.d.ts
vendored
Normal file
205
src/servers/dao/supplierEvaluateScore.d.ts
vendored
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
// 评价打分模块类型声明
|
||||||
|
|
||||||
|
declare namespace supplierEvaluateScore {
|
||||||
|
// 基础分页请求参数
|
||||||
|
interface BasePageRequest {
|
||||||
|
pageNo: number;
|
||||||
|
pageSize: number;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评价打分请求参数
|
||||||
|
interface ScorePageRequest {
|
||||||
|
basePageRequest: BasePageRequest;
|
||||||
|
keyword?: string;
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
status?: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按供应商维度查询参数
|
||||||
|
interface SupplierDimensionRequest {
|
||||||
|
basePageRequest: BasePageRequest;
|
||||||
|
keyword?: string; // 供应商名称或评价主题关键字
|
||||||
|
startTime?: string; // 评价开始时间
|
||||||
|
endTime?: string; // 评价结束时间
|
||||||
|
status?: string; // 评价状态:1-待评分,2-已评分,3-进行中,4-已完成
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按任务查询参数
|
||||||
|
interface TaskPageRequest {
|
||||||
|
basePageRequest: BasePageRequest;
|
||||||
|
keyword?: string; // 评价主题关键字
|
||||||
|
startTime?: string; // 评价开始时间
|
||||||
|
endTime?: string; // 评价结束时间
|
||||||
|
status?: string; // 评价状态:1-待评分,2-已评分,3-进行中,4-已完成
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 供应商维度列表返回数据类型
|
||||||
|
interface SupplierDimensionResponse {
|
||||||
|
code: number;
|
||||||
|
data: SupplierDimensionData;
|
||||||
|
message: string;
|
||||||
|
success: boolean;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SupplierDimensionData {
|
||||||
|
countId: null;
|
||||||
|
current: number;
|
||||||
|
hitCount: boolean;
|
||||||
|
maxLimit: null;
|
||||||
|
optimizeCountSql: boolean;
|
||||||
|
orders: any[];
|
||||||
|
pages: number;
|
||||||
|
records: SupplierDimensionRecord[];
|
||||||
|
searchCount: boolean;
|
||||||
|
size: number;
|
||||||
|
total: number;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SupplierDimensionRecord {
|
||||||
|
basePageRequest?: null;
|
||||||
|
endTime?: string;
|
||||||
|
evaluateTheme?: string;
|
||||||
|
id?: string;
|
||||||
|
startTime?: string;
|
||||||
|
status?: string; // 1-待评分,2-已评分,3-进行中,4-已完成
|
||||||
|
statusName?: string;
|
||||||
|
tenantName?: string; // 发起单位
|
||||||
|
userId?: null;
|
||||||
|
supplierName?: string;
|
||||||
|
submissionStatus?: string; // 提交状态
|
||||||
|
submissionTime?: string; // 提交时间
|
||||||
|
name?: string; // 供应商名称,与 supplierName 同义
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评价任务列表返回数据类型
|
||||||
|
interface TaskPageResponse {
|
||||||
|
code: number;
|
||||||
|
data: TaskPageData;
|
||||||
|
message: string;
|
||||||
|
success: boolean;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TaskPageData {
|
||||||
|
countId: null;
|
||||||
|
current: number;
|
||||||
|
hitCount: boolean;
|
||||||
|
maxLimit: null;
|
||||||
|
optimizeCountSql: boolean;
|
||||||
|
orders: any[];
|
||||||
|
pages: number;
|
||||||
|
records: TaskPageRecord[];
|
||||||
|
searchCount: boolean;
|
||||||
|
size: number;
|
||||||
|
total: number;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TaskPageRecord {
|
||||||
|
basePageRequest?: null;
|
||||||
|
endTime?: string;
|
||||||
|
evaluateTheme?: string;
|
||||||
|
id?: string;
|
||||||
|
startTime?: string;
|
||||||
|
status?: string; // 1-待评分,2-已评分,3-进行中,4-已完成
|
||||||
|
statusName?: string;
|
||||||
|
deptName?: string; // 发起单位/部门
|
||||||
|
tenantName?: string; // 发起单位,与 deptName 同义
|
||||||
|
userId?: null;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评价打分明细请求参数
|
||||||
|
interface ScoreDetailRequest {
|
||||||
|
id: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评价打分明细返回数据类型
|
||||||
|
interface ScoreDetailResponse {
|
||||||
|
code: number;
|
||||||
|
data: ScoreDetailData;
|
||||||
|
message: string;
|
||||||
|
success: boolean;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评价明细数据
|
||||||
|
interface ScoreDetailData {
|
||||||
|
category: string;
|
||||||
|
name: string;
|
||||||
|
taskIndicatorVo: TaskIndicatorVo[];
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评价指标项
|
||||||
|
interface TaskIndicatorVo {
|
||||||
|
baseIndicator: string;
|
||||||
|
indicatorDesc: string;
|
||||||
|
score: string;
|
||||||
|
subIndicator: SubIndicator[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评价子指标项
|
||||||
|
interface SubIndicator {
|
||||||
|
id: string;
|
||||||
|
remark: string | null;
|
||||||
|
scoreNum: string | number | null;
|
||||||
|
starIndicator: string;
|
||||||
|
stId: string;
|
||||||
|
subIndicator: string;
|
||||||
|
subScore: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打分保存请求参数
|
||||||
|
interface ScoreSaveRequest {
|
||||||
|
id: string;
|
||||||
|
scoreVoList: ScoreVoList[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打分项
|
||||||
|
interface ScoreVoList {
|
||||||
|
id?: string;
|
||||||
|
remark?: string;
|
||||||
|
score?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打分保存响应
|
||||||
|
interface ScoreSaveResponse {
|
||||||
|
code: number;
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data: any;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交评分请求参数
|
||||||
|
interface ScoreSubmitRequest {
|
||||||
|
id: string;
|
||||||
|
taskIndicatorVo: TaskIndicatorVo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交评分响应
|
||||||
|
interface ScoreSubmitResponse {
|
||||||
|
code: number;
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data: any;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出评价数据请求参数
|
||||||
|
interface ExportRequest {
|
||||||
|
evaluateTaskId: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
}
|
5
src/typings.d.ts
vendored
5
src/typings.d.ts
vendored
@ -11,7 +11,9 @@ declare module '*.gif';
|
|||||||
declare module '*.bmp';
|
declare module '*.bmp';
|
||||||
declare module '*.tiff';
|
declare module '*.tiff';
|
||||||
declare module 'omit.js';
|
declare module 'omit.js';
|
||||||
|
declare module 'file-saver' {
|
||||||
|
export function saveAs(data: Blob | File | string, filename?: string, options?: object): void;
|
||||||
|
}
|
||||||
// google analytics interface
|
// google analytics interface
|
||||||
interface GAFieldsObject {
|
interface GAFieldsObject {
|
||||||
eventCategory: string;
|
eventCategory: string;
|
||||||
@ -43,6 +45,7 @@ declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
|
|||||||
|
|
||||||
declare const REQUEST_BASE: string;
|
declare const REQUEST_BASE: string;
|
||||||
declare const UPLOAD_URL: string;
|
declare const UPLOAD_URL: string;
|
||||||
|
declare const SERVER_BASE: string;
|
||||||
|
|
||||||
declare namespace API {
|
declare namespace API {
|
||||||
// ... 其他类型定义
|
// ... 其他类型定义
|
||||||
|
58
src/utils/download.ts
Normal file
58
src/utils/download.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* 通用文件下载工具(基于 umi-request + file-saver)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { extend } from 'umi-request';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
|
// 单独配置一个不拦截响应的 request 实例
|
||||||
|
const downloadRequest = extend({
|
||||||
|
timeout: 10000,
|
||||||
|
responseType: 'blob', // 关键
|
||||||
|
credentials: 'include', // 根据实际需要携带 cookie
|
||||||
|
prefix: REQUEST_BASE,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件通用函数
|
||||||
|
* @param url 请求地址
|
||||||
|
* @param params 请求参数(可选)
|
||||||
|
* @param filename 下载后的文件名(可选)
|
||||||
|
* @param method 请求方法(默认 POST)
|
||||||
|
*/
|
||||||
|
export async function downloadFile({
|
||||||
|
url,
|
||||||
|
params,
|
||||||
|
filename,
|
||||||
|
method = 'POST',
|
||||||
|
}: {
|
||||||
|
url: string;
|
||||||
|
params?: Record<string, any>;
|
||||||
|
filename?: string;
|
||||||
|
method?: 'GET' | 'POST';
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const blob: Blob = await downloadRequest(url, {
|
||||||
|
method,
|
||||||
|
data: method === 'POST' ? params : undefined,
|
||||||
|
params: method === 'GET' ? params : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 尝试从响应头中获取文件名
|
||||||
|
const contentDisposition = (blob as any).response?.headers?.get?.('content-disposition');
|
||||||
|
let finalFilename = filename;
|
||||||
|
|
||||||
|
if (!finalFilename && contentDisposition) {
|
||||||
|
const match = contentDisposition.match(/filename="?([^"]+)"?/);
|
||||||
|
if (match && match[1]) {
|
||||||
|
finalFilename = decodeURIComponent(match[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finalFilename = finalFilename || '下载文件';
|
||||||
|
|
||||||
|
saveAs(blob, finalFilename);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('文件下载失败:', error);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user