Browse Source

试卷分析 API debug

Michael Wang 3 years ago
parent
commit
82b4538952

+ 48 - 0
src/api/paperAnalysisPage.ts

@@ -0,0 +1,48 @@
+import { httpApp } from "@/plugins/axiosApp";
+
+/** 试卷查询 */
+export function getPaper(id: number) {
+  return httpApp.post("/api/ess/paper/" + id);
+}
+
+/** 试卷分析-查询指定试卷全卷信息 */
+export function getSasPaper(id: number) {
+  return httpApp.post("/api/ess/sasPaper/" + id);
+}
+
+/** 试题题目编排,试卷特征量数,试题难度分组分布 */
+export function getPaperQuestions(paperId: number) {
+  return httpApp.post(
+    "/api/ess/sasQuestion/list",
+    new URLSearchParams([["paperId", paperId + ""]])
+  );
+}
+
+/** 题型难度分布、题型区分度分布 */
+export function getPaperQuestionGroups(projectId: number, paperId: number) {
+  return httpApp.post(
+    "/api/ess/sasQuestionGroup/list",
+    new URLSearchParams([
+      ["projectId", projectId + ""],
+      ["paperId", paperId + ""],
+    ])
+  );
+}
+
+/** 试题难度分组分布-难度分组设置 */
+export function setPaperRangeConfig(params: {
+  projectId: number;
+  courseId?: number;
+  paperId?: number;
+  rangeConfig: any;
+}) {
+  return httpApp.post("/api/ess/paper/difficulityRangeConfig", params);
+}
+
+/** 导入题型设置 */
+export function importQuestionGroups(projectId: number, file: File) {
+  const f = new FormData();
+  f.append("projectId", projectId + "");
+  f.append("file", file);
+  return httpApp.post(`/api/ess/sasQuestionGroup/import`, f);
+}

+ 4 - 2
src/components/ExplainModal.vue

@@ -19,7 +19,6 @@ import { useMainStore } from "@/store";
 import { watch } from "vue-demi";
 
 const store = useMainStore();
-const rootOrgId = store.userInfo.rootOrgId;
 // const { keyCode } = defineProps<{ keyCode: string }>();
 let keyCode = $ref("");
 EventBus.on("SHOW_SETTING", (e) => {
@@ -35,7 +34,10 @@ let visible = $ref(false);
 watch(
   () => keyCode,
   async () => {
-    const res = await getRootOrgSettingsOfKey(rootOrgId, keyCode);
+    const res = await getRootOrgSettingsOfKey(
+      store.userInfo.rootOrgId,
+      keyCode
+    );
     keyDetail = res.data;
     visible = true;
   }

+ 1 - 1
src/features/courseManagement/CourseManagement.vue

@@ -284,7 +284,7 @@ async function handleToggleCourses(enable: boolean, ids: number[]) {
 }
 
 /** <handleImport> */
-let importModalVisible = ref<boolean>(false);
+let importModalVisible = $ref<boolean>(false);
 async function handleImport() {
   const files = (document.querySelector("#file-input") as HTMLInputElement)
     .files;

+ 128 - 0
src/features/paperAnalysis/PaperAnalysis.vue

@@ -0,0 +1,128 @@
+<template>
+  <div>
+    <div class="tw-bg-white tw-p-5 tw-rounded-xl tw-mb-5">
+      <ProjectSelect :project-id="projectId" v-model:value="projectId" />
+      <span class="tw-mr-4"></span>
+      <CourseSelect :root-org-id="rootOrgId" v-model:value="courseId" />
+      <span class="tw-mr-4"></span>
+      <PaperTypeSelect v-model:value="paperType" />
+      <span class="tw-mr-4"></span>
+      试卷名称: <a-input disabled :value="paperName" style="width: 100px" />
+      <span class="tw-mr-4"></span>
+      <a-button @click="search">查询</a-button>
+
+      <div class="tw-mt-4">
+        <a-button @click="goBack">返回</a-button>
+      </div>
+    </div>
+
+    <div class="tw-bg-white tw-p-5 tw-rounded-xl">
+      <a-radio-group v-model:value="activeTab" class="tw-mb-4">
+        <a-radio-button value="1">试卷题目编排</a-radio-button>
+        <a-radio-button value="2">试题特征量数</a-radio-button>
+        <a-radio-button value="3">题型难度分布</a-radio-button>
+        <a-radio-button value="4">题型难度分布</a-radio-button>
+        <a-radio-button value="5">试题难度分组分布</a-radio-button>
+      </a-radio-group>
+
+      <div v-if="activeTab === '1'">
+        <QuestionBianPai :questions="paperQuestions" />
+      </div>
+      <div v-if="activeTab === '2'">
+        <QuestionAttr :questions="paperQuestions" />
+      </div>
+      <div v-if="activeTab === '3'">
+        <QuestionTypeDifficulty
+          :project-id="projectId"
+          :questions="paperQuestionGroups"
+        />
+      </div>
+      <div v-if="activeTab === '5'">
+        <QuestionDifficultyGroup
+          :questions="paperQuestions"
+          :project-id="projectId"
+          :courseId="courseId"
+          :paperId="paperId"
+          :range-config="paper.difficulityRangeConfig"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useMainStore } from "@/store";
+import { goBack } from "@/utils/utils";
+import { watch, onMounted, ref, toRaw } from "vue-demi";
+import { useRoute } from "vue-router";
+import ProjectSelect from "@/components/ProjectSelect.vue";
+import router from "@/router";
+import {
+  getPaper,
+  getPaperQuestionGroups,
+  getPaperQuestions,
+  getSasPaper,
+} from "@/api/paperAnalysisPage";
+import QuestionBianPai from "./QuestionBianPai.vue";
+import QuestionAttr from "./QuestionAttr.vue";
+import QuestionDifficultyGroup from "./QuestionDifficultyGroup.vue";
+import QuestionTypeDifficulty from "./QuestionTypeDifficulty.vue";
+
+const store = useMainStore();
+store.currentLocation = "基础管理 / 试卷分析";
+
+let activeTab = $ref("1");
+
+let rootOrgId = $ref(undefined as unknown as number);
+let courseId = $ref(undefined as undefined | number);
+let paperType = $ref(undefined as undefined | string);
+let paperName = $ref(undefined as undefined | string);
+const route = useRoute();
+const projectId = +route.params.projectId;
+const paperId = +route.params.paperId;
+
+let paper = $ref({});
+let pageSize = $ref(10);
+let pageNo = $ref(1);
+
+let paperQuestions = $ref([]);
+let paperQuestionGroups = $ref([]);
+let sasPaper = $ref({});
+
+async function search() {
+  await fetchData();
+}
+
+watch(() => [pageNo, pageSize], fetchData);
+
+async function fetchData() {
+  const res = await getPaper(paperId);
+  // console.log(res);
+  paperType = res.data.paperType;
+  paperName = res.data.paperName;
+  courseId = res.data.courseId;
+  paper = res.data;
+
+  const res2 = await getPaperQuestions(paperId);
+  res2.data = res2.data.map((q) => {
+    q.difficulityLevel = JSON.parse(q.difficulityLevel || "[]");
+    return q;
+  });
+  paperQuestions = res2.data;
+
+  const res3 = await getPaperQuestionGroups(projectId, paperId);
+  res3.data = res3.data.map((q) => {
+    q.difficulityLevel = JSON.parse(q.difficulityLevel || "[]");
+    return q;
+  });
+  paperQuestionGroups = res3.data;
+
+  const res4 = await getSasPaper(paperId);
+  sasPaper = res4.data;
+}
+
+onMounted(async () => {
+  rootOrgId = store.userInfo.rootOrgId;
+  await search();
+});
+</script>

+ 91 - 0
src/features/paperAnalysis/QuestionAttr.vue

@@ -0,0 +1,91 @@
+<template>
+  <div>
+    <a-button @click="openModal">说明</a-button>
+
+    <a-table
+      style="width: 100%; overflow-x: scroll"
+      row-key="id"
+      :columns="columns"
+      :data-source="props.questions"
+    ></a-table>
+  </div>
+</template>
+
+<script setup lang="ts">
+import EventBus from "@/plugins/eventBus";
+import { Question } from "@/types";
+
+const props = defineProps<{ questions: Question[] }>();
+
+const columns = [
+  {
+    title: "大题号",
+    dataIndex: "mainNumber",
+    width: 80,
+  },
+  {
+    title: "小题号",
+    dataIndex: "subNumber",
+    width: 80,
+  },
+  {
+    title: "满分",
+    dataIndex: "totalScore",
+    width: 150,
+  },
+  {
+    title: "最高分",
+    dataIndex: "maxScore",
+    width: 150,
+  },
+  {
+    title: "最低分",
+    dataIndex: "minScore",
+    width: 150,
+  },
+  {
+    title: "平均分",
+    dataIndex: "avgScore",
+    width: 150,
+  },
+  {
+    title: "标准差",
+    dataIndex: "stdev",
+    width: 150,
+  },
+  {
+    title: "差异系数",
+    dataIndex: "coefficient",
+    width: 150,
+  },
+  {
+    title: "难度",
+    dataIndex: "difficulty",
+    width: 150,
+  },
+  {
+    title: "区分度",
+    dataIndex: "discrimination",
+    width: 150,
+  },
+  {
+    title: "零分人数",
+    dataIndex: "zeroCount",
+    width: 150,
+  },
+  {
+    title: "满分人数 ",
+    dataIndex: "fullCount",
+    width: 150,
+  },
+  {
+    title: "有效卷数",
+    dataIndex: "effectiveCount",
+    width: 150,
+  },
+];
+
+function openModal() {
+  EventBus.emit("SHOW_SETTING", "DESCRIBE06");
+}
+</script>

+ 40 - 0
src/features/paperAnalysis/QuestionBianPai.vue

@@ -0,0 +1,40 @@
+<template>
+  <div>
+    <a-button @click="openModal">说明</a-button>
+
+    <a-table
+      row-key="id"
+      :columns="columns"
+      :data-source="props.questions"
+    ></a-table>
+  </div>
+</template>
+
+<script setup lang="ts">
+import EventBus from "@/plugins/eventBus";
+import { Question } from "@/types";
+
+const props = defineProps<{ questions: Question[] }>();
+
+const columns = [
+  {
+    title: "大题号",
+    dataIndex: "mainNumber",
+    width: 80,
+  },
+  {
+    title: "小题号",
+    dataIndex: "subNumber",
+    width: 80,
+  },
+  {
+    title: "难度",
+    dataIndex: "difficulty",
+    width: 150,
+  },
+];
+
+function openModal() {
+  EventBus.emit("SHOW_SETTING", "DESCRIBE05");
+}
+</script>

+ 87 - 0
src/features/paperAnalysis/QuestionDifficultyGroup.vue

@@ -0,0 +1,87 @@
+<template>
+  <div>
+    <a-button @click="openModal1">说明</a-button>
+    <a-button @click="openModal2">说明</a-button>
+
+    <table class="table">
+      <tr>
+        <th>大题号</th>
+        <th>小题号</th>
+        <!-- <th v-for="(item, index) in props" :key="index"></th> -->
+      </tr>
+      <tr v-for="(item, index) in props.questions" :key="index">
+        <td>{{ item.mainNumber }}</td>
+        <td>{{ item.subNumber }}</td>
+        <td v-for="(item, index) in item.difficulityLevel" :key="index">
+          {{ item }}
+        </td>
+      </tr>
+    </table>
+
+    <a-button @click="openRangeConfigModal">分段设置</a-button>
+    <CommonRangeConfig
+      ref="rangeConfigRef"
+      :project-id="projectId"
+      :course-id="selectedCourseId"
+      :range-config="selectedRangeConfig"
+      @updated="handleRangeConfigUpdate"
+    />
+    <!-- <table></table> -->
+  </div>
+</template>
+
+<script setup lang="ts">
+import { setPaperRangeConfig } from "@/api/paperAnalysisPage";
+import EventBus from "@/plugins/eventBus";
+import { Question } from "@/types";
+
+const props = defineProps<{
+  questions: Question[];
+  rangeConfig?: string;
+  projectId: number;
+  courseId: number;
+  paperId: number;
+}>();
+
+function openModal1() {
+  EventBus.emit("SHOW_SETTING", "DESCRIBE15");
+}
+
+function openModal2() {
+  EventBus.emit("SHOW_SETTING", "DESCRIBE16");
+}
+
+let rangeConfigRef = $ref(null);
+
+let selectedRangeConfig = $ref([]);
+let selectedCourseId = $ref(0);
+
+const openRangeConfigModal = () => {
+  selectedCourseId = props.courseId;
+  selectedRangeConfig = JSON.parse(props.rangeConfig || "0") || [
+    {
+      type: "ZERO",
+      baseScore: 0,
+      adjustScore: 0,
+    },
+  ];
+  // @ts-ignore
+  rangeConfigRef.showModal();
+};
+
+async function handleRangeConfigUpdate(rangeConfig: any) {
+  await setPaperRangeConfig({
+    projectId: props.projectId,
+    courseId: selectedCourseId,
+    paperId: props.paperId,
+    rangeConfig: JSON.stringify(rangeConfig),
+  });
+}
+</script>
+
+<style scoped>
+.table td,
+.table th {
+  border: 1px solid grey;
+}
+</style>

+ 132 - 0
src/features/paperAnalysis/QuestionTypeDifficulty.vue

@@ -0,0 +1,132 @@
+<template>
+  <div>
+    <a-button @click="importModalVisible = true">题型分布设置</a-button>
+    <a-button @click="openModal">说明</a-button>
+
+    <a-table
+      style="width: 100%; overflow-x: scroll"
+      row-key="id"
+      :columns="columns"
+      :data-source="props.questions"
+    ></a-table>
+
+    <a-modal
+      v-model:visible="importModalVisible"
+      title="批量科目导入"
+      @ok="handleImport"
+      ok-text="确定"
+      cancel-text="取消"
+    >
+      <a-form>
+        <a-form-item label="文件地址">
+          <input id="file-input" :multiple="false" type="file" />
+        </a-form-item>
+        <a-form-item label="下载模板">
+          <a-button @click="downloadTpl">下载模板</a-button>
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { importQuestionGroups } from "@/api/paperAnalysisPage";
+import EventBus from "@/plugins/eventBus";
+import { Question } from "@/types";
+import { downloadFileURL } from "@/utils/utils";
+import { message } from "ant-design-vue";
+
+const props = defineProps<{ questions: Question[]; projectId: number }>();
+
+const columns = [
+  {
+    title: "大题号",
+    dataIndex: "mainNumber",
+    width: 80,
+  },
+  {
+    title: "小题号",
+    dataIndex: "subNumber",
+    width: 80,
+  },
+  {
+    title: "满分",
+    dataIndex: "totalScore",
+    width: 150,
+  },
+  {
+    title: "最高分",
+    dataIndex: "maxScore",
+    width: 150,
+  },
+  {
+    title: "最低分",
+    dataIndex: "minScore",
+    width: 150,
+  },
+  {
+    title: "平均分",
+    dataIndex: "avgScore",
+    width: 150,
+  },
+  {
+    title: "标准差",
+    dataIndex: "stdev",
+    width: 150,
+  },
+  {
+    title: "差异系数",
+    dataIndex: "coefficient",
+    width: 150,
+  },
+  {
+    title: "难度",
+    dataIndex: "difficulty",
+    width: 150,
+  },
+  {
+    title: "区分度",
+    dataIndex: "discrimination",
+    width: 150,
+  },
+  {
+    title: "零分人数",
+    dataIndex: "zeroCount",
+    width: 150,
+  },
+  {
+    title: "满分人数 ",
+    dataIndex: "fullCount",
+    width: 150,
+  },
+  {
+    title: "有效卷数",
+    dataIndex: "effectiveCount",
+    width: 150,
+  },
+];
+
+function openModal() {
+  EventBus.emit("SHOW_SETTING", "DESCRIBE06");
+}
+
+/** <handleImport> */
+let importModalVisible = $ref<boolean>(false);
+async function handleImport() {
+  const files = (document.querySelector("#file-input") as HTMLInputElement)
+    .files;
+  const fileToImport = files && files[0];
+  if (!fileToImport) {
+    message.warn({ content: "请选择文件" });
+    return;
+  }
+
+  await importQuestionGroups(props.projectId, fileToImport);
+  message.success({ content: "导入成功" });
+}
+/** </handleImport> */
+
+async function downloadTpl() {
+  downloadFileURL("/api/ess/sasQuestionGroup/template");
+}
+</script>

+ 30 - 0
src/types/index.ts

@@ -7,3 +7,33 @@ export interface Role {
 export type Course_Type = "PUBLIC" | "MAJOR";
 
 export type Privilege_Type = "COURSE" | "ORG";
+
+export interface Question {
+  id: number;
+  createTime: string;
+  updateTime: string;
+  projectId: number;
+  courseId: number;
+  paperId: number;
+  questionName: string;
+  mainNumber: number;
+  subNumber: number;
+  subIndex: null;
+  objective: boolean;
+  totalScore: number;
+  maxScore: number;
+  minScore: number;
+  avgScore: number;
+  stdev: number;
+  coefficient: number;
+  difficulty: number;
+  discrimination: number;
+  zeroCount: number;
+  fullCount: number;
+  effectiveCount: number;
+  options: null;
+  optionLevel: null;
+  answer: null;
+  difficulityLevel: number[];
+  difficulityGroupLevel: number[];
+}