| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- <template>
- <ReportModule :titleList="['1、分数段图']" tableOrChart="chart" :showPrintBtn="false" :showExportBtn="false">
- <template #title_right>
- <div :class="['right_item', { item_active: state.sectionScore == item }]" v-for="item in state.sortRangeScore"
- :key="item" @click="TagClick(item)">
- {{ item }}分段
- </div>
- <div class="right_set">
- <span>设置分数段</span>
- <el-input v-model="state.sectionScore" maxlength="3" style="width: 54px" @input="HandleInput"
- @change="BlurSectionScore" />
- <span>分/段</span>
- </div>
- <div class="right_radio">
- <el-select v-model="state.radioRangeScore" v-if="analysisStore?.filterObject?.classLevel != 2">
- <el-option :value="0" label="按年级"></el-option>
- <el-option :value="1" label="按班级"></el-option>
- </el-select>
- </div>
- </template>
- <template #module_table_chart>
- <template v-if="state.scoreSegmentData.datay.length">
- <template v-if="state.radioRangeScore == 0">
- <BarLineChart :datax="state.scoreSegmentData.datax" :datay="state.scoreSegmentData.datay"
- :fullScore="state.scoreSegmentData.fullScore" :markLine="state.scoreSegmentData.markLine"
- :color="state.scoreSegmentData.color" :title="state.scoreSegmentData.title"
- :tooltipData="state.scoreSegmentData.tooltipData" :unit="state.scoreSegmentData.unit"
- :tooltipTitle="state.scoreSegmentData.tooltipTitle">
- </BarLineChart>
- </template>
- <!-- 按班级 -->
- <template v-if="state.radioRangeScore == 1">
- <LineBarChart :datax="state.scoreSegmentClassData.datax" :datay="state.scoreSegmentClassData.datay"
- :showBackground="false" :legendList="state.scoreSegmentClassData.legendList"
- :title="state.scoreSegmentClassData.title" :tooltipData="state.scoreSegmentClassData.tooltipData"
- :hideOverlap="true"></LineBarChart>
- </template>
- </template>
- <div v-else class="no_content_data" v-loading="state.tableLoading" :element-loading-text="state.loadingText"
- element-loading-spinner="el-icon-loading" element-loading-background="#ffffff">
- <span>暂无数据</span>
- </div>
- </template>
- <template #module_describe>
- 说明:信度是反应考试一致性或可靠性的指标,信度高,表示考试成绩较为准确,误差较小;
- 信度低,表明误差大;信度低的考试无法正确评价考生的知识水平与智能素质。
- 效度是指测验的有效性或正确性,即测验能否准确测量出其所要测量的内容。效度高的测验能够确保测验内容与测验目的的一致性,反映测验的正确性和准确性。
- 分析试题的难度和区分度可以确保试题的质量和有效性,从而更好地评估学生的知识掌握程度和区分不同水平的学生。
- 难度指试题的难易程度,通常用P值表示。计算方式为P=X/M(P为难度,X为试题平均得分,M为试题满分)。P值在0到1之间,值越大表示试题越简单,试题通常分为容易题(P≥0.7)、中等题(0.4至0.7之间)和难题(P≤0.4)。
- 区分度是衡量试题对不同水平考生的区分能力,通常用D值表示。通过计算高分组和低分组在某一试题上的通过率之差,得到试题区分度。区分度高的试题能将不同水平的考生区分开来,水平高的学生得高分,水平低的学生得低分。D值的取值范围介于-1至1之间,D值越高,区分的效果越好。D≥0.4表明此题的区分度很好,属于优秀;0.3≤D<0.4表明此题的区分度较好,属于良好;0.2≤D<0.4表明此题的区分度一般;D<0.2表明此题的区分度较低。
- 图中展示了各科的命题分析明细,点击科目的柱可在下方查看该科所有小题的命题分析。
- </template>
- </ReportModule>
- <ReportModule :titleList="['2、分数段表']" tableOrChart="table" :showPrintBtn="false" :showDescribe="false"
- :currentPage="state.scoreSegmentData.pageNum" :pageSize="state.scoreSegmentData.pageSize"
- :total="state.scoreSegmentData.total" @update:pageSize="handleSizeChange" @update:currentPage="handleCurrentChange">
- <template #module_table_chart>
- <el-table :data="scoreRangeTableData" border>
- <template v-for="(header, headerIndex) in state.scoreSegmentData.headerData">
- <el-table-column align="center" :label="header.name" v-if="header.child && header.child.length"
- :key="`child_${headerIndex}`">
- <el-table-column v-for="(child, childIndex) in header.child" align="center" :label="child.value"
- :prop="child.prop" :key="`${headerIndex}_${childIndex}`" show-overflow-tooltip>
- <template #default="scope">
- <template v-for="(detailItem, detailKey) in scope.row.detailList">
- <span v-if="header.name == detailItem.schoolName" :key="`${headerIndex}_${childIndex}_${detailKey}`">
- <span :class="child.prop == 'doubleOnlineNum' ? 'table_row_blue' : ''"
- v-if="child.prop == 'doubleOnlineNum' && detailItem[child.prop] != 0 && detailItem[child.prop] != '-'">
- {{ detailItem[child.prop] }}
- </span>
- <span v-else>
- {{ detailItem[child.prop] }}
- </span>
- </span>
- </template>
- </template>
- </el-table-column>
- </el-table-column>
- <el-table-column v-else align="center" width="100" :label="header.name" :prop="header.prop" :key="headerIndex"
- fixed="left" show-overflow-tooltip>
- <template #default="scope">
- {{ scope.row[header.prop] }}
- </template>
- </el-table-column>
- </template>
- </el-table>
- </template>
- </ReportModule>
- </template>
- <script lang="ts" setup>
- import ReportModule from "@/components/ReportModule.vue";
- import BarLineChart from "@/components/echarts/barLineChart.vue";//柱状图折线图组件
- import LineBarChart from "@/components/echarts/lineBarChart.vue";//折线图柱状图组件
- import { useAnalysisStore } from "@/store/analysis";
- import { onMounted, reactive, computed, watch } from "vue";
- import { scoreSegment } from "@/api/analysis";
- const analysisStore = useAnalysisStore();
- const state = reactive({
- sectionScore: '', // 设置分数段输入框
- sortRangeScore: ['5', '10'], // 设置分数段
- radioRangeScore: 0, // 0按年级, 1按班级
- scoreSegmentData: {
- exportLoading: false,
- datax: [],
- datay: [],
- fullScore: 100, //满分值
- markLine: [], //辅助线
- tooltipData: [], //悬浮弹窗的数据
- title: ["年级", "年级"],
- color: ["#995FB3", "#5470C6"],
- unit: "人",
- tooltipTitle: "及格率",
- tableData: [],
- headerData: [],
- pageSize: 10, //每页显示数据
- total: 0, //总数
- pageNum: 1, //当前页
- }, //分数段图数据
- tableLoading: true,
- loadingText: "加载中……",
- scoreSegmentClassData: {
- datax: [],
- datay: [],
- title: [],
- legendList: [],
- tooltipData: [],//悬浮弹窗的数据
- }//分数段 按班级分析数据
- });
- const scoreRangeTableData = computed(() => {
- const { tableData, pageSize, pageNum } = state.scoreSegmentData;
- const start = (pageNum - 1) * pageSize;
- const end = start + pageSize;
- return tableData.slice(start, end);
- })
- //分数段
- const GetScoreSegment = async () => {
- state.tableLoading = true;
- const res = await scoreSegment({
- ...analysisStore.filterObject,
- scoreSegmentNum: state.sectionScore,
- });
- if (res.code === 200 && res.data && res.data.rowData && res.data.rowData.length) {
- let chartData = res.data.chartData
- let chartDataTotal = res.data.chartData.groupSchoolData // 联校/年级数据
- let chartDataSingle = res.data.chartData.oneSchoolData // 单校/班级数据
- let datax = []; // 分数段x轴数据
- let datay = []; // 联校/年级分数段y轴数据
- let count = []; // 联校/年级数据
- let tooltipData = []; // 联校/年级悬浮弹窗数据
- let markLine = []; // 辅助线数据
- let classTooltipData = []; //班级悬浮弹窗数据
- let classDatay = []; //分数段按班级y轴数据
- let classTitle = []; //分数段按班级图例数据
- let average = parseFloat(res.data.chartData.average); // 平均分
- let averageIndex = 0;//平均分的索引
- let standard = parseFloat(res.data.chartData.standard);//标准差
- let standardIndex = 0;//标准差索引
- let twoStandard = parseFloat(res.data.chartData.twoStandard);//2倍标准差
- let twoStandardIndex = 0;//2倍标准差索引
- let negativeOneStandard = parseFloat(res.data.chartData.negativeOneStandard);//-1倍标准差
- let negativeOneStandardIndex = 0;//-1倍标准差索引
- let negativeTwoStandard = parseFloat(res.data.chartData.negativeTwoStandard);//-2倍标准差
- let negativeTwoStandardIndex = 0;//-2倍标准差索引
- chartDataTotal.forEach((item, index) => {
- let list = item.name.split('-');
- let num1 = parseInt(list[0].replace(/[\[\]()]/g, ''));
- let num2 = parseInt(list[1].replace(/[\[\]()]/g, ''));
- if (num2 < average && average < num1) { // 平均分
- averageIndex = index
- }
- if (num2 < standard && standard < num1) { // 标准差
- standardIndex = index
- }
- if (num2 < twoStandard && twoStandard < num1) { // 2倍标准差
- twoStandardIndex = index
- }
- if (num2 < negativeOneStandard && negativeOneStandard < num1) { // -1倍标准差
- negativeOneStandardIndex = index
- }
- if (num2 < negativeTwoStandard && negativeTwoStandard < num1) { // -2倍标准差
- negativeTwoStandardIndex = index
- }
- datax.push(item.name)
- count.push(item.doubleOnlineNum)
- tooltipData.push({
- name: '',
- value: `${item.doubleOnlineNum}人,占比${item.doubleOnlineRate} ${item.studentUserName.length ? `(${item.studentUserName.join('、')})` : ''}`
- })
- });
- datay.push(count, count)
- markLine.push({
- name: '平均分',
- value: average,
- color: '#FAC858',
- xAxis: averageIndex,
- });
- markLine.push({
- name: '标准差',
- color: '#3BA272',
- value: standard,
- xAxis: standardIndex,
- });
- markLine.push({
- name: '-标准差',
- color: '#EE6666',
- value: negativeOneStandard,
- xAxis: negativeOneStandardIndex,
- });
- markLine.push({
- name: '-2倍标准差',
- color: '#EE6666',
- value: negativeTwoStandard,
- xAxis: negativeTwoStandardIndex,
- });
- markLine.push({
- name: '2倍标准差',
- color: '#3BA272',
- value: twoStandard,
- xAxis: twoStandardIndex,
- });
- //判断人数是否可点击
- const rowData = res.data.rowData || [];
- state.scoreSegmentData = {
- datax: datax,
- datay: datay,
- fullScore: parseFloat(chartData.fullScore),
- markLine: markLine,//辅助线数据
- tooltipData: tooltipData,//悬浮弹窗数据
- title: ["年级", "1班"],
- color: ["#995FB3", "#5470C6"],
- unit: '人',
- tooltipTitle: '及格率',
- tableData: rowData,
- headerData: res.data.titleData || [],
- pageSize: 10,//每页显示数据
- total: rowData.length,//总数
- pageNum: 1,//当前页
- };//分数段图数据
- // 单校/班级图表数据处理
- chartDataSingle.forEach(item => {
- classTitle.push(item.name)
- let singleItem = []
- let tootlipItem = []
- item.detailList.forEach(scoreItem => {
- singleItem.push(scoreItem.doubleOnlineNum)
- tootlipItem.push(`${scoreItem.doubleOnlineNum}人,占比${scoreItem.doubleOnlineRate}`);
- })
- classDatay.push(singleItem)
- classTooltipData.push(tootlipItem)
- })
- state.scoreSegmentClassData = {
- datax: datax,
- datay: classDatay,
- title: classTitle,//不会修改原数组
- legendList: classTitle.slice(0, 3),//默认显示全部
- tooltipData: classTooltipData,
- };
- } else {
- state.scoreSegmentData = {
- datax: [],
- datay: [],
- fullScore: 100,//满分值
- markLine: [],//辅助线
- tooltipData: [],//悬浮弹窗的数据
- title: ["年级", "年级"],
- color: ["#995FB3", "#5470C6"],
- unit: '人',
- tooltipTitle: '及格率',
- tableData: [],
- headerData: [],
- pageSize: 10,//每页显示数据
- total: 0,//总数
- pageNum: 1,//当前页
- };//分数段图数据
- state.scoreSegmentClassData = {
- datax: [],
- datay: [],
- title: [],
- legendList: [],
- tooltipData: [],//悬浮弹窗的数据
- };//分数段 按班级分析数据
- }
- state.tableLoading = false;
- };
- //分数段切换
- const TagClick = (item) => {
- state.sectionScore = item;
- GetScoreSegment();//获取分数段数据
- }
- const HandleInput = (value: string) => {
- state.sectionScore = value.replace(/[^\d]/g, '');
- }
- // 设置分数段失去焦点
- const BlurSectionScore = (value: string) => {
- if (value == '0') {
- state.sectionScore = '';
- }
- GetScoreSegment();//获取分数段数据
- }
- // 分页
- const handleCurrentChange = (val: number) => {
- state.scoreSegmentData.pageNum = val;
- }
- const handleSizeChange = (val: number) => {
- state.scoreSegmentData.pageSize = val;
- state.scoreSegmentData.pageNum = 1;
- }
- // 初始化
- const pageInit = () => {
- GetScoreSegment();
- };
- // 监听筛选条件
- watch(
- () => analysisStore.filterObject,
- async () => {
- state.sectionScore = '';
- pageInit();
- },
- { deep: true },
- );
- onMounted(() => {
- pageInit();
- });
- </script>
- <style lang="scss" scoped>
- .el-input__inner {
- height: 36px;
- }
- .el-input__suffix {
- line-height: 36px;
- }
- .right_item {
- font-size: 14px;
- color: #999;
- font-weight: 400;
- cursor: pointer;
- margin-right: 5px;
- border-bottom: 2px solid #ffffff;
- &.item_active {
- font-size: 14px;
- color: #2e64fa;
- font-weight: 500;
- cursor: pointer;
- border-bottom: 2px solid #2e64fa;
- }
- }
- .right_set {
- font-weight: 400;
- font-size: 14px;
- color: #333333;
- :deep(.el-input) {
- padding: 0 5px;
- margin: 0 2px;
- .el-input__inner {
- text-align: center;
- }
- }
- }
- .right_radio {
- :deep(.el-select) {
- width: 100px;
- }
- }
- </style>
|