|
@@ -1,113 +1,112 @@
|
|
|
<template>
|
|
|
- <div>
|
|
|
- <div class="tw-bg-white tw-rounded-xl">
|
|
|
- <div class="tw-mt-4"></div>
|
|
|
- 分数间隔
|
|
|
- <a-select v-model:value="scoreGap">
|
|
|
- <a-select-option :value="1">1</a-select-option>
|
|
|
- <a-select-option :value="5">5</a-select-option>
|
|
|
- <a-select-option :value="10">10</a-select-option>
|
|
|
- <a-select-option :value="20">20</a-select-option>
|
|
|
- <a-select-option :value="50">50</a-select-option>
|
|
|
- </a-select>
|
|
|
- <div class="tw-mt-4"></div>
|
|
|
+ <div class="score-rate">
|
|
|
+ <div class="score-part">
|
|
|
+ <div class="score-part-head">
|
|
|
+ <h2>等距({{ scoreGap }}分)分组频数分布</h2>
|
|
|
+ </div>
|
|
|
+ <div class="score-part-body">
|
|
|
+ <div class="mb-16">
|
|
|
+ 分数间隔
|
|
|
+ <a-select v-model:value="scoreGap">
|
|
|
+ <a-select-option :value="1">1</a-select-option>
|
|
|
+ <a-select-option :value="5">5</a-select-option>
|
|
|
+ <a-select-option :value="10">10</a-select-option>
|
|
|
+ <a-select-option :value="20">20</a-select-option>
|
|
|
+ <a-select-option :value="50">50</a-select-option>
|
|
|
+ </a-select>
|
|
|
+ </div>
|
|
|
|
|
|
- <a-collapse :activeKey="courses[0]?.id">
|
|
|
- <a-collapse-panel
|
|
|
- v-for="course in courses"
|
|
|
- :key="course.id"
|
|
|
- :header="course.courseName + '(' + course.courseCode + ')'"
|
|
|
- >
|
|
|
- <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
|
|
|
- <h3 class="section-title">等距({{ scoreGap }}分)分组频数分布</h3>
|
|
|
- <!-- <a-button @click="openModal1">说明</a-button> -->
|
|
|
- </div>
|
|
|
- <div class="tw-flex tw-gap-4" style="max-height: 500px">
|
|
|
- <div class="tw-overflow-auto">
|
|
|
- <table class="custom-table" style="width: 400px">
|
|
|
- <tr>
|
|
|
- <th>分数段</th>
|
|
|
- <th>频数</th>
|
|
|
- <th>频率(%)</th>
|
|
|
- <th>累计频数</th>
|
|
|
- <th>累计频数(%)</th>
|
|
|
- </tr>
|
|
|
- <tr
|
|
|
- v-for="(seg, index2) in course.segements"
|
|
|
- :key="index2 + scoreGap * 100000"
|
|
|
- >
|
|
|
- <td>
|
|
|
- <span v-if="index2 < course.segements.length - 1"
|
|
|
- >{{ scoreGap * index2 }}-{{
|
|
|
- scoreGap * (index2 + 1)
|
|
|
- }}</span
|
|
|
- >
|
|
|
- <span v-else>{{ seg[0] }}</span>
|
|
|
- </td>
|
|
|
- <td>{{ seg[1] }}</td>
|
|
|
- <td v-number-to-percent>{{ seg[2] }}%</td>
|
|
|
- <td>{{ seg[3] }}</td>
|
|
|
- <td v-number-to-percent>{{ seg[4] }}%</td>
|
|
|
- </tr>
|
|
|
- </table>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div style="flex-grow: 1; min-height: 260px">
|
|
|
+ <a-row v-if="course.segements" :gutter="16">
|
|
|
+ <a-col :span="12">
|
|
|
+ <a-table
|
|
|
+ class="page-table"
|
|
|
+ :scroll="{ y: 453 }"
|
|
|
+ :data-source="course.segements"
|
|
|
+ :pagination="false"
|
|
|
+ bordered
|
|
|
+ size="middle"
|
|
|
+ >
|
|
|
+ <a-table-column key="0" title="分数段" data-index="0">
|
|
|
+ <template #default="{ text, index }">
|
|
|
+ <span v-if="index < course.segements.length - 1"
|
|
|
+ >{{ scoreGap * index }}-{{ scoreGap * (index + 1) }}</span
|
|
|
+ >
|
|
|
+ <span v-else>{{ text }}</span>
|
|
|
+ </template>
|
|
|
+ </a-table-column>
|
|
|
+ <a-table-column key="1" title="频率" data-index="1" />
|
|
|
+ <a-table-column key="2" title="频率(%)" data-index="2">
|
|
|
+ <template #default="{ text }">{{
|
|
|
+ $filters.numberToPercentFilter(text)
|
|
|
+ }}</template>
|
|
|
+ </a-table-column>
|
|
|
+ <a-table-column key="3" title="累计频数" data-index="3" />
|
|
|
+ <a-table-column key="4" title="累计频数(%)" data-index="4">
|
|
|
+ <template #default="{ text }">{{
|
|
|
+ $filters.numberToPercentFilter(text)
|
|
|
+ }}</template>
|
|
|
+ </a-table-column>
|
|
|
+ </a-table>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="12">
|
|
|
+ <div class="chart-box">
|
|
|
<v-chart
|
|
|
class="chart"
|
|
|
:option="segementsLine(course)"
|
|
|
:autoresize="true"
|
|
|
/>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
|
|
|
- <h3 class="section-title">科目分数线分组的频数分布</h3>
|
|
|
- <div class="tw-flex tw-gap-2">
|
|
|
- <a-button @click="openRangeConfigModal(course)">
|
|
|
- 分段设置
|
|
|
- </a-button>
|
|
|
- <!-- <a-button @click="openModal2">说明</a-button> -->
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div
|
|
|
- v-if="course.rangeConfig"
|
|
|
- class="tw-flex tw-gap-4"
|
|
|
- style="max-height: 500px"
|
|
|
- >
|
|
|
- <div class="tw-overflow-auto">
|
|
|
- <table class="custom-table" style="width: 400px">
|
|
|
- <tr>
|
|
|
- <th>分数段</th>
|
|
|
- <th>频数</th>
|
|
|
- <th>频率(%)</th>
|
|
|
- <th>累计频数</th>
|
|
|
- <th>累计频数(%)</th>
|
|
|
- </tr>
|
|
|
- <tr
|
|
|
- v-for="(seg, index3) in course.rangeSegements"
|
|
|
- :key="index3"
|
|
|
- >
|
|
|
- <td>{{ seg[0] }}</td>
|
|
|
- <td>{{ seg[1] }}</td>
|
|
|
- <td v-number-to-percent>{{ seg[2] }}%</td>
|
|
|
- <td>{{ seg[3] }}</td>
|
|
|
- <td v-number-to-percent>{{ seg[4] }}%</td>
|
|
|
- </tr>
|
|
|
- </table>
|
|
|
- </div>
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <div style="flex-grow: 1; min-height: 260px">
|
|
|
+ <div class="score-part">
|
|
|
+ <div class="score-part-head">
|
|
|
+ <h2>科目分数线分组的频数分布</h2>
|
|
|
+ </div>
|
|
|
+ <div class="score-part-body">
|
|
|
+ <div class="mb-16">
|
|
|
+ <a-button @click="openRangeConfigModal(course)"> 分段设置 </a-button>
|
|
|
+ </div>
|
|
|
+ <a-row v-if="course.rangeConfig && course.rangeSegements" :gutter="16">
|
|
|
+ <a-col :span="12">
|
|
|
+ <a-table
|
|
|
+ class="page-table"
|
|
|
+ :scroll="{ y: 235 }"
|
|
|
+ :data-source="course.rangeSegements"
|
|
|
+ :pagination="false"
|
|
|
+ bordered
|
|
|
+ size="middle"
|
|
|
+ >
|
|
|
+ <a-table-column key="0" title="分数段" data-index="0">
|
|
|
+ </a-table-column>
|
|
|
+ <a-table-column key="1" title="频率" data-index="1" />
|
|
|
+ <a-table-column key="2" title="频率(%)" data-index="2">
|
|
|
+ <template #default="{ text }">{{
|
|
|
+ $filters.numberToPercentFilter(text)
|
|
|
+ }}</template>
|
|
|
+ </a-table-column>
|
|
|
+ <a-table-column key="3" title="累计频数" data-index="3" />
|
|
|
+ <a-table-column key="4" title="累计频数(%)" data-index="4">
|
|
|
+ <template #default="{ text }">{{
|
|
|
+ $filters.numberToPercentFilter(text)
|
|
|
+ }}</template>
|
|
|
+ </a-table-column>
|
|
|
+ </a-table>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="12">
|
|
|
+ <div class="chart-box" style="height: 282px">
|
|
|
<v-chart
|
|
|
class="chart"
|
|
|
:option="rangeSegementsLine(course)"
|
|
|
:autoresize="true"
|
|
|
/>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div v-else>请先进行分段设置。</div>
|
|
|
- </a-collapse-panel>
|
|
|
- </a-collapse>
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
+ <div v-else>请先进行分段设置。</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
<CommonRangeConfig
|
|
@@ -145,7 +144,7 @@ const props = defineProps<{
|
|
|
const route = useRoute();
|
|
|
const projectId = +route.params.projectId;
|
|
|
|
|
|
-let courses = $ref<SasCourse[]>([]);
|
|
|
+let course = $ref<SasCourse>({} as SasCourse);
|
|
|
async function fetchData() {
|
|
|
const res = await getSasCourseList({
|
|
|
projectId,
|
|
@@ -180,7 +179,7 @@ async function fetchData() {
|
|
|
return v;
|
|
|
});
|
|
|
// console.log(res.data);
|
|
|
- courses = res.data;
|
|
|
+ course = res.data[0];
|
|
|
}
|
|
|
|
|
|
onMounted(async () => {
|
|
@@ -192,74 +191,72 @@ watch(() => [props.courseId], fetchData);
|
|
|
let scoreGap = $ref(10);
|
|
|
|
|
|
watch(
|
|
|
- () => [scoreGap, courses],
|
|
|
+ () => [scoreGap, course],
|
|
|
() => {
|
|
|
- for (const course of courses) {
|
|
|
- course.segements = [];
|
|
|
- const validSeg = Math.round(course.totalScore / scoreGap);
|
|
|
- for (let score = 0; score < validSeg; score++) {
|
|
|
- const row = [];
|
|
|
- row[0] = score * scoreGap;
|
|
|
- let nextScore = score + 1 > validSeg ? course.totalScore : score + 1;
|
|
|
- // row[1] =
|
|
|
- // course.scoreRangeAcc[nextScore * scoreGap - 1] -
|
|
|
- // course.scoreRangeAcc[score * scoreGap];
|
|
|
- row[1] = course.scoreRange
|
|
|
- .slice(row[0], nextScore * scoreGap)
|
|
|
- .reduce((p, c) => p + c, 0);
|
|
|
- row[2] = row[1] / course.scoreRangeAcc[course.totalScore];
|
|
|
- const endGap =
|
|
|
- nextScore * scoreGap - 1 >= course.totalScore
|
|
|
- ? course.totalScore
|
|
|
- : nextScore * scoreGap - 1;
|
|
|
- row[3] = course.scoreRangeAcc[endGap];
|
|
|
- row[4] = row[3] / course.scoreRangeAcc[course.totalScore];
|
|
|
- course.segements.push(row);
|
|
|
- }
|
|
|
- if (validSeg * scoreGap === course.totalScore) {
|
|
|
- course.segements.push([
|
|
|
- course.totalScore,
|
|
|
- course.scoreRange[course.totalScore],
|
|
|
- course.scoreRange[course.totalScore] /
|
|
|
- course.scoreRangeAcc[course.totalScore],
|
|
|
+ if (!course || !course.id) return;
|
|
|
+ course.segements = [];
|
|
|
+ const validSeg = Math.round(course.totalScore / scoreGap);
|
|
|
+ for (let score = 0; score < validSeg; score++) {
|
|
|
+ const row = [];
|
|
|
+ row[0] = score * scoreGap;
|
|
|
+ let nextScore = score + 1 > validSeg ? course.totalScore : score + 1;
|
|
|
+ // row[1] =
|
|
|
+ // course.scoreRangeAcc[nextScore * scoreGap - 1] -
|
|
|
+ // course.scoreRangeAcc[score * scoreGap];
|
|
|
+ row[1] = course.scoreRange
|
|
|
+ .slice(row[0], nextScore * scoreGap)
|
|
|
+ .reduce((p, c) => p + c, 0);
|
|
|
+ row[2] = row[1] / course.scoreRangeAcc[course.totalScore];
|
|
|
+ const endGap =
|
|
|
+ nextScore * scoreGap - 1 >= course.totalScore
|
|
|
+ ? course.totalScore
|
|
|
+ : nextScore * scoreGap - 1;
|
|
|
+ row[3] = course.scoreRangeAcc[endGap];
|
|
|
+ row[4] = row[3] / course.scoreRangeAcc[course.totalScore];
|
|
|
+ course.segements.push(row);
|
|
|
+ }
|
|
|
+ if (validSeg * scoreGap === course.totalScore) {
|
|
|
+ course.segements.push([
|
|
|
+ course.totalScore,
|
|
|
+ course.scoreRange[course.totalScore],
|
|
|
+ course.scoreRange[course.totalScore] /
|
|
|
course.scoreRangeAcc[course.totalScore],
|
|
|
- 1,
|
|
|
- ]);
|
|
|
- }
|
|
|
-
|
|
|
- course.rangeSegements = [];
|
|
|
- for (let i = 0; i < course.rangeConfig.length; i++) {
|
|
|
- const range = course.rangeConfig[i]!;
|
|
|
- const nextRange =
|
|
|
- i === course.rangeConfig.length - 1
|
|
|
- ? { baseScore: course.totalScore, adjustScore: 0 }
|
|
|
- : course.rangeConfig[i + 1];
|
|
|
- const row = [];
|
|
|
- row[0] = scoreTitle(range);
|
|
|
- row[1] =
|
|
|
- course.scoreRange
|
|
|
- .slice(
|
|
|
- range.baseScore + range.adjustScore,
|
|
|
- nextRange.baseScore + nextRange.adjustScore
|
|
|
- )
|
|
|
- .reduce((p, c) => p + c, 0) || 0;
|
|
|
- row[2] = row[1] / course.scoreRangeTotal;
|
|
|
- row[3] =
|
|
|
- course.scoreRangeAcc[
|
|
|
- nextRange.baseScore + nextRange.adjustScore - 1
|
|
|
- ] || 0;
|
|
|
- if (
|
|
|
- nextRange.baseScore + nextRange.adjustScore >=
|
|
|
- course.scoreRangeAcc.length
|
|
|
- ) {
|
|
|
- row[3] = course.scoreRangeAcc[course.scoreRangeAcc.length - 1];
|
|
|
- }
|
|
|
- row[4] = row[3] / course.scoreRangeTotal;
|
|
|
+ course.scoreRangeAcc[course.totalScore],
|
|
|
+ 1,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
|
|
|
- course.rangeSegements.push(row);
|
|
|
+ course.rangeSegements = [];
|
|
|
+ for (let i = 0; i < course.rangeConfig.length; i++) {
|
|
|
+ const range = course.rangeConfig[i]!;
|
|
|
+ const nextRange =
|
|
|
+ i === course.rangeConfig.length - 1
|
|
|
+ ? { baseScore: course.totalScore, adjustScore: 0 }
|
|
|
+ : course.rangeConfig[i + 1];
|
|
|
+ const row = [];
|
|
|
+ row[0] = scoreTitle(range);
|
|
|
+ row[1] =
|
|
|
+ course.scoreRange
|
|
|
+ .slice(
|
|
|
+ range.baseScore + range.adjustScore,
|
|
|
+ nextRange.baseScore + nextRange.adjustScore
|
|
|
+ )
|
|
|
+ .reduce((p, c) => p + c, 0) || 0;
|
|
|
+ row[2] = row[1] / course.scoreRangeTotal;
|
|
|
+ row[3] =
|
|
|
+ course.scoreRangeAcc[nextRange.baseScore + nextRange.adjustScore - 1] ||
|
|
|
+ 0;
|
|
|
+ if (
|
|
|
+ nextRange.baseScore + nextRange.adjustScore >=
|
|
|
+ course.scoreRangeAcc.length
|
|
|
+ ) {
|
|
|
+ row[3] = course.scoreRangeAcc[course.scoreRangeAcc.length - 1];
|
|
|
}
|
|
|
- // console.log(course);
|
|
|
+ row[4] = row[3] / course.scoreRangeTotal;
|
|
|
+
|
|
|
+ course.rangeSegements.push(row);
|
|
|
}
|
|
|
+ // console.log(course);
|
|
|
},
|
|
|
{
|
|
|
immediate: true,
|
|
@@ -319,14 +316,37 @@ function segementsLine(course: SasCourse) {
|
|
|
return {
|
|
|
title: {
|
|
|
text: "频数",
|
|
|
- left: "left",
|
|
|
+ left: 16,
|
|
|
+ top: 16,
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 14,
|
|
|
+ color: "#595959",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ color: ["#4080FF"],
|
|
|
+ grid: {
|
|
|
+ left: 50,
|
|
|
+ right: 70,
|
|
|
+ bottom: 50,
|
|
|
+ top: 60,
|
|
|
},
|
|
|
xAxis: {
|
|
|
type: "category",
|
|
|
data: course.segements.map((v) => v[0]),
|
|
|
+ name: "分数段",
|
|
|
+ axisLabel: {
|
|
|
+ color: "#8C8C8C",
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 400,
|
|
|
+ },
|
|
|
},
|
|
|
yAxis: {
|
|
|
type: "value",
|
|
|
+ axisLabel: {
|
|
|
+ color: "#8C8C8C",
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 400,
|
|
|
+ },
|
|
|
},
|
|
|
series: [
|
|
|
{
|
|
@@ -343,14 +363,37 @@ function rangeSegementsLine(course: SasCourse) {
|
|
|
return {
|
|
|
title: {
|
|
|
text: "频数",
|
|
|
- left: "left",
|
|
|
+ left: 16,
|
|
|
+ top: 16,
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 14,
|
|
|
+ color: "#595959",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ color: ["#4080FF"],
|
|
|
+ grid: {
|
|
|
+ left: 50,
|
|
|
+ right: 70,
|
|
|
+ bottom: 50,
|
|
|
+ top: 60,
|
|
|
},
|
|
|
xAxis: {
|
|
|
type: "category",
|
|
|
data: course.rangeSegements.map((v) => v[0]),
|
|
|
+ name: "分数段",
|
|
|
+ axisLabel: {
|
|
|
+ color: "#8C8C8C",
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 400,
|
|
|
+ },
|
|
|
},
|
|
|
yAxis: {
|
|
|
type: "value",
|
|
|
+ axisLabel: {
|
|
|
+ color: "#8C8C8C",
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 400,
|
|
|
+ },
|
|
|
},
|
|
|
series: [
|
|
|
{
|