考试统计页面

main
sunhonglei 4 months ago
parent 4adf262a8a
commit c3318b1259
  1. 33
      packages/examination/src/api/exam-online/index.tsx
  2. 2
      packages/examination/src/components/contentMain/index.js
  3. 2
      packages/examination/src/views/exam-online/compoents/ExamDetailAnalysisPage.tsx
  4. 144
      packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisDetailPage.tsx
  5. 91
      packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisPage.tsx
  6. 76
      packages/examination/src/views/exam-online/compoents/QuestionComponent.tsx
  7. 57
      packages/examination/src/views/exam-online/exam_paper_analysis_detail.tsx

@ -109,3 +109,36 @@ export const getPaperAnalysisData = async () => {
const response = await axios.get('/ex/exam-statistics/getPaperAnalysisData');
return response.data;
};
export const getExamPaperAnalysisPageList = async (obj:any) => {
const response = await axios.get('/ex/exam-statistics/getExamPaperAnalysisPageList',{
params:obj
});
return response.data;
};
// api.ts
// 模拟获取试卷名称的接口
export const getPaperName = async (id:string|null) => {
const response = await axios.get('/ex/exam-statistics/getPaperName',{
params: {id:id}
});
return response.data;
};
// 模拟获取题目准确率数据的接口
export const getQuestionAccuracyData = async (id:string|null) => {
const response = await axios.get('/ex/exam-statistics/getQuestionAccuracyData',{
params: {id:id}
});
return response.data;
};
// 模拟获取题目准确率数据的接口
export const getQuestions = async (id:string|null) => {
// 这里可以替换为实际的 API 请求
const response = await axios.get('/ex/exam-statistics/getQuestions',{
params: {id:id}
});
return response.data;
};

@ -35,6 +35,7 @@ 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 ExamPaperAnalysisDetail from "../../views/exam-online/exam_paper_analysis_detail";
class ContentMain extends Component {
@ -60,6 +61,7 @@ class ContentMain extends Component {
<Route exact path='/exam-statistics' component={ ExamStatistics }/>
<Route exact path='/exam-detail-analysis' component={ ExamDetailAnalysis }/>
<Route exact path='/exam-paper-analysis' component={ ExamPaperAnalysis }/>
<Route exact path='/exam_paper_analysis_detail' component={ ExamPaperAnalysisDetail }/>
<Route exact path='/questionList' component={ QuestionList }/>
<Route exact path='/questionAdd' component={ QuestionAdd }/>
<Route exact path='/questionUp' component={ QuestionUp }/>

@ -3,6 +3,7 @@ import { Row, Col, Input, Select, Button, Table, Space, Cascader, Modal } from '
import ReactECharts from 'echarts-for-react';
import {getExamDetailAnalysisPageList, getIndustryList, getScoreDistribution} from 'api/exam-online/index';
import { convertToCascaderData } from "./ExamEditPage";
import ESBreadcrumbComponent from "./ESBreadcrumbComponent";
const { Option } = Select;
@ -166,6 +167,7 @@ const ExamDetailAnalysisPage: React.FC = () => {
return (
<div style={{ padding: 20 }}>
<ESBreadcrumbComponent url="/exam-statistics" previousText={"考试统计"} currentText={"考试详情"} />
{/* 检索条件行 */}
<Row gutter={16}>
<Col span={5}>

@ -0,0 +1,144 @@
import React, { useState, useEffect } from 'react';
import {Row, Col, Typography, Button} from 'antd';
import ReactECharts from 'echarts-for-react';
import QuestionComponent from './QuestionComponent';
import { getPaperName, getQuestionAccuracyData, getQuestions } from 'api/exam-online/index';
import { withRouter, RouteComponentProps } from 'react-router-dom'; // 引入 withRouter 和 RouteComponentProps
import { SingleQuestionProps } from './QuestionComponent';
import ESBreadcrumbComponent from "./ESBreadcrumbComponent";
const { Title } = Typography;
// 接口返回的数据结构,包含准确率和数量(这里数量每个都是 1)
interface QuestionAccuracyItem {
accuracy: number;
count: number;
}
interface PaperData {
paperName: string;
questionAccuracyData: QuestionAccuracyItem[];
}
const ExamPaperAnalysisDetailPage: React.FC<RouteComponentProps> = ({history}) => {
const [paperData, setPaperData] = useState<PaperData | null>(null);
const [tableData, setTableData] = useState<SingleQuestionProps[]>([]);
useEffect(() => {
const fetchData = async () => {
try {
const id = sessionStorage.getItem("paperId");
sessionStorage.removeItem("paperId");
const paperName = await getPaperName(id);
const questionAccuracyData = await getQuestionAccuracyData(id);
setPaperData({ paperName, questionAccuracyData });
const questions = await getQuestions(id);
setTableData(questions);
} catch (error) {
console.error('数据获取失败:', error);
}
};
fetchData();
}, []);
const handleGoBack = () => {
history.push("/exam-paper-analysis");
};
const getBarChartOption = (data: QuestionAccuracyItem[]) => {
// 初始化每个区间的题目数量
const countGroupedData = [0, 0, 0, 0];
let totalCount = 0;
// 统计每个区间的题目数量和总数量
data.forEach(item => {
if (item.accuracy < 0.1) {
countGroupedData[0] += item.count;
} else if (item.accuracy >= 0.1 && item.accuracy < 0.6) {
countGroupedData[1] += item.count;
} else if (item.accuracy >= 0.6 && item.accuracy < 0.8) {
countGroupedData[2] += item.count;
} else if (item.accuracy >= 0.8) {
countGroupedData[3] += item.count;
}
totalCount += item.count;
});
// 计算每个区间的百分比
const groupedData = countGroupedData.map(count => totalCount > 0 ? count / totalCount : 0);
return {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
// 修改 formatter 函数,将数值转换为百分比形式
formatter: (params: any) => {
const xAxisValue = params[0].name;
const yAxisValue = (params[0].value * 100).toFixed(2) + '%';
return `${xAxisValue}<br/>试题数量占比: ${yAxisValue}`;
},
},
xAxis: {
type: 'category',
data: ['<10%', '10%-60%', '60%-80%', '80%-100%'],
},
yAxis: {
type: 'value',
min: 0,
max: 1,
interval: 0.2,
axisLabel: {
formatter: (value: number) => `${value * 100}%`,
},
name: '试题数量占比',
nameLocation: 'top',
nameRotate: 0, // 让名称水平显示
nameGap: 0, // 调整名称与坐标轴的间距
nameTextStyle: {
padding: [0, 40, 100, 0] // 给名称上方增加一些内边距,使其更靠上
}
},
series: [
{
data: groupedData,
type: 'bar',
},
],
};
};
return (
<div style={{ padding: 20 }}>
<ESBreadcrumbComponent url="/exam-statistics" previousText={"考试统计"} currentText={"试卷分析"} />
{paperData && (
<>
{/* 第一行:试卷名称 */}
<Row gutter={16}>
<Col span={24}>
<Title level={3}>{paperData.paperName}</Title>
<Button onClick={()=>handleGoBack()}></Button>
</Col>
</Row>
{/* 第二行:QuestionComponent 标签 */}
<Row gutter={16} style={{ marginTop: 20 }}>
<Col span={24}>
<QuestionComponent questions={tableData} />
</Col>
</Row>
{/* 第三行:柱状图 */}
<Row gutter={16} style={{ marginTop: 20 }}>
<Col span={24}>
<h1></h1>
<ReactECharts option={getBarChartOption(paperData.questionAccuracyData)} />
</Col>
</Row>
</>
)}
</div>
);
};
export default withRouter(ExamPaperAnalysisDetailPage);

@ -1,11 +1,17 @@
import React, { useState, useEffect } from 'react';
import { Row, Col, Input, Select, Button, Table, Space } from 'antd';
import { getIndustryList, getPaperAnalysisData } from 'api/exam-online/index'; // 假设的接口导入
import { withRouter, RouteComponentProps } from 'react-router-dom'; // 引入 withRouter 和 RouteComponentProps
import {
getExamPaperAnalysisPageList,
getIndustryList,
getPaperAnalysisData, getScoreDistribution
} from 'api/exam-online/index';
import ESBreadcrumbComponent from "./ESBreadcrumbComponent"; // 假设的接口导入
const { Option } = Select;
// 定义表格数据项的类型
type PaperAnalysisDataItem = {
export type PaperAnalysisDataItem = {
key: string;
paperId: string;
paperName: string;
@ -15,38 +21,15 @@ type PaperAnalysisDataItem = {
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 ExamPaperAnalysisPage: React.FC<RouteComponentProps> = ({history}) => {
const [paperName, setPaperName] = useState('');
const [industry, setIndustry] = useState('');
const [industryOptions, setIndustryOptions] = useState<{ value: string; label: string }[]>([]);
const [tableData, setTableData] = useState(mockData);
const [tableData, setTableData] = useState<PaperAnalysisDataItem[]>([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: mockData.length,
total: tableData.length,
});
const [totalCount, setTotalCount] = useState(0);
const [usedCount, setUsedCount] = useState(0);
@ -72,27 +55,42 @@ const ExamPaperAnalysisPage: React.FC = () => {
const handleReset = () => {
setPaperName('');
setIndustry('');
setTableData(mockData);
setTableData(tableData);
setPagination({
...pagination,
current: 1,
});
};
const handleShowScoreDistribution = async (paperId: string) => {
try {
// const response = await getScoreDistribution(paperId);
sessionStorage.setItem("paperId",paperId)
history.push("/exam_paper_analysis_detail");
} catch (error) {
console.error('获取成绩分布数据失败:', error);
}
};
const handleSearch = () => {
// 这里可以添加实际的筛选逻辑
const filteredData = mockData.filter((item) => {
return (
item.paperName.includes(paperName) &&
(industry === '' || item.industry === industry)
);
});
setTableData(filteredData);
const param = {
paperName:paperName,
industry:industry,
}
const fetchData = async () => {
try {
const resultData = await getExamPaperAnalysisPageList(param)
setTableData(resultData);
setPagination({
...pagination,
current: 1,
total: filteredData.length,
total: resultData.length,
});
} catch (error) {
console.error('数据获取失败:', error);
}
};
fetchData();
};
const handleTableChange = (newPagination: any) => {
@ -124,21 +122,24 @@ const ExamPaperAnalysisPage: React.FC = () => {
{
title: '准确率',
dataIndex: 'accuracyRate',
render: (text:any, record: PaperAnalysisDataItem) =>{
if(record.accuracyRate){
return record.accuracyRate+"%";
}else{
return "";
}
}
},
{
title: '查看',
dataIndex: 'view',
render: (text:any, record: PaperAnalysisDataItem) => <a href={`/paper-detail/${record.paperId}`}></a>,
},
{
title: 'paperId',
dataIndex: 'paperId',
visible: false, // 隐藏列
render: (text:any, record: PaperAnalysisDataItem) => <a onClick={()=>{handleShowScoreDistribution(record.paperId)}}></a>,
},
];
return (
<div style={{ padding: 20 }}>
<ESBreadcrumbComponent url="/exam-statistics" previousText={"考试统计"} currentText={"试卷分析"} />
{/* 检索条件行 */}
<Row gutter={16}>
<Col span={6}>
@ -211,4 +212,4 @@ const ExamPaperAnalysisPage: React.FC = () => {
);
};
export default ExamPaperAnalysisPage;
export default withRouter(ExamPaperAnalysisPage);

@ -0,0 +1,76 @@
import React from 'react';
// 定义单条题目的 props 类型
export interface SingleQuestionProps {
question_types: number; // 题目类型,1 为单选,2 为多选
question_content: string; // 题干
answer: string; // 答案
options: string; // 答题选项
question_number: number; // 题目编号
accuracyRate: number; // 准确率
}
// 定义组件接收的参数类型,为单条题目 props 的数组
interface QuestionProps {
questions: SingleQuestionProps[];
style?: React.CSSProperties; // 用于外部传入样式,控制组件范围大小
}
const QuestionComponent: React.FC<QuestionProps> = ({ questions, style }) => {
return (
<div className="question-container" style={{ ...style, overflow: 'auto' }}>
<h1></h1>
{questions.map((question, index) => {
const { question_types, question_content, answer, options, question_number, accuracyRate } = question;
// 处理 answer 为空的情况
const answerArray = answer ? answer.split(',') : [];
// 处理 options 为空的情况
const optionArray = options ? options.split(',') : [];
// 根据题目类型判断是单选还是多选
const isSingleChoice = question_types === 1;
// 生成选项字母列表(A, B, C...)
const optionLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return (
<div key={index} className="single-question" style={{ background: "#B5DBFF", marginBottom: 20 }}>
{/* 显示题目编号、题干和准确率 */}
<p className="question-title">
{question_number}. {question_content}
<span className="accuracy-rate">{(accuracyRate* 100).toFixed(2) + '%'}</span>
</p>
{/* 显示答题选项 */}
<div className="option-list">
{optionArray.map((option, optionIndex) => {
const letter = optionLetters[optionIndex];
const isChecked = answerArray.includes(letter);
return (
<label key={optionIndex} className="option-label" style={{ marginRight: 20 }}>
{isSingleChoice ? (
<input
type="radio"
checked={isChecked}
disabled
/>
) : (
<input
type="checkbox"
checked={isChecked}
disabled
/>
)}
{option.replace(":", ".")}
</label>
);
})}
</div>
</div>
);
})}
</div>
);
};
export default QuestionComponent;

@ -0,0 +1,57 @@
import React from 'react';
import QuestionComponent from './compoents/QuestionComponent';
import ExamPaperAnalysisDetailPage from "./compoents/ExamPaperAnalysisDetailPage";
const ExamPaperAnalysisDetail: React.FC = () => {
const questions = [
{
question_types: 1,
question_content: '一天有多少小时?',
answer: 'B',
options: 'A:12小时,B:24小时,C:36小时',
question_number: 1,
accuracyRate: '90%'
},
{
question_types: 2,
question_content: '以下哪些是水果?',
answer: 'A,C',
options: 'A:苹果,B:黄瓜,C:香蕉',
question_number: 2,
accuracyRate: '80%'
},
{
question_types: 2,
question_content: '以下哪些是水果?',
answer: 'A,C',
options: 'A:苹果,B:黄瓜,C:香蕉',
question_number: 2,
accuracyRate: '80%'
},
{
question_types: 2,
question_content: '以下哪些是水果?',
answer: 'A,C',
options: 'A:苹果,B:黄瓜,C:香蕉',
question_number: 2,
accuracyRate: '80%'
},
{
question_types: 2,
question_content: '以下哪些是水果?',
answer: 'A,C',
options: 'A:苹果,B:黄瓜,C:香蕉',
question_number: 2,
accuracyRate: '80%'
}
];
return (
<div>
<ExamPaperAnalysisDetailPage
/>
</div>
);
};
export default ExamPaperAnalysisDetail;
Loading…
Cancel
Save