diff --git a/packages/examination/src/api/exam-online/index.tsx b/packages/examination/src/api/exam-online/index.tsx index dd9682c..60fcad8 100644 --- a/packages/examination/src/api/exam-online/index.tsx +++ b/packages/examination/src/api/exam-online/index.tsx @@ -108,4 +108,37 @@ export const getExamDetailAnalysisPageList = async (obj:any) => { 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; }; \ No newline at end of file diff --git a/packages/examination/src/components/contentMain/index.js b/packages/examination/src/components/contentMain/index.js index b0ce1b9..98f6d0f 100644 --- a/packages/examination/src/components/contentMain/index.js +++ b/packages/examination/src/components/contentMain/index.js @@ -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 { + diff --git a/packages/examination/src/views/exam-online/compoents/ExamDetailAnalysisPage.tsx b/packages/examination/src/views/exam-online/compoents/ExamDetailAnalysisPage.tsx index 5ab1621..c9c05eb 100644 --- a/packages/examination/src/views/exam-online/compoents/ExamDetailAnalysisPage.tsx +++ b/packages/examination/src/views/exam-online/compoents/ExamDetailAnalysisPage.tsx @@ -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 (
+ {/* 检索条件行 */} diff --git a/packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisDetailPage.tsx b/packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisDetailPage.tsx new file mode 100644 index 0000000..252bceb --- /dev/null +++ b/packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisDetailPage.tsx @@ -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 = ({history}) => { + const [paperData, setPaperData] = useState(null); + const [tableData, setTableData] = useState([]); + + 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}
试题数量占比: ${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 ( +
+ + {paperData && ( + <> + {/* 第一行:试卷名称 */} + + + 试卷名称:{paperData.paperName} + + + + {/* 第二行:QuestionComponent 标签 */} + + + + + + {/* 第三行:柱状图 */} + + +

准确率分析

+ + +
+ + )} +
+ ); +}; + +export default withRouter(ExamPaperAnalysisDetailPage); \ No newline at end of file diff --git a/packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisPage.tsx b/packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisPage.tsx index 31e9fc2..f128141 100644 --- a/packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisPage.tsx +++ b/packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisPage.tsx @@ -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 = ({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([]); 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); - setPagination({ - ...pagination, - current: 1, - total: filteredData.length, - }); + const param = { + paperName:paperName, + industry:industry, + } + const fetchData = async () => { + try { + const resultData = await getExamPaperAnalysisPageList(param) + setTableData(resultData); + setPagination({ + ...pagination, + current: 1, + 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) => 详情, - }, - { - title: 'paperId', - dataIndex: 'paperId', - visible: false, // 隐藏列 + render: (text:any, record: PaperAnalysisDataItem) => {handleShowScoreDistribution(record.paperId)}}>题目正确率分析, }, ]; return (
+ {/* 检索条件行 */} @@ -211,4 +212,4 @@ const ExamPaperAnalysisPage: React.FC = () => { ); }; -export default ExamPaperAnalysisPage; \ No newline at end of file +export default withRouter(ExamPaperAnalysisPage); \ No newline at end of file diff --git a/packages/examination/src/views/exam-online/compoents/QuestionComponent.tsx b/packages/examination/src/views/exam-online/compoents/QuestionComponent.tsx new file mode 100644 index 0000000..a92ddc9 --- /dev/null +++ b/packages/examination/src/views/exam-online/compoents/QuestionComponent.tsx @@ -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 = ({ questions, style }) => { + return ( +
+

试题详情

+ {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 ( +
+ {/* 显示题目编号、题干和准确率 */} +

+ {question_number}. {question_content} + 准确率:{(accuracyRate* 100).toFixed(2) + '%'} +

+ {/* 显示答题选项 */} +
+ {optionArray.map((option, optionIndex) => { + const letter = optionLetters[optionIndex]; + const isChecked = answerArray.includes(letter); + return ( + + ); + })} +
+
+ ); + })} +
+ ); +}; + +export default QuestionComponent; \ No newline at end of file diff --git a/packages/examination/src/views/exam-online/exam_paper_analysis_detail.tsx b/packages/examination/src/views/exam-online/exam_paper_analysis_detail.tsx new file mode 100644 index 0000000..a046c85 --- /dev/null +++ b/packages/examination/src/views/exam-online/exam_paper_analysis_detail.tsx @@ -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 ( +
+ +
+ ); +}; + +export default ExamPaperAnalysisDetail; \ No newline at end of file