考试统计页面

main
sunhonglei 5 months ago
parent 17e162eed9
commit 2f0bf16bd6
  1. 19
      packages/examination/src/api/exam-online/index.tsx
  2. 4
      packages/examination/src/components/contentMain/index.js
  3. 243
      packages/examination/src/views/exam-online/compoents/ExamDetailAnalysisPage.tsx
  4. 214
      packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisPage.tsx
  5. 3
      packages/examination/src/views/exam-online/exam-detail-analysis.tsx
  6. 3
      packages/examination/src/views/exam-online/exam-paper-analysis.tsx

@ -90,3 +90,22 @@ export const examStatisticsChange = async (id:string) => {
});
return response.data;
};
export const getScoreDistribution = async (id:string) => {
const response = await axios.get('/ex/exam-statistics/getScoreDistribution',{
params:{id:id}
});
return response.data;
};
export const getExamDetailAnalysisPageList = async (obj:any) => {
const response = await axios.get('/ex/exam-statistics/getExamDetailAnalysisPageList',{
params:obj
});
return response.data;
};
export const getPaperAnalysisData = async () => {
const response = await axios.get('/ex/exam-statistics/getPaperAnalysisData');
return response.data;
};

@ -34,7 +34,7 @@ import QuestionUp from 'views/question/questionUp';
import ExamPaperList from 'views/examPaper/examPaperList';
import ExamDetailAnalysis from "../../views/exam-online/exam-detail-analysis";
import ExamPaperAnalysis from "../../views/exam-online/exam-paper-analysis";
import ExamPaperAdd from 'views/examPaper/examPaperAdd';
// import ExamPaperAdd from 'views/examPaper/examPaperAdd';
class ContentMain extends Component {
@ -64,7 +64,7 @@ class ContentMain extends Component {
<Route exact path='/questionAdd' component={ QuestionAdd }/>
<Route exact path='/questionUp' component={ QuestionUp }/>
<Route exact path='/examPaperList' component={ ExamPaperList }/>
<Route exact path='/examPaperAdd' component={ ExamPaperAdd }/>
{/*<Route exact path='/examPaperAdd' component={ ExamPaperAdd }/>*/}
<Redirect exact from='/' to='/home'/>
</Switch>
</div>

@ -0,0 +1,243 @@
import React, { useState, useEffect } from 'react';
import { Row, Col, Input, Select, Button, Table, Space, Cascader, Modal } from 'antd';
import ReactECharts from 'echarts-for-react';
import {getExamDetailAnalysisPageList, getIndustryList, getScoreDistribution} from 'api/exam-online/index';
import { convertToCascaderData } from "./ExamEditPage";
const { Option } = Select;
// 定义表格数据项的类型
type ExamDataItem = {
key: string;
examId: string;
examName: string;
paperName: string;
examArea: string;
industry: string;
participants: number;
averageTime: number;
passRate: string;
};
const ExamDetailAnalysisPage: React.FC = () => {
const [examName, setExamName] = useState('');
const [paperName, setPaperName] = useState('');
const [examArea, setExamArea] = useState('');
const [industry, setIndustry] = useState('');
const [industryOptions, setIndustryOptions] = useState<{ value: string; label: string }[]>([]);
const [tableData, setTableData] = useState<ExamDataItem[]>([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: tableData.length,
});
const [visible, setVisible] = useState(false);
const [scoreDistributionData, setScoreDistributionData] = useState<{ value: number; name: string }[]>([]);
useEffect(() => {
const fetchData = async () => {
try {
const industryResponse = await getIndustryList();
setIndustryOptions(industryResponse.map((item: any) => ({ value: item.industry_id, label: item.industry_name })));
} catch (error) {
console.error('数据获取失败:', error);
}
};
fetchData();
}, []);
// 将省/市格式的数据转换为数组
const convertRegionToArr = (region: string) => {
return region ? region.split('/') : [];
};
// 修改后的 convertArrToRegion 函数
const convertArrToRegion = (regionArr: (string | number)[]) => {
return regionArr.map(value => String(value)).join('/');
};
const handleReset = () => {
setExamName('');
setPaperName('');
setExamArea('');
setIndustry('');
handleSearch();
};
const handleSearch = () => {
const param = {
examName:examName,
paperName:paperName,
industry:industry,
examArea:examArea,
}
const fetchData = async () => {
try {
const resultData = await getExamDetailAnalysisPageList(param)
setTableData(resultData);
setPagination({
...pagination,
current: 1,
total: resultData.length,
});
} catch (error) {
console.error('数据获取失败:', error);
}
};
fetchData();
};
const handleTableChange = (newPagination: any) => {
setPagination(newPagination);
};
const handleShowScoreDistribution = async (examId: string) => {
try {
const response = await getScoreDistribution(examId);
setScoreDistributionData(response);
setVisible(true);
} catch (error) {
console.error('获取成绩分布数据失败:', error);
}
};
const columns = [
{
title: '序号',
dataIndex: 'key',
render: (text: any, record: ExamDataItem, index: number) => (pagination.current - 1) * pagination.pageSize + index + 1,
},
{
title: '考试名称',
dataIndex: 'examName',
},
{
title: '试卷名称',
dataIndex: 'paperName',
},
{
title: '考试区域',
dataIndex: 'examArea',
},
{
title: '监管行业',
dataIndex: 'industry',
},
{
title: '参与人数',
dataIndex: 'participants',
},
{
title: '平均用时(分钟)',
dataIndex: 'averageTime',
},
{
title: '合格率',
dataIndex: 'passRate',
},
{
title: '查看',
dataIndex: 'view',
// 指定 record 的类型为 ExamDataItem
render: (text: any, record: ExamDataItem) => <a onClick={() => handleShowScoreDistribution(record.examId)}></a>,
},
];
const getPieChartOption = (data: { value: number; name: string }[]) => ({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)',
},
legend: {
orient: 'vertical',
right: 10,
top: 'center',
data: data.map(item => item.name),
},
series: [
{
name: '成绩分布',
type: 'pie',
radius: '50%',
center: ['40%', '50%'],
data,
},
],
});
return (
<div style={{ padding: 20 }}>
{/* 检索条件行 */}
<Row gutter={16}>
<Col span={5}>
<span style={{ marginRight: 8 }}>:</span>
<Input
placeholder="考试名称"
value={examName}
onChange={(e) => setExamName(e.target.value)}
style={{ width: 'calc(100% - 100px)' }} // 调整输入框宽度
/>
</Col>
<Col span={5}>
<span style={{ marginRight: 8 }}>:</span>
<Input
placeholder="试卷名称"
value={paperName}
onChange={(e) => setPaperName(e.target.value)}
style={{ width: 'calc(100% - 100px)' }} // 调整输入框宽度
/>
</Col>
<Col span={5}>
<span style={{ marginRight: 8 }}>:</span>
<Cascader
options={convertToCascaderData()}
placeholder="请选择考试区域"
onChange={(values) => setExamArea(convertArrToRegion(values))}
style={{ width: 'calc(100% - 100px)' }} // 调整级联选择器宽度
/>
</Col>
<Col span={5}>
<span style={{ marginRight: 8 }}>:</span>
<Select
placeholder="监管行业"
value={industry}
onChange={(value) => setIndustry(value)}
style={{ width: 150 }} // 调整下拉框宽度
>
<Option key="" value=""></Option>
{industryOptions.map((option) => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
))}
</Select>
</Col>
<Col span={4}>
<Space>
<Button onClick={handleReset}></Button>
<Button type="primary" onClick={handleSearch}></Button>
</Space>
</Col>
</Row>
{/* 表格 */}
<Table
columns={columns}
dataSource={tableData}
pagination={pagination}
onChange={handleTableChange}
style={{ marginTop: 20 }}
/>
<Modal
title="成绩分布"
visible={visible}
onCancel={() => setVisible(false)}
footer={null}
width={600}
>
<ReactECharts option={getPieChartOption(scoreDistributionData)} />
</Modal>
</div>
);
};
export default ExamDetailAnalysisPage;

@ -0,0 +1,214 @@
import React, { useState, useEffect } from 'react';
import { Row, Col, Input, Select, Button, Table, Space } from 'antd';
import { getIndustryList, getPaperAnalysisData } from 'api/exam-online/index'; // 假设的接口导入
const { Option } = Select;
// 定义表格数据项的类型
type PaperAnalysisDataItem = {
key: string;
paperId: string;
paperName: string;
industry: string;
useCount: number;
participantCount: number;
accuracyRate: string;
};
// 模拟数据
const mockData: PaperAnalysisDataItem[] = [
{
key: '1',
paperId: 'paper1',
paperName: '试卷1',
industry: '行业1',
useCount: 20,
participantCount: 100,
accuracyRate: '80%',
},
{
key: '2',
paperId: 'paper2',
paperName: '试卷2',
industry: '行业2',
useCount: 30,
participantCount: 150,
accuracyRate: '90%',
},
// 可以添加更多模拟数据
];
const ExamPaperAnalysisPage: React.FC = () => {
const [paperName, setPaperName] = useState('');
const [industry, setIndustry] = useState('');
const [industryOptions, setIndustryOptions] = useState<{ value: string; label: string }[]>([]);
const [tableData, setTableData] = useState(mockData);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: mockData.length,
});
const [totalCount, setTotalCount] = useState(0);
const [usedCount, setUsedCount] = useState(0);
const [disabledCount, setDisabledCount] = useState(0);
useEffect(() => {
const fetchData = async () => {
try {
const industryResponse = await getIndustryList();
setIndustryOptions(industryResponse.map((item: any) => ({ value: item.industry_id, label: item.industry_name })));
const paperAnalysisResponse = await getPaperAnalysisData();
setTotalCount(paperAnalysisResponse.totalCount);
setUsedCount(paperAnalysisResponse.usedCount);
setDisabledCount(paperAnalysisResponse.disabledCount);
} catch (error) {
console.error('数据获取失败:', error);
}
};
fetchData();
}, []);
const handleReset = () => {
setPaperName('');
setIndustry('');
setTableData(mockData);
setPagination({
...pagination,
current: 1,
});
};
const handleSearch = () => {
// 这里可以添加实际的筛选逻辑
const filteredData = mockData.filter((item) => {
return (
item.paperName.includes(paperName) &&
(industry === '' || item.industry === industry)
);
});
setTableData(filteredData);
setPagination({
...pagination,
current: 1,
total: filteredData.length,
});
};
const handleTableChange = (newPagination: any) => {
setPagination(newPagination);
};
const columns = [
{
title: '序号',
dataIndex: 'key',
render: (text: any, record: PaperAnalysisDataItem, index: number) => (pagination.current - 1) * pagination.pageSize + index + 1,
},
{
title: '试卷名称',
dataIndex: 'paperName',
},
{
title: '监管行业',
dataIndex: 'industry',
},
{
title: '使用次数',
dataIndex: 'useCount',
},
{
title: '参与人数',
dataIndex: 'participantCount',
},
{
title: '准确率',
dataIndex: 'accuracyRate',
},
{
title: '查看',
dataIndex: 'view',
render: (text:any, record: PaperAnalysisDataItem) => <a href={`/paper-detail/${record.paperId}`}></a>,
},
{
title: 'paperId',
dataIndex: 'paperId',
visible: false, // 隐藏列
},
];
return (
<div style={{ padding: 20 }}>
{/* 检索条件行 */}
<Row gutter={16}>
<Col span={6}>
<span style={{ marginRight: 8 }}>:</span>
<Input
placeholder="试卷名称"
value={paperName}
onChange={(e) => setPaperName(e.target.value)}
style={{ width: 'calc(100% - 100px)' }}
/>
</Col>
<Col span={6}>
<span style={{ marginRight: 8 }}>:</span>
<Select
placeholder="监管行业"
value={industry}
onChange={(value) => setIndustry(value)}
style={{ width: 150 }}
>
<Option key="" value=""></Option>
{industryOptions.map((option) => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
))}
</Select>
</Col>
<Col span={4}>
<Space>
<Button onClick={handleReset}></Button>
<Button type="primary" onClick={handleSearch}></Button>
</Space>
</Col>
</Row>
{/* 试卷数量行 */}
<Row gutter={16} style={{ marginTop: 20 }}>
<Col span={6}>
<div style={{ backgroundColor: '#DCE4F8', padding: 10, textAlign: 'center' }}>
<span style={{ color: 'black' }}></span>
<span style={{ color: '#86A3E7' }}>{totalCount}</span>
<span style={{ color: 'black' }}></span>
</div>
</Col>
<Col span={6}>
<div style={{ backgroundColor: '#DCE4F8', padding: 10, textAlign: 'center' }}>
<span style={{ color: 'black' }}>使</span>
<span style={{ color: '#86A3E7' }}>{usedCount}</span>
<span style={{ color: 'black' }}></span>
</div>
</Col>
<Col span={6}>
<div style={{ backgroundColor: '#DCE4F8', padding: 10, textAlign: 'center' }}>
<span style={{ color: 'black' }}></span>
<span style={{ color: '#86A3E7' }}>{disabledCount}</span>
<span style={{ color: 'black' }}></span>
</div>
</Col>
</Row>
{/* 表格 */}
<div style={{ marginTop: 20 }}>
<h3>使</h3>
<Table
columns={columns}
dataSource={tableData}
pagination={pagination}
onChange={handleTableChange}
/>
</div>
</div>
);
};
export default ExamPaperAnalysisPage;

@ -1,5 +1,6 @@
import React, { Component } from "react";
import ExamEditPage from "./compoents/ExamEditPage";
import ExamDetailAnalysisPage from "./compoents/ExamDetailAnalysisPage";
interface States {
resData: any
visible: boolean
@ -25,7 +26,7 @@ class ExamDetailAnalysis extends Component<any, States> {
render() {
return (
<div className="container">
<ExamEditPage isEdit={false}/>
<ExamDetailAnalysisPage/>
</div>
)
}

@ -1,5 +1,6 @@
import React, { Component } from "react";
import ExamEditPage from "./compoents/ExamEditPage";
import ExamPaperAnalysisPage from "./compoents/ExamPaperAnalysisPage";
interface States {
resData: any
visible: boolean
@ -25,7 +26,7 @@ class ExamPaperAnalysis extends Component<any, States> {
render() {
return (
<div className="container">
<ExamEditPage isEdit={false}/>
<ExamPaperAnalysisPage/>
</div>
)
}

Loading…
Cancel
Save