فهرست منبع

命题、分组、选项分析

liurongli 2 روز پیش
والد
کامیت
01e4f6fe0c

+ 41 - 0
src/api/analysis.ts

@@ -75,4 +75,45 @@ export const queryAnswerListByAnswerAndScore = (data: any): Promise<ApiResponse>
     method: "post",
     data,
   });
+};
+// ==========================================分组分析============================================
+export const questionGroupAnalysis = (data: any): Promise<ApiResponse> => {
+  return request({
+    url: "/api/v1/ai_analysis/questionGroupAnalysis",
+    method: "post",
+    data,
+  });
+};
+// ==========================================选项分析============================================
+// 客观题分析(选择题分析图)
+export const selectQuestionAnalysis = (data: any): Promise<ApiResponse> => {
+  return request({
+    url: "/api/v1/ai_analysis/selectQuestionAnalysis",
+    method: "post",
+    data,
+  });
+};
+// 选择题分析表-分页
+export const selectQuestionAnalysisPage = (data: any): Promise<ApiResponse> => {
+  return request({
+    url: "/api/v1/ai_analysis/selectQuestionAnalysisPage",
+    method: "post",
+    data,
+  });
+};
+//分页获取学生作答
+export const selectQuestionStudentPageList = (data: any): Promise<ApiResponse> => {
+  return request({
+    url: "/api/v1/ai_analysis/selectQuestionPageList",
+    method: "post",
+    data,
+  });
+};
+// ==========================================命题分析============================================
+export const propositionAnalysis = (data: any): Promise<ApiResponse> => {
+  return request({
+    url: "/api/v1/ai_analysis/propositionAnalysis",
+    method: "post",
+    data,
+  });
 };

BIN
src/assets/icon/difficulty_ratio.png


BIN
src/assets/icon/discrimination_ratio.png


BIN
src/assets/icon/paper_difficulty.png


BIN
src/assets/icon/test_paper_distinction.png


BIN
src/assets/icon/test_paper_reliability.png


+ 2 - 0
src/components/ReportModule.vue

@@ -316,6 +316,8 @@ onMounted(() => {
 
     .module_qita {
         width: 100%;
+        padding: 0 20px;
+        box-sizing: border-box;
         display: flex;
     }
 }

+ 6 - 1
src/components/echarts/barLineCharts.vue

@@ -402,7 +402,9 @@ function ChangeOrder(val: string) {
   );
   emit("ChangeChartOrder", val, legendData, selectedBarIndex);
 }
-
+const ChangeSelectValue = () => {
+  selectValue.value = "1";
+};
 // ==================== Watch ====================
 watch(
   () => props.data,
@@ -441,6 +443,9 @@ onBeforeUnmount(() => {
     .querySelectorAll("#tooltipContent, #x-axis-tooltip")
     .forEach((el) => el.remove());
 });
+defineExpose({
+  ChangeSelectValue
+});
 </script>
 
 <style lang="scss" scoped>

+ 537 - 0
src/components/echarts/lineChart.vue

@@ -0,0 +1,537 @@
+<template>
+  <div class="echart_content">
+    <div class="is_show_all" v-if="showCheckBox">
+      <el-checkbox
+        v-model="checkAll"
+        :indeterminate="isIndeterminate"
+        @change="toggleChangeAll"
+      >
+        显示全部
+      </el-checkbox>
+    </div>
+    <div
+      ref="lineChartRef"
+      class="chart_box"
+      :style="{ height: reportHeight || '400px' }"
+    ></div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
+import _ from "lodash";
+import * as echarts from "echarts";
+import { getScorePerformanceAnalysis } from "@/utils/common";
+
+// 定义 props 类型
+interface Props {
+  gridRight?: number;
+  gridTop?: number | string;
+  legendWidth?: number | string;
+  showCheckBox?: boolean;
+  extraText?: boolean;
+  datax?: string[];
+  datay?: number[][];
+  tooltipData?: any[]; // 复杂结构,可进一步定义
+  colors?: string[];
+  unit?: string;
+  title?: string[];
+  markNumber?: number[];
+  isSmooth?: boolean;
+  showBackground?: boolean;
+  isShowLabel?: boolean;
+  labelColor?: string;
+  showMarkPoint?: boolean;
+  legendList?: string[];
+  yInverse?: boolean;
+  updateKey?: number;
+  gridLeft?: number;
+  fontSize?: number | string;
+  fontColor?: string;
+  reportHeight?: string;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  gridRight: 0,
+  gridTop: "",
+  legendWidth: "auto",
+  showCheckBox: true,
+  extraText: true,
+  datax: () => ["G10", "G9", "G8", "G7", "G6", "G5", "G4", "G3", "G2", "G1"],
+  datay: () => [],
+  tooltipData: () => [],
+  colors: () => getScorePerformanceAnalysis(),
+  unit: "",
+  title: () => [],
+  markNumber: () => [],
+  isSmooth: false,
+  showBackground: true,
+  isShowLabel: false,
+  labelColor: "",
+  showMarkPoint: false,
+  legendList: () => [],
+  yInverse: false,
+  updateKey: 0,
+  gridLeft: 20,
+  fontSize: "",
+  fontColor: "",
+  reportHeight: "",
+});
+
+// 定义 emits
+const emit = defineEmits<{
+  (e: "SelectLegend", selected: string[]): void;
+}>();
+
+// DOM 引用
+const lineChartRef = ref<HTMLElement | null>(null);
+
+// 数据状态
+let echart: echarts.ECharts | null = null;
+const checkAll = ref(true);
+const isIndeterminate = ref(false);
+const legendSelected = ref<Record<string, boolean>>({});
+const legenSelectList = ref<string[]>([]);
+const legenAllList = ref<string[]>([]);
+
+// 辅助函数 - 辅助线配置
+const markLine = (index: number) => {
+  if (!props.markNumber.length) return {};
+  return {
+    data: [
+      {
+        type: "average" as const,
+        name: "平均值",
+        yAxis: props.markNumber[index] ?? null,
+      },
+    ],
+    label: {
+      position: "end",
+      formatter: (params: any) => {
+        return props.markNumber[index] ?? params.value;
+      },
+    },
+    lineStyle: {
+      type: "dashed",
+      color: props.colors[index],
+    },
+  };
+};
+
+// 加载 / 重绘图表
+const loadEchart = () => {
+  // 更新图例选择列表
+  legenSelectList.value =
+    props.legendList.length > 0 ? [...props.legendList] : [];
+  legenAllList.value = [...props.title];
+
+  // 销毁旧实例
+  if (echart) {
+    echart.dispose();
+    echart = null;
+  }
+
+  if (!lineChartRef.value) return;
+
+  // 初始化新实例(提高清晰度)
+  echart = echarts.init(lineChartRef.value, undefined, { devicePixelRatio: 2 });
+
+  // 初始化图例选中状态
+  legendSelected.value = {};
+  props.title.forEach((name) => {
+    legendSelected.value[name] = legenSelectList.value.includes(name);
+  });
+
+  // 更新全选按钮状态
+  checkAll.value = legenSelectList.value.length === legenAllList.value.length;
+  isIndeterminate.value =
+    legenSelectList.value.length > 0 &&
+    legenSelectList.value.length < legenAllList.value.length;
+
+  // 计算最大值/最小值用于Y轴刻度隐藏
+  const allValues = props.datay.flat();
+  const maxValue = Math.max(...allValues);
+  const minValue = Math.min(...allValues);
+  const hideTick = minValue < 0 || props.yInverse;
+
+  // 获取当前显示图例对应数据中的最大值(用于Y轴范围,原代码中注释未启用,这里保留计算)
+  // const visibleSeriesIndices = legenSelectList.value.map(name => props.title.indexOf(name))
+  // const visibleData = visibleSeriesIndices.filter(i => i !== -1).map(i => props.datay[i])
+  // const visibleMax = visibleData.length ? Math.max(...visibleData.flat()) : maxValue
+
+  const extraTextStr = props.extraText ? "占比" : "";
+  const dataCount = props.datax.length;
+  const containerWidth = lineChartRef.value.clientWidth;
+  const singleSeriesWidth = (containerWidth - 100) / dataCount;
+
+  let gridTop = props.showMarkPoint ? 45 : 20;
+  if (props.title.length > 0) gridTop = 70;
+  const option: echarts.EChartsOption = {
+    tooltip: {
+      trigger: "axis",
+      axisPointer: { type: "line" },
+      renderModel: "html",
+      confine: true,
+      extraCssText:
+        "border-radius: 4px;padding:5px 0px 5px 5px;white-space:normal;word-warp:break-word;max-width: 400px;",
+      enterable: true,
+      formatter: (params: any) => {
+        if (!params || !params.length) return "";
+        let tooltip = `<div class='tooltip_content'>`;
+        tooltip += `<div class='tooltip_title'>${params[0].name}</div>`;
+        params.forEach((item: any, idx: number) => {
+          const name = item.seriesName;
+          const rate = item.value + props.unit;
+          if (props.tooltipData.length > 0) {
+            const tooltipItem =
+              props.tooltipData?.[item.seriesIndex]?.[item.dataIndex] || {};
+            tooltip += `<div class='tooltip_student'>
+                            <div class='tooltip_line_icon' style='background:${item.color}'></div>
+                            <div class='tooltip_student_name'>${tooltipItem.name}:${tooltipItem.value}</div>
+                        </div>`;
+          } else {
+            tooltip += `<div class='tooltip_student'>
+                            <div class='tooltip_line_icon' style='background:${item.color}'></div>
+                            <div class='tooltip_student_name'>${name}${extraTextStr}:${rate || "-"}</div>
+                        </div>`;
+          }
+        });
+        tooltip += `</div>`;
+        return tooltip;
+      },
+    },
+    legend: {
+      show: props.title.length > 0,
+      top:0,
+      left: props.showCheckBox ? "110px" : "0px",
+      width: props.legendWidth,
+      itemGap: 20,
+      itemHeight: 10,
+      itemWidth: 25,
+      data: props.title,
+      textStyle: {
+        fontSize: 12,
+        color: "#333",
+        lineHeight: 30,
+      },
+      selectedMode: true,
+      selected: legendSelected.value,
+      type: "scroll",
+      pageIconColor: "#999",
+      pageIconInactiveColor: "#ccc",
+      pageButtonItemGap: 5,
+      pageButtonPosition: "end",
+      orient: "horizontal",
+      alignTo: "none",
+    },
+    grid: {
+      left: props.gridLeft,
+      right: props.gridRight,
+      top: props.gridTop || gridTop,
+      bottom: 0,
+      containLabel: true,
+    },
+    xAxis: {
+      type: "category",
+      data: props.datax,
+      boundaryGap: true,
+      triggerEvent: true,
+      axisTick: {
+        alignWithLabel: true,
+        show: !hideTick,
+        inside: false,
+        length: 8,
+      },
+      axisLine: { show: !props.yInverse },
+      axisLabel: {
+        interval: 0,
+        rotate: singleSeriesWidth < 60 ? 45 : 0,
+        fontSize: props.fontSize ? Number(props.fontSize) : 14,
+        color: props.fontColor || "#666",
+        fontWeight: 400,
+        formatter: (value: string) => {
+          const valueWidth = value.length * 14;
+          if (valueWidth > singleSeriesWidth) {
+            if (singleSeriesWidth < 80) return value;
+            const maxLength = Math.floor(singleSeriesWidth / 14);
+            return value.slice(0, maxLength) + "...";
+          }
+          return value;
+        },
+      },
+    },
+    yAxis: {
+      type: "value",
+      axisLabel: {
+        margin: 20,
+        formatter: `{value}${props.unit}`,
+        interval: 0,
+        fontSize: props.fontSize ? Number(props.fontSize) : 14,
+        color: props.fontColor || "#666",
+        fontWeight: 400,
+      },
+      splitNumber: 5,
+      axisTick: { interval: 0 },
+      splitLine: {
+        show: true,
+        lineStyle: { color: "#E4E7ED", width: 1, type: "solid" },
+      },
+      splitArea: {
+        show: true,
+        areaStyle: { color: ["#fafafa", "#ffffff"] },
+      },
+      inverse: props.yInverse,
+    },
+    graphic: {
+      elements: [
+        {
+          type: "group",
+          key: "mark-point-group",
+          z: 10,
+        },
+      ],
+    },
+    series: props.datay.map((data, index) => ({
+      type: "line",
+      name: props.title[index],
+      symbol: "emptyCircle",
+      showSymbol: true,
+      symbolSize: 8,
+      label: {
+        show: props.isShowLabel,
+        color: props.labelColor || props.colors[index],
+        formatter: `{c}${props.unit}`,
+      },
+      smooth: props.isSmooth,
+      markLine: markLine(index),
+      markPoint: props.showMarkPoint
+        ? {
+            data: [
+              { type: "max", name: "最大值", label: { color: "#fff" } },
+              { type: "min", name: "最小值", label: { color: "#fff" } },
+            ],
+          }
+        : undefined,
+      itemStyle: {
+        color: props.colors[index],
+        borderColor: props.colors[index],
+        borderWidth: 1,
+        shadowColor: "rgba(0, 0, 0, .1)",
+        shadowBlur: 0,
+      },
+      lineStyle: { width: 3 },
+      areaStyle: props.showBackground
+        ? {
+            color: {
+              type: "linear",
+              x: 0,
+              y: 0,
+              x2: 0,
+              y2: 1,
+              colorStops: [
+                { offset: 0, color: props.colors[index] + "30" },
+                { offset: 1, color: props.colors[index] + "00" },
+              ],
+            },
+          }
+        : undefined,
+      emphasis: {
+        focus: "series",
+        blurScope: "coordinateSystem",
+      },
+      data: data,
+    })),
+  };
+
+  echart.setOption(option);
+
+  // X轴文字过长悬浮提示
+  setupXAxisTooltip();
+
+  // 绑定图例切换事件
+  echart.on("legendselectchanged", handleLegendSelectChanged);
+};
+
+// X轴标签悬浮显示完整文本
+const setupXAxisTooltip = () => {
+  let tooltipDiv = document.getElementById("x-axis-tooltip");
+  if (!tooltipDiv) {
+    tooltipDiv = document.createElement("div");
+    tooltipDiv.id = "x-axis-tooltip";
+    tooltipDiv.style.cssText = `
+            position: absolute;
+            z-index: 9999;
+            background: #303133;
+            color: #fff;
+            padding: 6px 10px;
+            border-radius: 4px;
+            font-size: 12px;
+            pointer-events: none;
+            display: none;
+            white-space: nowrap;
+        `;
+    document.body.appendChild(tooltipDiv);
+  }
+
+  if (!echart) return;
+  echart.on("mouseover", (params: any) => {
+    if (params.componentType === "xAxis") {
+      const value = params.value;
+      if (value && value.length > 5) {
+        tooltipDiv!.style.display = "block";
+        tooltipDiv!.innerText = value;
+      }
+    }
+  });
+  echart.on("mouseout", (params: any) => {
+    if (params.componentType === "xAxis") {
+      tooltipDiv!.style.display = "none";
+    }
+  });
+
+  const container = lineChartRef.value;
+  if (container) {
+    container.addEventListener("mousemove", (e) => {
+      if (tooltipDiv && tooltipDiv.style.display === "block") {
+        tooltipDiv.style.left = `${e.pageX + 10}px`;
+        tooltipDiv.style.top = `${e.pageY + 15}px`;
+      }
+    });
+  }
+};
+
+// 图例切换处理
+const handleLegendSelectChanged = (params: any) => {
+  if (!echart) return;
+  const option = echart.getOption();
+  const legendData = (option.legend as any)[0].data as string[];
+  const selected = params.selected;
+  legendSelected.value = selected;
+
+  const allSelected = legendData.every((seriesName) => selected[seriesName]);
+  const noneSelected = legendData.every((seriesName) => !selected[seriesName]);
+  const someSelected = !allSelected && !noneSelected;
+
+  if (noneSelected) checkAll.value = false;
+  if (allSelected) checkAll.value = true;
+  isIndeterminate.value = someSelected;
+
+  const legendSelectedList = Object.keys(selected).filter(
+    (key) => selected[key],
+  );
+  emit("SelectLegend", legendSelectedList);
+};
+
+// 全选 / 全不选按钮点击
+const toggleChangeAll = () => {
+  if (!props.showCheckBox) return;
+  if (!echart) return;
+  const option = echart.getOption();
+  const legendData = (option.legend as any)[0].data as string[];
+  legendData.forEach((seriesName) => {
+    echart!.dispatchAction({
+      type: checkAll.value ? "legendSelect" : "legendUnSelect",
+      name: seriesName,
+    });
+  });
+  isIndeterminate.value = false;
+
+  const legendSelectedList = checkAll.value ? [...props.title] : [];
+  emit("SelectLegend", legendSelectedList);
+};
+
+// 窗口自适应(节流)
+const handleResize = _.throttle(() => {
+  nextTick(() => {
+    loadEchart();
+  });
+}, 500);
+
+// 监听 props 变化
+watch(
+  () => props.legendList,
+  () => loadEchart(),
+  { deep: true },
+);
+watch(
+  () => props.updateKey,
+  () => loadEchart(),
+);
+watch(
+  () => props.datay,
+  () => loadEchart(),
+  { deep: true },
+);
+
+// 生命周期
+onMounted(() => {
+  loadEchart();
+  window.addEventListener("resize", handleResize);
+});
+
+onBeforeUnmount(() => {
+  window.removeEventListener("resize", handleResize);
+  if (echart) {
+    echart.dispose();
+    echart = null;
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+//图标外层公共样式
+.echart_content {
+  position: relative;
+  width: 100%;
+  margin: auto;
+  min-height: 360px;
+  height: auto;
+  // overflow-x: auto;
+  .is_show_all {
+    position: absolute;
+    top: 10px;
+    left: 5px;
+  }
+}
+
+//弹窗悬浮层样式更改
+.echart_content::-webkit-scrollbar {
+  width: 100%;
+  height: 8px;
+  /* 设置滚动条宽度为8像素 */
+  background-color: transparent;
+}
+
+/* 滑块样式 */
+.echart_content::-webkit-scrollbar-thumb {
+  background-color: #b8b8b8;
+  /* 设置滑块颜色为深灰色 */
+  border-radius: 4px;
+  /* 设置滑块边角半径为4像素 */
+  min-height: 40px; //设置手柄最小高度
+}
+
+/* 滚动条轨道内部空白区域样式 */
+.echart_content::-webkit-scrollbar-track {
+  background-color: #f0f0f0;
+  /* 设置轨道背景色为浅灰色 */
+}
+
+/* 滚动条两端按钮样式 */
+.echart_content::-webkit-scrollbar-button {
+  display: none;
+  /* 不显示按钮 */
+}
+
+/* 交叉点处的区域样式 */
+.echart_content::-webkit-scrollbar-corner {
+  background-color: transparent;
+  /* 设置交叉点处的背景色为透明 */
+}
+
+/* 调整大小手柄样式 */
+.echart_content::-webkit-resizer {
+  display: none;
+  /* 不显示调整大小手柄 */
+}
+</style>

+ 153 - 0
src/components/echarts/pieChart.vue

@@ -0,0 +1,153 @@
+<template>
+  <!-- 饼图 -->
+  <div ref="pieChart" :style="{ height: `${height}px` }" class="chart_width"></div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted, watch, onBeforeUnmount } from 'vue'
+import * as echarts from 'echarts'
+import throttle from 'lodash/throttle'
+import { getScorePerformanceAnalysis } from '@/utils/common'
+
+// 定义 props 类型
+interface Props {
+  height?: string | number
+  color?: string[]
+  data?: Array<{ value: number; name: string }>
+  refresh?: boolean
+  position?: 'outside' | 'inside'
+  unit?: string
+  radius?: string
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  height: '',
+  color: () => getScorePerformanceAnalysis(),
+  data: () => [],
+  refresh: false,
+  position: 'outside',
+  unit: '%',
+  radius: '80%'
+})
+
+// 定义 emits
+const emit = defineEmits<{
+  (e: 'refresh', value: boolean): void
+}>()
+
+// 模板引用
+const pieChart = ref<HTMLElement | null>(null)
+let myChart: echarts.ECharts | null = null
+
+// 加载图表的函数
+const LoadEchart = (): void => {
+  if (myChart) {
+    myChart.dispose()
+  }
+  if (!pieChart.value) return
+
+  myChart = echarts.init(pieChart.value)
+
+  const option: echarts.EChartsOption = {
+    tooltip: {
+      show: false,
+      trigger: 'item'
+    },
+    // 调色板
+    color: props.color && props.color.length > 0 ? props.color : getScorePerformanceAnalysis(),
+    legend: {
+      show: false
+    },
+    series: []
+  }
+
+  if (props.position === 'outside') {
+    option.series = [
+      {
+        type: 'pie',
+        radius: props.radius,
+        center: ['50%', '50%'],
+        avoidLabelOverlap: true,
+        label: {
+          position: 'outside',
+          color: '#666666',
+          formatter: `{b}: {d}${props.unit}`,
+          fontSize: 14
+        },
+        labelLine: {
+          show: true
+        },
+        data: props.data
+      }
+    ]
+  } else {
+    option.series = [
+      {
+        type: 'pie',
+        radius: props.radius,
+        center: ['50%', '50%'],
+        avoidLabelOverlap: false,
+        label: {
+          position: 'inside',
+          color: '#fff',
+          formatter: `{b}: {d}${props.unit}`,
+          fontSize: 14
+        },
+        data: props.data
+      }
+    ]
+  }
+
+  myChart.setOption(option, true)
+  emit('refresh', false)
+
+  // 注意:原逻辑每次 LoadEchart 都会添加 resize 监听,会导致监听器重复添加。
+  // 为保持逻辑完全一致,此处保留原行为。建议实际使用时将监听器移至 onMounted 中只添加一次。
+  window.addEventListener('resize', handleResize)
+}
+
+// 节流处理窗口大小变化
+const handleResize = throttle(() => {
+  LoadEchart()
+}, 500)
+
+// 监听 data 变化(深度监听)
+watch(
+  () => props.data,
+  () => {
+    LoadEchart()
+  },
+  { deep: true }
+)
+
+// 监听 refresh 变化
+watch(
+  () => props.refresh,
+  (value) => {
+    if (value) {
+      LoadEchart()
+    }
+  }
+)
+
+// 生命周期
+onMounted(() => {
+  LoadEchart()
+})
+
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', handleResize)
+  if (myChart) {
+    myChart.dispose()
+    myChart = null
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.chart_width {
+  width: 100%;
+  height: 300px; // 设置图表高度
+  overflow: hidden;
+}
+</style>

+ 328 - 153
src/views/analysis/groupAnalysis.vue

@@ -74,6 +74,80 @@
       展示每道试题的得分率图。得分率指实际得分/考核分的比值,换算成的百分数。可以用于分析每道试题的难易程度和质量,试题得分率高意味着试题难度低或者学生整体水平高;得分率低意味着试题难度较高或者考生整体水平较低。点击每道试题的柱或雷达图的题号可在下方查看该题每个班的得分率情况。
     </template>
   </ReportModule>
+  <ReportModule
+    :showTitle="true"
+    :titleList="[state.groupTitle, state.groupName]"
+    :showDescribe="true"
+    tableOrChart="chart"
+    :showPrintBtn="false"
+    :showExportBtn="false"
+  >
+    <template #title_right>
+      <EchartType
+        :chartTypeList="state.majorQuestionData.chartTypeList"
+        :current="state.majorQuestionData.chartType"
+        @ChangeEchartType="(val) => ChangeEchartType(val, 'majorQuestionData')"
+      />
+    </template>
+    <template #module_table_chart>
+      <template v-if="state.majorQuestionData.data.length > 0">
+        <BarLineCharts
+          ref="majorQuestionCharts"
+          v-if="state.majorQuestionData.chartType == 'line_bar_chart'"
+          :legendList="state.majorQuestionData.legendList"
+          :showBarLegendIndex="state.majorQuestionData.showBarLegendIndex"
+          title="得分率"
+          :data="state.majorQuestionData.data"
+          @HandleChartClick="HandleQuestionChartClick"
+          @ChangeChartOrder="
+            (sortType, legendData, barIndex) =>
+              ChangeChartOrder(sortType, legendData, barIndex, 2)
+          "
+        >
+        </BarLineCharts>
+        <BarsCharts
+          ref="majorQuestionCharts"
+          :key="state.chartKey"
+          v-if="state.majorQuestionData.chartType == 'vertical_bar'"
+          :data="state.majorQuestionData.data"
+          :showSortSelectbox="true"
+          :legendList="state.majorQuestionData.legendList"
+          unit="%"
+          title="得分率"
+          :isClick="true"
+          @HandleChartClick="HandleQuestionChartClick"
+          @ChangeChartOrder="
+            (sortType, legendData, barIndex) =>
+              ChangeChartOrder(sortType, legendData, barIndex, 2)
+          "
+        ></BarsCharts>
+        <RadarCharts
+          v-if="state.majorQuestionData.chartType == 'radar_chart'"
+          :key="state.chartKey"
+          :data="state.majorQuestionData.data"
+          :legendList="state.majorQuestionData.legendList"
+          :showCheckBox="true"
+          :openShowAllLegend="true"
+          :isClick="true"
+          @HandleChartClick="HandleQuestionChartClick"
+        >
+        </RadarCharts>
+      </template>
+      <div
+        v-else
+        class="no_content_data"
+        v-loading="state.dataLoading"
+        :element-loading-text="state.loadingText"
+        element-loading-spinner="el-icon-loading"
+        element-loading-background="#ffffff"
+      >
+        <span>暂无数据</span>
+      </div>
+    </template>
+    <template #module_describe>
+      展示每道试题的得分率图。得分率指实际得分/考核分的比值,换算成的百分数。可以用于分析每道试题的难易程度和质量,试题得分率高意味着试题难度低或者学生整体水平高;得分率低意味着试题难度较高或者考生整体水平较低。点击每道试题的柱或雷达图的题号可在下方查看该题每个班的得分率情况。
+    </template>
+  </ReportModule>
   <ReportModule
     :showTitle="true"
     :titleList="[state.groupTitle, state.questionTitle]"
@@ -230,8 +304,8 @@
       </el-button>
     </template>
     <template #module_qita>
-      <div class="content_left card"></div>
-      <div class="content_right card table_42">
+      <div class="content_left paper_card"></div>
+      <div class="content_right paper_card table_42">
         <el-table
           border
           :data="state.majorAnswerData.tableData"
@@ -382,7 +456,7 @@ import ReportModule from "@/components/ReportModule.vue";
 import EchartType from "@/components/EchartType.vue";
 import { useAnalysisStore } from "@/store/analysis";
 import {
-  questionAnalysis,
+  questionGroupAnalysis,
   queryAnswerListByAnswerAndScore,
 } from "@/api/analysis";
 import BarLineCharts from "@/components/echarts/barLineCharts.vue"; //柱状图折线图组合图组件
@@ -465,6 +539,20 @@ const state = reactive({
   }, //小题分析数据
   majorQuestionData: {
     chartType: "line_bar_chart", //默认显示折线图柱状图line_bar_chart
+    chartTypeList: [
+      {
+        label: "组合图",
+        value: "line_bar_chart",
+      },
+      {
+        label: "柱状图",
+        value: "vertical_bar",
+      },
+      {
+        label: "雷达图",
+        value: "radar_chart",
+      },
+    ],
     legendList: [],
     defaultLegendList: [],
     showBarLegendIndex: 1,
@@ -648,147 +736,165 @@ const state = reactive({
   cardQuestionId: "", // 批量查看答题卡试题id
   cardRegistrationCodeList: [], // 批量查看答题卡学生账号数组
 });
+const majorQuestionCharts = ref(null);
 const majorTable = ref(null);
 //排序
 const ChangeChartOrder = (sortType, legendData, barIndex, number) => {
-  const isHasEstimatedScore = state.problemAnalysisData.headerList.find(
-    (item) => item.prop == "estimatedScore",
-  ); //是否存在预估满分
-  const fullVolume = state.problemAnalysisData.questionList.filter(
-    (item) => item.showCode == 999,
-  ); //全卷
-  const questionList = state.problemAnalysisData.questionList.filter(
-    (item) => item.showCode != 999,
-  );
-  const newBarIndex = isHasEstimatedScore ? barIndex - 1 : barIndex;
-  if (sortType == "2") {
-    //从低到高
-    questionList.sort(function (a, b) {
-      return (
-        (a?.classList?.[newBarIndex]?.questionStats?.scoreRate || 0) -
-        (b?.classList?.[newBarIndex]?.questionStats?.scoreRate || 0)
-      );
+  if (number == 1) {
+    const fullVolume = state.problemAnalysisData.groupList.filter(
+      (item) => item.groupCode == 999,
+    ); //全卷
+    const groupList = state.problemAnalysisData.groupList.filter(
+      (item) => item.groupCode != 999,
+    );
+    //题目分组排序
+    if (sortType == "2") {
+      //从低到高
+      groupList.sort(function (a, b) {
+        return (
+          (a?.classList?.[barIndex]?.questionStats?.scoreRate || 0) -
+          (b?.classList?.[barIndex]?.questionStats?.scoreRate || 0)
+        );
+      });
+    } else if (sortType == "3") {
+      //从高到低
+      groupList.sort(function (a, b) {
+        return (
+          (b?.classList?.[barIndex]?.questionStats?.scoreRate || 0) -
+          (a?.classList?.[barIndex]?.questionStats?.scoreRate || 0)
+        );
+      });
+    } else {
+      //默认排序 按题号排序
+      groupList.sort(function (a, b) {
+        return (a?.groupCode || 0) - (b.groupCode || 0);
+      });
+    }
+    state.problemAnalysisData.groupList = [...groupList, ...fullVolume];
+    state.problemAnalysisData.data = [];
+    state.problemAnalysisData.groupList.forEach((group) => {
+      const scoreRateArr =
+        group.classList?.map((item) => item?.questionStats?.scoreRate ?? 0) ||
+        [];
+      state.problemAnalysisData.data.push([group.groupName, ...scoreRateArr]);
     });
-  } else if (sortType == "3") {
-    //从高到低
-    questionList.sort(function (a, b) {
-      return (
-        (b?.classList?.[newBarIndex]?.questionStats?.scoreRate || 0) -
-        (a?.classList?.[newBarIndex]?.questionStats?.scoreRate || 0)
-      );
+    let chartTitle = ["group"];
+    state.problemAnalysisData.changeHeaderList.forEach((item) => {
+      chartTitle.push(item.label);
     });
+    state.problemAnalysisData.data.unshift(chartTitle); // 柱状图 图例标题
+    state.problemAnalysisData.data.pop(); // 删除最后一行 全卷
+    state.problemAnalysisData.legendList = legendData;
+    //大题分析 /阅读表达
+    GetClassQuestionData(0, state.problemAnalysisData.groupList[0].groupName);
   } else {
-    //默认排序 按题号排序
-    questionList.sort(function (a, b) {
-      return (a?.showCode || 0) - (b.showCode || 0);
-    });
-  }
-  state.problemAnalysisData.questionList = [...questionList, ...fullVolume];
-  let chartData = [],
-    chartTitle = [];
-  state.problemAnalysisData.questionList.forEach((ques) => {
-    const scoreRateArr = ques.classList.map(
-      (item) => item?.questionStats?.scoreRate || 0,
+    const fullVolume = state.problemAnalysisData.questionList.filter(
+      (item) => item.showCode == 999,
+    ); //全卷
+    const questionList = state.problemAnalysisData.questionList.filter(
+      (item) => item.showCode != 999,
     );
-    if (isHasEstimatedScore) {
-      chartData.push([
-        ques.questionName,
-        ques.estimatedScoreRate,
-        ...scoreRateArr,
-      ]);
+    if (sortType == "2") {
+      //从低到高
+      questionList.sort(function (a, b) {
+        return (
+          (a?.classList?.[barIndex]?.questionStats?.scoreRate || 0) -
+          (b?.classList?.[barIndex]?.questionStats?.scoreRate || 0)
+        );
+      });
+    } else if (sortType == "3") {
+      //从高到低
+      questionList.sort(function (a, b) {
+        return (
+          (b?.classList?.[barIndex]?.questionStats?.scoreRate || 0) -
+          (a?.classList?.[barIndex]?.questionStats?.scoreRate || 0)
+        );
+      });
     } else {
-      chartData.push([ques.questionName, ...scoreRateArr]);
+      //默认排序 按题号排序
+      questionList.sort(function (a, b) {
+        return (a?.showCode || 0) - (b.showCode || 0);
+      });
     }
-  });
-  const newChangeHeaderList = isHasEstimatedScore
-    ? [
-        { ...isHasEstimatedScore, isG: false, type: 1 },
-        ...state.problemAnalysisData.changeHeaderList,
-      ]
-    : [...state.problemAnalysisData.changeHeaderList];
-  newChangeHeaderList.forEach((item) => {
-    chartTitle.push(item.label);
-  });
-  chartTitle.unshift("group");
-  chartData.unshift(chartTitle); // 柱状图 图例标题
-  chartData.pop(); // 删除最后一行 全卷
-  state.problemAnalysisData.data = chartData;
-  state.problemAnalysisData.legendList = legendData;
-  //小题分析 /第N题
-  GetQuestionStatsData(0);
+    state.problemAnalysisData.questionList = [...questionList, ...fullVolume];
+
+    //Y轴数据
+    state.majorQuestionData.data = [];
+    state.problemAnalysisData.questionList.forEach((question) => {
+      const scoreRateArr = question.classList.map(
+        (item) => item?.questionStats?.scoreRate || 0,
+      );
+      state.majorQuestionData.data.push([
+        question.questionName,
+        ...scoreRateArr,
+      ]);
+    });
+    let chartTitle = [];
+    state.problemAnalysisData.changeHeaderList.forEach((item) => {
+      chartTitle.push(item.label);
+    });
+    chartTitle.unshift("group");
+    state.majorQuestionData.legendList = legendData;
+    state.majorQuestionData.data.unshift(chartTitle); // 柱状图 图例标题
+    //大题分析 /阅读表达 / 第32题
+    GetQuestionStatsData(0);
+  }
 };
 //试题图表切换公共方法
 const ChangeEchartType = (value, prop) => {
   if (prop == "problemAnalysisData") {
     ChangeChartOrder("1", state.problemAnalysisData.defaultLegendList, "", 1);
+  } else if (prop == "majorQuestionData") {
+    ChangeChartOrder("1", state.majorQuestionData.defaultLegendList, "", 2);
   }
   state[prop].chartType = value;
 };
-//获取小题分析数据
-const GetQuestionAnalysisData = () => {
-  state.problemAnalysisData.data = []; //柱状图折线图 X轴
+//题目分组分析
+const GetQuestionGroupAnalysisData = () => {
+  state.problemAnalysisData.data = []; //柱状图折线图
   state.dataLoading = true;
-  questionAnalysis({
+  questionGroupAnalysis({
     ...analysisStore.filterObject,
-    analysisType: 0, //0-小题分析 1大题分析 2-知识点 3-能力点 question_group_code(4,5,6,7,8) 11-题型分析
+    analysisType: 4,
   })
     .then((res) => {
-      if (
-        res.code == 200 &&
-        res.data &&
-        res.data.questionList &&
-        res.data.questionList.length
-      ) {
+      // console.log("打印题目分组分析接口返回数据",res)
+      state.problemAnalysisData.data = []; //柱状图折线图
+      if (res.code == 200 && res.data && res.data.groupList.length) {
         const { data } = res;
-        const questionList = data.questionList || [];
+        const groupList = data.groupList || [];
         const changeHeaderList = data.changeHeaderList || []; //柱状图折线图 班级名称
-        state.problemAnalysisData.groupList = [];
-        state.problemAnalysisData.groupTableList = [];
-        state.problemAnalysisData.questionList = questionList;
-        state.problemAnalysisData.questionTableList = cloneDeep(questionList);
+        state.problemAnalysisData.groupList = groupList;
+        state.problemAnalysisData.groupTableList = cloneDeep(groupList);
         state.problemAnalysisData.headerList = data.headerList || [];
         state.problemAnalysisData.changeHeaderList = changeHeaderList;
         state.problemAnalysisData.childHeaderList = data.childHeaderList || [];
 
         state.chartKey++;
         //Y轴数据
-        let chartData = [];
-        let legendList = [];
-        const isHasEstimatedScore = state.problemAnalysisData.headerList.find(
-          (item) => item.prop == "estimatedScore",
-        ); //是否存在预估满分
-        chartData = questionList.map((item) => {
-          return isHasEstimatedScore
-            ? [item.questionName, item.estimatedScoreRate]
-            : [item.questionName];
+        state.problemAnalysisData.data = groupList.map((item) => {
+          return [item.groupName];
         });
-        questionList.forEach((ques, key) => {
-          const scoreRateArr = ques.classList.map(
-            (item) => item?.questionStats?.scoreRate || 0,
-          );
-          chartData[key].push(...scoreRateArr);
+
+        groupList.forEach((group, key) => {
+          const scoreRateArr =
+            group.classList?.map(
+              (item) => item?.questionStats?.scoreRate ?? 0,
+            ) || [];
+          state.problemAnalysisData.data[key].push(...scoreRateArr);
         });
         let chartTitle = [],
           titleType = [],
           classSelectLegend = [];
-        const newChangeHeaderList = isHasEstimatedScore
-          ? [
-              { ...isHasEstimatedScore, isG: false, type: 1 },
-              ...changeHeaderList,
-            ]
-          : [...changeHeaderList];
-        newChangeHeaderList.forEach((item) => {
+        changeHeaderList.forEach((item) => {
           chartTitle.push(item.label);
           titleType.push(item.type ? item.type : ""); //1柱状图 2折线
-          if (
-            item.prop != "0" &&
-            item.prop != "estimatedScore" &&
-            item.prop.indexOf("school_group") == -1
-          ) {
+          if (item.prop != "0" && item.prop.indexOf("school_group") == -1) {
             classSelectLegend.push(item.label);
           }
         });
         chartTitle.unshift("group");
+
         if (analysisStore.filterObject.classLevel != 2) {
           //0 年级 1 组合班级 2具体班级
           let startIndex = 1,
@@ -805,30 +911,31 @@ const GetQuestionAnalysisData = () => {
             startIndex = barFirtIndex;
             endIndex = barFirtIndex + 2;
           }
-          legendList =
+          state.problemAnalysisData.legendList =
             chartTitle.length > 1 ? chartTitle.slice(startIndex, endIndex) : [];
           state.problemAnalysisData.showBarLegendIndex = endIndex - 2;
         } else {
           //单班 联校和组合学校不默认显示
-          legendList = classSelectLegend;
+          state.problemAnalysisData.legendList = classSelectLegend;
           state.problemAnalysisData.showBarLegendIndex =
             changeHeaderList.length - 1;
         }
-        chartData.unshift(chartTitle); // 柱状图 图例标题
-        chartData.pop(); // 删除最后一行 全卷
-        state.problemAnalysisData.data = chartData;
-        state.problemAnalysisData.legendList = legendList;
-        state.problemAnalysisData.defaultLegendList = cloneDeep(legendList);
-        //小题分析 /第N题
-        GetQuestionStatsData(0);
-        // 获取项目分析表
+        state.problemAnalysisData.defaultLegendList = cloneDeep(
+          state.problemAnalysisData.legendList,
+        );
+        state.problemAnalysisData.data.unshift(chartTitle); // 柱状图 图例标题
+        state.problemAnalysisData.data.pop(); // 删除最后一行 全卷
+
+        //大题分析 /阅读表达
+        GetClassQuestionData(0, groupList[0].groupName);
+        // 大题分析表
         GetMajorTableData();
       } else {
+        state.questionTypesData.data = [];
+        state.questionTypesData.tableData = [];
+        state.problemAnalysisData.showBarLegendIndex = 1;
         state.problemAnalysisData.groupList = [];
-        state.problemAnalysisData.groupTableList = [];
         state.problemAnalysisData.questionList = [];
-        state.problemAnalysisData.questionTableList = [];
-        state.problemAnalysisData.showBarLegendIndex = 1;
         state.problemAnalysisData.headerList = [];
         state.problemAnalysisData.changeHeaderList = [];
         state.problemAnalysisData.childHeaderList = [];
@@ -878,6 +985,13 @@ const GetQuestionAnalysisData = () => {
 };
 // 点击柱状图折线图
 const HandleChartClick = (index, name) => {
+  if (majorQuestionCharts.value) {
+    majorQuestionCharts.value.ChangeSelectValue();
+  }
+  GetClassQuestionData(index, name); //题目分组分析
+};
+//大题分析 /阅读表达 点击柱状图
+const HandleQuestionChartClick = (index, name) => {
   GetQuestionStatsData(index); //小题分析
 };
 // 获取小题分析 /第N题
@@ -972,10 +1086,67 @@ const GetQuestionStatsData = (index) => {
       dataStackY,
     ];
   }
-  console.log("打印questionScoreStatsData1111", state.questionScoreStatsData);
   // 获取小题分析 /第N题 / 第N班
   GetQuestionAnswerData(state.problemAnalysisData.showBarLegendIndex);
 };
+//大题分析 /阅读表达
+const GetClassQuestionData = (index, name) => {
+  state.majorQuestionData.data = [];
+  state.groupName = name;
+  const groupList = state.problemAnalysisData.groupList[index];
+  const questionList = groupList.questionList;
+  state.problemAnalysisData.questionList = questionList;
+  //Y轴数据
+  state.majorQuestionData.data = questionList.map((item) => {
+    return [item.questionName];
+  });
+  questionList.forEach((question, key) => {
+    const scoreRateArr = question.classList.map(
+      (item) => item?.questionStats?.scoreRate || 0,
+    );
+    state.majorQuestionData.data[key].push(...scoreRateArr);
+  });
+  let chartTitle = [],
+    titleType = [],
+    classSelectLegend = [];
+  state.problemAnalysisData.changeHeaderList.forEach((item) => {
+    chartTitle.push(item.label);
+    titleType.push(item.type ? item.type : ""); //1柱状图 2折线
+    if (item.prop != "0" && item.prop.indexOf("school_group") == -1) {
+      classSelectLegend.push(item.label);
+    }
+  });
+  chartTitle.unshift("group");
+  if (analysisStore.filterObject.classLevel != 2) {
+    //0 年级 1 组合班级 2具体班级
+    let startIndex = 1,
+      endIndex = 3;
+    if (analysisStore.filterObject.schoolLevel == 2) {
+      //0-联考 1-学校分组 2-具体学校
+      startIndex =
+        titleType.lastIndexOf(1) > -1 ? titleType.lastIndexOf(1) + 1 : 1;
+      endIndex = titleType.indexOf(2) > -1 ? titleType.indexOf(2) + 2 : 3; //单校时显示联校、组合校、年级是柱子;班级组合、班级是折线。
+    } else {
+      //联校 组合学校 默认当前的为第一个选中的柱子
+      const barFirtIndex =
+        titleType.lastIndexOf(1) > -1 ? titleType.lastIndexOf(1) + 1 : 1;
+      startIndex = barFirtIndex;
+      endIndex = barFirtIndex + 2;
+    }
+    state.majorQuestionData.legendList =
+      chartTitle.length > 1 ? chartTitle.slice(startIndex, endIndex) : [];
+    state.majorQuestionData.showBarLegendIndex = endIndex - 2;
+  } else {
+    //联校和组合学校不默认显示
+    state.majorQuestionData.legendList = classSelectLegend;
+  }
+  state.majorQuestionData.defaultLegendList = cloneDeep(
+    state.majorQuestionData.legendList,
+  );
+  state.majorQuestionData.data.unshift(chartTitle); // 柱状图 图例标题
+  //大题分析 /阅读表达 / 第32题
+  GetQuestionStatsData(0);
+};
 //切换 获取小题分析 /第N题 获取第N班答题列表
 const HandleQuestionScoreChartClick = (index) => {
   GetQuestionAnswerData(index + state.problemAnalysisData.showBarLegendIndex);
@@ -1107,7 +1278,7 @@ const GetMajorTableData = () => {
     (item) => item.prop,
   ); //动态表头字段名
   let allTableData = [];
-  const allList = state.problemAnalysisData.questionTableList;
+  const allList = state.problemAnalysisData.groupTableList;
   allList.forEach((item) => {
     let itemObj = {
       questionId: item?.questionId || "",
@@ -1157,7 +1328,7 @@ const GetPageMajorTableData = () => {
 const ResetTableScroll = () => {
   nextTick(() => {
     if (majorTable.value) {
-      const tableBody = majorTable.value.querySelector(
+      const tableBody = majorTable.value.$el.querySelector(
         ".el-table__body-wrapper",
       );
       if (tableBody) {
@@ -1175,7 +1346,7 @@ const handleSizeChange = (val: number) => {
   state.majorTableData.pageSize = val;
   state.majorTableData.currentPage = 1;
   GetMajorTableData(); //加载分析表格数据
-}
+};
 //设置表头样式
 const HeaderRowStyle = ({ row, rowIndex }) => {
   if (rowIndex === 1) {
@@ -1185,42 +1356,46 @@ const HeaderRowStyle = ({ row, rowIndex }) => {
   }
 };
 const pageInit = () => {
+  const chartTypeLists = [
+    {
+      label: "组合图",
+      value: "line_bar_chart",
+    },
+    {
+      label: "柱状图",
+      value: "vertical_bar",
+    },
+    {
+      label: "雷达图",
+      value: "radar_chart",
+    },
+  ];
+  const chartTypeList = [
+    {
+      label: "柱状图",
+      value: "vertical_bar",
+    },
+    {
+      label: "雷达图",
+      value: "radar_chart",
+    },
+  ];
   state.problemAnalysisData.chartTypeList =
-    analysisStore.filterObject.classLevel != 2
-      ? [
-          {
-            label: "组合图",
-            value: "line_bar_chart",
-          },
-          {
-            label: "柱状图",
-            value: "vertical_bar",
-          },
-          {
-            label: "雷达图",
-            value: "radar_chart",
-          },
-        ]
-      : [
-          {
-            label: "柱状图",
-            value: "vertical_bar",
-          },
-          {
-            label: "雷达图",
-            value: "radar_chart",
-          },
-        ];
+    analysisStore.filterObject.classLevel != 2 ? chartTypeLists : chartTypeList;
   state.problemAnalysisData.chartType =
     analysisStore.filterObject.classLevel != 2
       ? "line_bar_chart"
       : "vertical_bar";
+  state.majorQuestionData.chartTypeList =
+    analysisStore.filterObject.classLevel != 2 ? chartTypeLists : chartTypeList;
+  state.majorQuestionData.chartType =
+    analysisStore.filterObject.classLevel != 2
+      ? "line_bar_chart"
+      : "vertical_bar";
   state.questionScoreStatsData.chartType = "vertical_bar";
   state.majorTableData.currentPage = 1;
   state.majorAnswerData.rowIndex = 0;
-  state.studentPreviousExamData.selectVal = "standardScore";
-  state.studentPreviousExamData.selectName = "标准分";
-  GetQuestionAnalysisData(); //获取小题分析数据
+  GetQuestionGroupAnalysisData(); //题目分组分析
 };
 // 监听筛选条件
 watch(
@@ -1242,7 +1417,7 @@ onMounted(() => {
     width: calc(100% - 220px);
   }
 
-  &.card {
+  &.paper_card {
     width: calc(100% - 480px);
   }
 }
@@ -1251,14 +1426,14 @@ onMounted(() => {
   height: 100%;
   display: flex;
   align-items: center;
-  margin: auto;
+  margin: auto 0 auto auto;
   padding-bottom: 0;
 
   &.answer {
     width: 200px;
   }
 
-  &.card {
+  &.paper_card {
     width: 460px;
   }
 
@@ -1271,4 +1446,4 @@ onMounted(() => {
     }
   }
 }
-</style>
+</style>

+ 813 - 67
src/views/analysis/optionAnalysis.vue

@@ -1,87 +1,833 @@
 <template>
-  <!-- 成绩查询 成绩单 -->
-  <ReportModule :showTitle="false" :showDescribe="false" tableOrChart="table">
-    <template #title_left>
-      <el-input v-model="state.keyWord" style="width: 200px" placeholder="请输入学号或姓名" class="input_with">
-        <template #append>
-          <el-button :icon="Search" />
-        </template>
-      </el-input>
-      <span class="count_item">应考:293人</span>
-      <span class="count_item">实考:280人</span>
-      <span class="count_item orange">缺考:13人</span>
+  <ReportModule
+    :showTitle="true"
+    :titleList="[`1、${state.groupTitle}图`]"
+    :showDescribe="true"
+    tableOrChart="qita"
+    :showPrintBtn="false"
+    :showExportBtn="false"
+  >
+    <template #module_qita>
+      <div class="module_chart_query">
+        <div class="query_left">
+          <span
+            >满分:{{
+              state.question?.resData?.[state.question.selectIndex]?.fullScore
+                ? `${state.question.resData[state.question.selectIndex].fullScore}分`
+                : "-"
+            }}</span
+          >
+          <span
+            >平均分:{{
+              state.question?.resData?.[state.question.selectIndex]?.avgScore
+                ? `${state.question.resData[state.question.selectIndex].avgScore}分`
+                : "-"
+            }}</span
+          >
+          <span
+            >得分率:{{
+              state.question?.resData?.[state.question.selectIndex]?.scoreRate
+                ? `${state.question.resData[state.question.selectIndex].scoreRate}%`
+                : "-"
+            }}</span
+          >
+          <span
+            >答案:{{
+              state.question?.resData?.[state.question.selectIndex]
+                ?.answerValue || "-"
+            }}</span
+          >
+        </div>
+        <div class="query_right report_button">
+          <div class="select_question">
+            <span>当前小题:</span>
+            <el-select
+              v-model="state.question.select"
+              placeholder="请选择小题"
+              style="width: 140px"
+              @change="ChangeQuestionSelect"
+            >
+              <el-option
+                v-for="item in state.question.questionList"
+                :key="item?.questionId"
+                :label="item?.questionName"
+                :value="item?.questionId"
+              ></el-option>
+            </el-select>
+          </div>
+          <el-button
+            class="select_btn"
+            :disabled="state.question.selectIndex === 0"
+            @click="PrevQuestion"
+            >上一题</el-button
+          >
+          <el-button
+            class="select_btn"
+            :disabled="
+              state.question.selectIndex === state.question.lastQuestionIndex
+            "
+            @click="NextQuestion"
+            >下一题</el-button
+          >
+        </div>
+      </div>
+      <div class="module_content">
+        <div class="content_left">
+          <BarChart
+            :datax="state.barChartData.datax"
+            :datay="state.barChartData.datay"
+            :isShowMarkLine="false"
+            color=""
+            typeName="人数"
+            unit="人"
+          ></BarChart>
+        </div>
+        <div class="content_right">
+          <PieChart
+            :key="state.pieKey"
+            v-if="state.pieChart.data.length > 0"
+            :data="state.pieChart.data"
+            :refresh="state.pieChart.refresh"
+            @refresh="GetPieRefresh"
+            height="380"
+          />
+        </div>
+      </div>
     </template>
-    <template #title_right>
-      <el-checkbox-group class="checkbox_group" v-model="state.checkList">
-        <el-checkbox label="显示分组" value="group" />
-        <el-checkbox label="显示小题" value="question" />
-      </el-checkbox-group>
+    <template #module_describe>
+      说明:柱图代表每个选项的选择人数,饼图代表每个选项选择人数的占比。通过分析选择题选项的人数和占比,可以评估试题的质量。如果某个选项的选择人数与预期不符,可能意味着试题存在质量问题,需要进一步分析和调整。
     </template>
+  </ReportModule>
+  <ReportModule
+    :showTitle="true"
+    :titleList="[`2、${state.groupTitle}表`]"
+    :showDescribe="false"
+    tableOrChart="table"
+    :showPrintBtn="false"
+    :showExportBtn="true"
+    :currentPage="state.objectiveAnalysisData.currentPage"
+    :pageSize="state.objectiveAnalysisData.pageSize"
+    :total="state.objectiveAnalysisData.total"
+    @update:pageSize="handleSizeChange"
+    @update:currentPage="handleCurrentChange"
+  >
     <template #module_table_chart>
-      <el-table :data="tableData" border style="width: 100%">
-        <el-table-column prop="date" label="Date" width="180" />
-        <el-table-column prop="name" label="Name" width="180" />
-        <el-table-column prop="address" label="Address" />
+      <el-table
+        :data="objectiveTable"
+        border
+        stripe
+        align="left"
+        row-key="showCode"
+      >
+        <el-table-column
+          prop="questionName"
+          label="题号"
+          width="74"
+          align="center"
+          fixed
+          show-overflow-tooltip
+          v-if="state.objectiveAnalysisData.tableData.length"
+        />
+        <el-table-column
+          prop="questionType"
+          label="题型"
+          width="114"
+          align="center"
+          fixed
+          show-overflow-tooltip
+          v-if="state.objectiveAnalysisData.tableData.length"
+        />
+        <el-table-column
+          prop="answerScore"
+          label="分数"
+          width="80"
+          align="center"
+          fixed
+          show-overflow-tooltip
+          v-if="state.objectiveAnalysisData.tableData.length"
+        />
+        <el-table-column
+          prop="averageScore"
+          label="平均分"
+          width="80"
+          align="center"
+          fixed
+          show-overflow-tooltip
+          v-if="state.objectiveAnalysisData.tableData.length"
+        />
+        <el-table-column
+          prop="scoreRate"
+          label="得分率"
+          width="80"
+          align="center"
+          fixed
+          show-overflow-tooltip
+          v-if="state.objectiveAnalysisData.tableData.length"
+        >
+          <template #default="{ row: org }">
+            <span>{{ org["scoreRate"] }}%</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="answerValue"
+          label="答案"
+          width="80"
+          align="center"
+          fixed
+          show-overflow-tooltip
+          v-if="state.objectiveAnalysisData.tableData.length"
+        />
+        <el-table-column
+          label="各选项选择人数"
+          align="center"
+          v-if="state.objectiveAnalysisData.tableData.length"
+        >
+          <el-table-column
+            align="center"
+            v-for="key in state.objectiveAnalysisData.headLabels"
+            :key="key"
+            :label="key"
+            min-width="70"
+            show-overflow-tooltip
+          >
+            <template #default="{ row: org }">
+              <span
+                :style="{
+                  color:
+                    org?.['answerMap']?.[key]?.['studentNum'] &&
+                    org?.['answerMap']?.[key]?.['studentNum'] !== '0' &&
+                    org?.['answerMap']?.[key]?.['studentNum'] != '-'
+                      ? '#2E64FA'
+                      : '',
+                  cursor:
+                    org?.['answerMap']?.[key]?.['studentNum'] &&
+                    org?.['answerMap']?.[key]?.['studentNum'] !== '0' &&
+                    org?.['answerMap']?.[key]?.['studentNum'] != '-'
+                      ? 'pointer'
+                      : '',
+                }"
+                @click="
+                  showDialogData(
+                    org.questionCode,
+                    org.questionId,
+                    org?.['answerMap']?.[key]?.['name'],
+                    org?.['answerMap']?.[key]?.['studentNum'],
+                    org?.['answerMap']?.[key]?.['studentNames'],
+                  )
+                "
+                >{{ org?.["answerMap"]?.[key]?.["studentNum"] ?? "-" }}</span
+              >
+            </template>
+          </el-table-column>
+        </el-table-column>
+        <el-table-column
+          label="各选项选择人数占比"
+          align="center"
+          v-if="state.objectiveAnalysisData.tableData.length"
+        >
+          <el-table-column
+            align="center"
+            v-for="key in state.objectiveAnalysisData.headLabels"
+            :key="key"
+            :label="key"
+            min-width="70"
+            show-overflow-tooltip
+          >
+            <template #default="{ row: org }">
+              <span
+                v-if="
+                  org?.['answerMap']?.[key]?.['rate'] &&
+                  org?.['answerMap']?.[key]?.['rate'] != '-'
+                "
+                >{{ org?.["answerMap"]?.[key]?.["rate"] }}%</span
+              >
+              <span v-else>{{
+                org?.["answerMap"]?.[key]?.["rate"] ?? "-"
+              }}</span>
+            </template>
+          </el-table-column>
+        </el-table-column>
       </el-table>
     </template>
   </ReportModule>
+  <!-- 各选项选择人数 弹框 -->
+  <!-- <div class="page_dialog">
+    <el-dialog :title="dialogData.title" :visible.sync="dialogData.show">
+      <div class="dialog_center padding_20" :key="dialogData.dialogKey">
+        <div class="center_header">
+          <div class="header_left left_center">
+            <span class="left_count">总计:{{ dialogData.total }}人</span>
+          </div>
+          <div class="header_right report_button">
+            <el-button
+              class="button_task_select"
+              :loading="dialogExportLoading"
+              @click="DialogExportExcel()"
+            >
+              <img
+                v-if="!dialogExportLoading"
+                src="@/assets/icon/export_icon.webp"
+                alt="导出Excel"
+              />导出Excel
+            </el-button>
+          </div>
+        </div>
+        <div class="page_jg_20"></div>
+        <div class="module_table" style="width: 100%">
+          <el-table
+            :data="dialogData.list"
+            height="471px"
+            border
+            stripe
+            row-key="studentCode"
+          >
+            <el-table-column
+              type="index"
+              align="center"
+              label="序号"
+              width="50"
+              fixed
+            >
+              <template slot-scope="{ $index }">
+                {{
+                  (dialogData.currentPage - 1) * dialogData.pageSize +
+                  $index +
+                  1
+                }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              align="center"
+              property="studentCode"
+              label="学号"
+              width="100"
+              show-overflow-tooltip
+              fixed
+            ></el-table-column>
+            <el-table-column
+              align="center"
+              property="studentName"
+              label="姓名"
+              width="80"
+              show-overflow-tooltip
+              fixed
+            ></el-table-column>
+            <el-table-column
+              align="center"
+              property="examCode"
+              label="考号"
+              width="80"
+              show-overflow-tooltip
+            ></el-table-column>
+            <el-table-column
+              align="center"
+              property="className"
+              label="行政班"
+              width="80"
+              show-overflow-tooltip
+            >
+              <template slot-scope="{ row }">
+                {{ row.className || "-" }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              align="center"
+              property="teacherClassName"
+              label="教学班"
+              width="80"
+              show-overflow-tooltip
+            >
+              <template slot-scope="{ row }">
+                {{ row.teacherClassName || "-" }}
+              </template>
+            </el-table-column>
+            <el-table-column
+              align="center"
+              v-for="(item, index) in dialogData.headerList"
+              :key="item"
+              :label="item"
+              min-width="60"
+              show-overflow-tooltip
+            >
+              <template slot="header">
+                <div class="table_header">
+                  <el-tooltip
+                    class="item"
+                    effect="dark"
+                    :content="item"
+                    placement="top"
+                  >
+                    <span>{{ item }}</span>
+                  </el-tooltip>
+                </div>
+              </template>
+              <template slot-scope="scope">
+                <span
+                  :style="{
+                    color:
+                      scope.row.questionList[index].answerValue ===
+                      scope.row.questionList[index].studentAnswer
+                        ? '#606266'
+                        : '#EE6666',
+                  }"
+                  >{{ scope.row.questionList[index].studentAnswer }}</span
+                >
+              </template>
+            </el-table-column>
+          </el-table>
+          <div
+            class="page_pagination"
+            style="margin-top: 10px; padding-bottom: 10px"
+            v-if="dialogData.total > 10"
+          >
+            <el-pagination
+              @current-change="GetStudentTableDataChangePage"
+              layout="prev, pager, next"
+              :current-page="dialogData.currentPage"
+              :page-size="dialogData.pageSize"
+              :total="dialogData.total"
+            >
+            </el-pagination>
+          </div>
+          <div v-else class="page_jg_20"></div>
+        </div>
+      </div>
+    </el-dialog>
+  </div> -->
 </template>
 <script lang="ts" setup>
-import ReportModule from '@/components/ReportModule.vue';
-import { Search } from '@element-plus/icons-vue'
-import { onMounted, reactive, ref } from "vue";
-const tableData = [
-  {
-    date: '2016-05-03',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
-  },
-  {
-    date: '2016-05-02',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
-  },
-  {
-    date: '2016-05-04',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+import ReportModule from "@/components/ReportModule.vue";
+import BarChart from "@/components/echarts/barChart.vue"; //单柱状图
+import PieChart from "@/components/echarts/PieChart.vue";
+import { onMounted, watch, computed, reactive } from "vue";
+import { useAnalysisStore } from "@/store/analysis";
+import {
+  selectQuestionAnalysis,
+  selectQuestionAnalysisPage,
+  selectQuestionStudentPageList,
+} from "@/api/analysis";
+const analysisStore = useAnalysisStore();
+const state = reactive({
+  params: {}, //接口参数
+  groupTitle: "选项分析",
+  question: {
+    resData: [], //小题分析数据
+    select: "", //当前选择的小题
+    questionList: [], //当前小题下拉框
+    selectIndex: 0, //当前小题下拉框索引
+    lastQuestionIndex: null, //最后一题索引
+  }, //当前小题
+  barChartData: {
+    datax: [], //x轴数据
+    datay: [], //Y轴数据
+  }, //1、选择题分析图 柱状图
+  pieChart: {
+    refresh: false,
+    data: [],
+  }, //1、选择题分析图 饼状图
+  objectiveAnalysisData: {
+    tableData: [], //表格数据
+    originalTableData: [], //原始表格数据
+    data: [], //表数据
+    headLabels: [], //选项头部信息
+    pageSize: 10, //每页显示数据
+    total: 0, //总数
+    currentPage: 1, //当前页
+  }, //客观题分析数据
+  dialogData: {
+    dialogKey: 1,
+    title: "",
+    questionId: "", //题号
+    answer: "", //学生选择的答案
+    studentCodeList: [], //学生学号
+    show: false,
+    pageSize: 10, //每页显示数据
+    total: 0, //总数
+    currentPage: 1, //当前页
+    list: [],
+    headerList: [],
+    studentNum: 0,
   },
-  {
-    date: '2016-05-01',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+  exportLoading: false, // 客观题表格数据导出
+  dialogExportLoading: false, // 弹窗内数据导出
+  pieKey: 0,
+});
+const objectiveTable = computed(() => {
+  const start =
+    (state.objectiveAnalysisData.currentPage - 1) *
+    state.objectiveAnalysisData.pageSize;
+  const end = start + state.objectiveAnalysisData.pageSize;
+  return state.objectiveAnalysisData.tableData.slice(start, end);
+});
+const PageInit = (isRefresh) => {
+  state.objectiveAnalysisData.currentPage = 1;
+  if (isRefresh) {
+    state.pieChart.refresh = true;
+  }
+  GetSelectQuestionAnalysisChartData(); //客观题分析(选择题分析图)
+  GetSelectQuestionAnalysisPage(); //客观题分析(选择题分析表-分页)
+};
+//客观题分析(选择题分析图)
+const GetSelectQuestionAnalysisChartData = () => {
+  selectQuestionAnalysis({ ...analysisStore.filterObject }).then((res) => {
+    state.question.questionList = []; //小题下拉选项
+    state.question.select = ""; //当前选择小题的值
+    state.question.selectIndex = 0; //当前选择小题的索引
+    state.question.lastQuestionIndex = null; //最后一题索引
+    state.question.resData = [];
+    if (res.code == 200 && res.data && res.data.length) {
+      const resData = res.data || [];
+      state.question.resData = resData;
+      state.question.questionList = resData.map((item) => {
+        return {
+          questionId: item.questionId,
+          questionName: item.questionName,
+        };
+      });
+      if (state.question.questionList.length > 0) {
+        state.question.select = state.question.questionList[0].questionId;
+        state.question.lastQuestionIndex =
+          state.question.questionList.length - 1; //最后一题索引
+      } else {
+        state.question.lastQuestionIndex = 0;
+      }
+      state.pieKey++;
+      GetChartData(); //1、选择题分析图 柱状图
+    } else {
+      state.barChartData.datax = []; // 1、选择题分析图 柱状图 X轴
+      state.barChartData.datay = []; // 1、选择题分析图 柱状图 Y轴
+      state.pieChart.data = []; //1、选择题分析图 饼状图
+
+      state.question.questionList = []; //小题下拉选项
+      state.question.select = ""; //当前选择小题的值
+      state.question.selectIndex = 0; //当前选择小题的索引
+      state.question.lastQuestionIndex = null; //最后一题索引
+      state.question.resData = [];
+    }
+  });
+};
+//切换小题下拉框
+const ChangeQuestionSelect = () => {
+  state.question.selectIndex = state.question.questionList.findIndex(
+    (item) => item.questionId == state.question.select,
+  );
+  state.pieChart.refresh = true;
+  GetChartData();
+};
+//上一题
+const PrevQuestion = () => {
+  if (state.question.selectIndex > 0) {
+    state.question.selectIndex--;
+    state.question.select =
+      state.question.questionList[state.question.selectIndex].questionId;
+    state.pieChart.refresh = true;
+    GetChartData();
+  }
+};
+//下一题
+const NextQuestion = () => {
+  if (state.question.selectIndex < state.question.lastQuestionIndex) {
+    state.question.selectIndex++;
+    state.question.select =
+      state.question.questionList[state.question.selectIndex].questionId;
+    state.pieChart.refresh = true;
+    GetChartData();
+  }
+};
+//获取当前选择的小题 柱状图 + 饼状图
+const GetChartData = () => {
+  const answerScoreData =
+    state.question?.resData[state.question.selectIndex]?.answerScore || [];
+  state.barChartData.datax = []; // 1、选择题分析图 柱状图 X轴
+  state.barChartData.datay = []; // 1、选择题分析图 柱状图 Y轴
+  state.pieChart.data = []; //1、选择题分析图 饼状图
+  answerScoreData.forEach((item) => {
+    state.barChartData.datax.push(item.name);
+    state.barChartData.datay.push(item.studentNum);
+    state.pieChart.data.push({
+      value: item.rate,
+      name: item.name,
+    });
+  });
+};
+const GetPieRefresh = (val) => {
+  state.pieChart.refresh = val;
+};
+//客观题分析(选择题分析表-分页)
+const GetSelectQuestionAnalysisPage = () => {
+  selectQuestionAnalysisPage({
+    ...analysisStore.filterObject,
+    pageParam: {
+      pageNum: state.objectiveAnalysisData.currentPage, //当前页码
+      pageSize: 200, //每页显示行数
+    },
+  }).then((res) => {
+    if (res.code == 200 && res.data) {
+      const { total, records } = res.data;
+      if (records?.length) {
+        state.objectiveAnalysisData.tableData = records[0]?.pageResult || []; //表格数据
+        state.objectiveAnalysisData.headLabels = records[0]?.label || []; //选项头部信息
+        state.objectiveAnalysisData.total = Number(total); //总条数
+      } else {
+        state.objectiveAnalysisData.tableData = [];
+        state.objectiveAnalysisData.headLabels = [];
+        state.objectiveAnalysisData.total = 0;
+      }
+    } else {
+      state.objectiveAnalysisData.tableData = [];
+      state.objectiveAnalysisData.headLabels = [];
+      state.objectiveAnalysisData.total = 0;
+    }
+  });
+};
+//分页切换
+const handleCurrentChange = (val) => {
+  state.objectiveAnalysisData.currentPage = val;
+};
+const handleSizeChange = (val: number) => {
+  state.objectiveAnalysisData.pageSize = val;
+  state.objectiveAnalysisData.currentPage = 1;
+};
+//显示 各选项选择人数 弹框
+const showDialogData = (
+  questionCode,
+  questionId,
+  answer,
+  studentNum,
+  studentCodeList,
+) => {
+  if (!studentNum || studentNum == "0" || studentNum == "-") {
+    return false;
+  }
+  state.dialogData.title = `${questionCode} ${answer}选项人数`;
+  state.dialogData.show = true;
+  state.dialogData.currentPage = 1;
+  state.dialogData.questionId = questionId;
+  state.dialogData.answer = answer;
+  state.dialogData.studentCodeList = studentCodeList;
+  state.dialogData.studentNum = studentNum;
+  GetSelectQuestionStudentPageList(studentCodeList, studentNum);
+};
+const GetSelectQuestionStudentPageList = (studentCodeList, studentNum) => {
+  state.dialogData.dialogKey += 1;
+  const start = (state.dialogData.currentPage - 1) * state.dialogData.pageSize;
+  const end = start + state.dialogData.pageSize;
+  const studentCodeListData = studentCodeList.slice(start, end);
+  state.dialogData.total = Number(studentNum); //总条数
+  selectQuestionStudentPageList({
+    ...analysisStore.filterObject,
+    studentCodeList: studentCodeListData,
+    // questionId,//题号
+    // answer,//学生选择的答案
+    pageParam: {
+      pageNum: 1, //当前页码
+      pageSize: state.dialogData.pageSize, //每页显示行数
+    },
+  }).then((res) => {
+    const { code, data } = res;
+    if (code == 200) {
+      const { total, records } = data;
+      state.dialogData.list = records[0]?.studentPageList || [];
+      state.dialogData.headerList = records[0]?.headerList || []; //选项头部信息
+    }
+  });
+};
+//弹框分页切换
+const GetStudentTableDataChangePage = (val) => {
+  state.dialogData.currentPage = val;
+  GetSelectQuestionStudentPageList(
+    state.dialogData.studentCodeList,
+    state.dialogData.studentNum,
+  );
+};
+//弹框导出
+const DialogExportExcel = () => {
+  state.dialogExportLoading = true;
+  const examName = this.$store.state.report.examSelectItem.examName;
+  const title = state.dialogData.title;
+  const fileName = `${GetFileName(examName, this.params)}_${state.groupTitle}表_${title.replace(/\s/g, "")}_${GetExportDate()}`;
+  const sheetName = title.replace(/\s/g, "");
+  let params = {
+    ...this.params,
+    studentCodeList: this.dialogData.studentCodeList,
+    fileName,
+    sheetName,
+  };
+  this.$api.reportSchool
+    .publicExportSelectQuestion(params)
+    .then((res) => {
+      if (res.status == 200) {
+        let blob = new Blob([res.data], {
+          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+        });
+        let a = document.createElement("a");
+        a.href = URL.createObjectURL(blob);
+        a.download = decodeURIComponent(
+          res.headers["content-disposition"].split("filename=")[1],
+        );
+        a.click();
+        URL.revokeObjectURL(a.href);
+        a.remove();
+      } else {
+        this.$message.error("导出失败!");
+      }
+    })
+    .finally(() => {
+      this.dialogExportLoading = false;
+    });
+};
+// 客观题分析表数据导出
+const ExportExcel = (title) => {
+  this.exportLoading = true;
+  const examName = this.$store.state.report.examSelectItem.examName;
+  const staticHeaderData = selectQuestionAnalysisStaticHeaderData();
+  const dynamicsHeaderData = selectQuestionAnalysisDynamicsHeaderData();
+  const childHeaderData = this.objectiveAnalysisData.headLabels.map((item) => ({
+    label: item,
+  }));
+  let dataList = [];
+  this.objectiveAnalysisData.tableData.forEach((row) => {
+    const staticData = staticHeaderData.map((header) => {
+      if (header.prop == "scoreRate") {
+        return `${row["scoreRate"]}%`;
+      } else {
+        return row[header.prop];
+      }
+    });
+    const studentNumData = childHeaderData.map((child) => {
+      const value = row?.["answerMap"]?.[child.label]?.["studentNum"] ?? "-";
+      if (child.label == row.answerValue) {
+        return `${value}答案`;
+      } else {
+        return value;
+      }
+    });
+    const rateData = childHeaderData.map((child) => {
+      const value = `${row?.["answerMap"]?.[child.label]?.["rate"]}${row?.["answerMap"]?.[child.label]?.["rate"] && row?.["answerMap"]?.[child.label]?.["rate"] != "-" ? "%" : ""}`;
+      if (child.label == row.answerValue) {
+        return `${value}答案`;
+      } else {
+        return value;
+      }
+    });
+    dataList.push([...staticData, ...studentNumData, ...rateData]);
+  });
+  let params = {
+    fileName: `${GetFileName(examName, this.params)}_${title}_${GetExportDate()}`,
+    examName: examName, //考试名称
+    sheetName: title, //sheet页名称
+    staticHeaderData: staticHeaderData, //静态表头
+    dynamicsHeaderData: dynamicsHeaderData, //动态表头的一级表头
+    childHeaderData: childHeaderData, //二级表头
+    dataList: dataList, //数据
+  };
+  this.$api.reportSchool
+    .exportObjectAnalysis(params)
+    .then((res) => {
+      if (res.status == 200) {
+        let blob = new Blob([res.data], {
+          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+        });
+        let a = document.createElement("a");
+        a.href = URL.createObjectURL(blob);
+        a.download = decodeURIComponent(
+          res.headers["content-disposition"].split("filename=")[1],
+        );
+        a.click();
+        URL.revokeObjectURL(a.href);
+        a.remove();
+      } else {
+        this.$message.error("导出失败!");
+      }
+    })
+    .finally(() => {
+      this.exportLoading = false;
+    });
+};
+// 监听筛选条件
+watch(
+  () => analysisStore.filterObject,
+  async () => {
+    PageInit(true);
   },
-]
-const state = reactive({
-  keyWord: '',
-  checkList: ['group']
+  { deep: true },
+);
+
+onMounted(() => {
+  PageInit(false);
 });
-onMounted(() => { });
 </script>
 
 <style lang="scss" scoped>
-.input_with {
-  margin-right: 10px;
-}
-
-.count_item {
-  font-weight: 400;
-  font-size: 16px;
-  color: #333333;
-  line-height: 24px;
-  margin-left: 10px;
-
-  &.orange {
-    color: #FB9F34;
+:deep(.module_qita) {
+  flex-direction: column;
+  .module_chart_query {
+    width: calc(100% - 40px);
+    margin: auto;
+    font-size: 14px;
+    color: #333333;
+    text-align: left;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 20px;
+    .query_left {
+      display: inline-flex;
+      flex: 1;
+      span {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        min-width: 120px;
+        padding: 0 8px;
+        font-size: 14px;
+        color: #2e64fa;
+        box-sizing: border-box;
+        height: 36px;
+        background: rgba(46, 100, 250, 0.1);
+        border-radius: 4px;
+        margin-right: 20px;
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+    .query_right {
+      display: flex;
+      justify-content: flex-end;
+      align-items: center;
+      .select_question {
+        color: #333333;
+        font-size: 14px;
+        font-weight: 400;
+      }
+    }
   }
-}
-
-.checkbox_group {
-  :deep(.el-checkbox) {
-    margin-right: 10px;
+  .report_button {
+    gap: 10px;
+    .select_btn {
+      border: 1px solid #2e64fa;
+      color: #2e64fa;
+      margin-left: 0;
+      &:hover {
+        border-color: #2e64fa;
+        background-color: rgba(46, 100, 250, 0.1);
+      }
+      &.is-disabled {
+        color: #bbbbbb;
+        border-color: #bbbbbb;
+      }
+    }
+  }
+  .module_content {
+    display: flex;
+    width: 100%;
+    .content_left {
+      width: 50%;
+    }
 
-    &:nth-child(1) {
-      margin-right: 20px;
+    .content_right {
+      width: 50%;
     }
   }
 }

+ 627 - 65
src/views/analysis/propositionAnalysis.vue

@@ -1,88 +1,650 @@
 <template>
-  <!-- 成绩查询 成绩单 -->
-  <ReportModule :showTitle="false" :showDescribe="false" tableOrChart="table">
-    <template #title_left>
-      <el-input v-model="state.keyWord" style="width: 200px" placeholder="请输入学号或姓名" class="input_with">
-        <template #append>
-          <el-button :icon="Search" />
-        </template>
-      </el-input>
-      <span class="count_item">应考:293人</span>
-      <span class="count_item">实考:280人</span>
-      <span class="count_item orange">缺考:13人</span>
+  <ReportModule
+    :showHeader="false"
+    :showTitle="false"
+    :showDescribe="false"
+    tableOrChart="qita"
+    :showPrintBtn="false"
+    :showExportBtn="false"
+  >
+    <template #module_qita>
+      <div class="card_container">
+        <div
+          class="paper_card"
+          v-for="(item, index) in state.paperItems"
+          :key="index"
+          :style="{ background: item.background }"
+        >
+          <div
+            class="background-image"
+            :style="{ backgroundImage: `url(${item.backgroundImage})` }"
+          ></div>
+          <div class="statistic" :style="{ color: item.color }">
+            {{ item.label }}
+          </div>
+          <div class="value" :style="{ color: item.color }">
+            {{ item.value }}
+          </div>
+        </div>
+      </div>
     </template>
-    <template #title_right>
-      <el-checkbox-group class="checkbox_group" v-model="state.checkList">
-        <el-checkbox label="显示分组" value="group" />
-        <el-checkbox label="显示小题" value="question" />
-      </el-checkbox-group>
+  </ReportModule>
+  <ReportModule
+    :titleList="['1、难度与区分度对比图']"
+    tableOrChart="chart"
+    :showPrintBtn="false"
+    :showExportBtn="false"
+  >
+    <template #module_table_chart>
+      <LineChart
+        v-if="state.lineChartData?.datax?.length"
+        :datax="state.lineChartData.datax"
+        :datay="state.lineChartData.datay"
+        :showCheckBox="false"
+        :extraText="false"
+        :showMarkPoint="true"
+        :showMarkLine="true"
+        :gridRight="40"
+        :isSetMarkNumber="true"
+        :markNumber="state.lineChartData.markNumber"
+        :title="state.lineChartData.title"
+        :legendList="state.lineChartData.title"
+      ></LineChart>
+      <div
+        v-else
+        class="module_chart no_content_data"
+        v-loading="state.dataLoading"
+        :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、难度分布']"
+    :showDescribe="false"
+    tableOrChart="qita"
+    :showPrintBtn="false"
+    :showExportBtn="false"
+  >
+    <template #module_qita>
+      <div class="content_left">
+        <PieChart
+          v-if="state.difficultyChart.data?.length"
+          height="340"
+          :data="state.difficultyChart.data"
+          :refresh="state.difficultyChart.refresh"
+          @refresh="GetFirstPieRefresh"
+        />
+        <div
+          v-else
+          class="module_chart no_content_data"
+          v-loading="state.dataLoading"
+          :element-loading-text="state.loadingText"
+          element-loading-spinner="el-icon-loading"
+          element-loading-background="#ffffff"
+        >
+          <span>暂无数据</span>
+        </div>
+      </div>
+      <div class="content_right table_42">
+        <el-table
+          border
+          :data="state.difficultyChart.tableData"
+          stripe
+          align="center"
+          row-key="name"
+        >
+          <el-table-column
+            prop="name"
+            label="分类"
+            align="center"
+            width="100"
+            show-overflow-tooltip
+            v-if="state.difficultyChart.tableData.length"
+          />
+          <el-table-column
+            prop="value"
+            label="难度系数"
+            align="center"
+            width="120"
+            show-overflow-tooltip
+            v-if="state.difficultyChart.tableData.length"
+          />
+          <el-table-column
+            prop="score"
+            label="满分"
+            align="center"
+            width="80"
+            show-overflow-tooltip
+            v-if="state.difficultyChart.tableData.length"
+          />
+          <el-table-column
+            prop="scoreRate"
+            label="分数占比"
+            align="center"
+            width="90"
+            show-overflow-tooltip
+            v-if="state.difficultyChart.tableData.length"
+          >
+            <template #default="scope">
+              <span>{{ scope.row.scoreRate }}%</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            :prop="item.prop"
+            :label="item.label"
+            align="center"
+            show-overflow-tooltip
+            v-for="(item, index) in state.discriminationChart.headerData"
+            :key="index"
+          >
+            <template #default="scope">
+              <span class="table_row_answer">{{
+                scope.row[item.prop] || "-"
+              }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
     </template>
+  </ReportModule>
+  <ReportModule
+    :titleList="['3、区分度分布']"
+    :showDescribe="false"
+    tableOrChart="qita"
+    :showPrintBtn="false"
+    :showExportBtn="false"
+  >
+    <template #module_qita>
+      <div class="content_left">
+        <PieChart
+          v-if="state.discriminationChart.data?.length"
+          :color="['#5470C6', '#3BA272', '#FAC858', '#EE6666']"
+          height="340"
+          :data="state.discriminationChart.data"
+          :refresh="state.discriminationChart.refresh"
+          @refresh="GetSecondPieRefresh"
+        />
+        <div
+          v-else
+          class="module_chart no_content_data"
+          v-loading="state.dataLoading"
+          :element-loading-text="state.loadingText"
+          element-loading-spinner="el-icon-loading"
+          element-loading-background="#ffffff"
+        >
+          <span>暂无数据</span>
+        </div>
+      </div>
+      <div class="content_right table_42">
+        <el-table
+          border
+          :data="state.discriminationChart.tableData"
+          stripe
+          align="center"
+          row-key="name"
+        >
+          <el-table-column
+            prop="name"
+            label="分类"
+            align="center"
+            width="70"
+            v-if="state.discriminationChart.tableData.length"
+          />
+          <el-table-column
+            prop="value"
+            label="区分度"
+            align="center"
+            width="110"
+            show-overflow-tooltip
+            v-if="state.discriminationChart.tableData.length"
+          />
+          <el-table-column
+            prop="score"
+            label="满分"
+            align="center"
+            width="80"
+            show-overflow-tooltip
+            v-if="state.discriminationChart.tableData.length"
+          />
+          <el-table-column
+            prop="scoreRate"
+            label="分数占比"
+            align="center"
+            width="100"
+            show-overflow-tooltip
+            v-if="state.discriminationChart.tableData.length"
+          >
+            <template #default="scope">
+              <span>{{ scope.row.scoreRate }}%</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            :prop="item.prop"
+            :label="item.label"
+            align="center"
+            show-overflow-tooltip
+            v-for="(item, index) in state.discriminationChart.headerData"
+            :key="index"
+          >
+            <template #default="scope">
+              <span class="table_row_answer">{{
+                scope.row[item.prop] || "-"
+              }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </template>
+  </ReportModule>
+  <ReportModule
+    :showTitle="true"
+    :titleList="['4、命题分析表']"
+    :showDescribe="false"
+    tableOrChart="table"
+    :showPrintBtn="false"
+    :showExportBtn="true"
+    :currentPage="state.questionData.currentPage"
+    :pageSize="state.questionData.pageSize"
+    :total="state.questionData.total"
+    @update:pageSize="handleSizeChange"
+    @update:currentPage="handleCurrentChange"
+  >
     <template #module_table_chart>
-      <el-table :data="tableData" border style="width: 100%">
-        <el-table-column prop="date" label="Date" width="180" />
-        <el-table-column prop="name" label="Name" width="180" />
-        <el-table-column prop="address" label="Address" />
+      <el-table
+        :data="questionTable"
+        border
+        stripe
+        align="center"
+        row-key="showCode"
+      >
+        <el-table-column
+          prop="questionCode"
+          label="题号"
+          min-width="120"
+          align="center"
+          show-overflow-tooltip
+        >
+          <template #default="scope">
+            <span v-if="scope.row.showCode == 0">全卷</span>
+            <span v-else>{{ scope.row.questionCode }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="questionTypeName"
+          label="题型"
+          min-width="120"
+          align="center"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          prop="studentCount"
+          label="实考人数"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          prop="fullMark"
+          label="满分"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          prop="difficulty"
+          label="难度"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          prop="discrimination"
+          label="区分度"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          prop="averageScore"
+          label="平均分"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          prop="maxScore"
+          label="最高分"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          prop="minScore"
+          label="最低分"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          prop="scoreRate"
+          label="得分率"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        >
+          <template #default="scope"> {{ scope.row.scoreRate }}% </template>
+        </el-table-column>
+        <el-table-column
+          prop="standardDeviation"
+          label="标准差"
+          min-width="100"
+          align="center"
+          show-overflow-tooltip
+        />
       </el-table>
     </template>
   </ReportModule>
 </template>
 <script lang="ts" setup>
-import ReportModule from '@/components/ReportModule.vue';
-import { Search } from '@element-plus/icons-vue'
-import { onMounted, reactive, ref } from "vue";
-const tableData = [
-  {
-    date: '2016-05-03',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+import { reactive, onMounted, computed, watch } from "vue";
+import ReportModule from "@/components/ReportModule.vue";
+import LineChart from "@/components/echarts/lineChart.vue";
+import PieChart from "@/components/echarts/PieChart.vue";
+import paperDifficultyImg from "@/assets/icon/Paper_difficulty.png";
+import difficultyRatioImg from "@/assets/icon/difficulty_ratio.png";
+import testPaperDistinctionImg from "@/assets/icon/test_paper_distinction.png";
+import discriminationRatioImg from "@/assets/icon/discrimination_ratio.png";
+import testPaperReliabilityImg from "@/assets/icon/test_paper_reliability.png";
+import { useAnalysisStore } from "@/store/analysis";
+import { propositionAnalysis } from "@/api/analysis";
+const analysisStore = useAnalysisStore();
+interface PaperItem {
+  label: string;
+  value: string | number;
+  background: string;
+  backgroundImage: string;
+  color: string;
+}
+
+const state = reactive({
+  paperItems: [
+    {
+      label: "试卷难度",
+      value: "",
+      background: "linear-gradient(99.67deg, #B4D2FF 0%, #F1F7FF 100%)",
+      backgroundImage: paperDifficultyImg,
+      color: "#15325E",
+    },
+    {
+      label: "难度比例(难中易)",
+      value: "",
+      background: "linear-gradient(99.67deg, #FFCAE1 0%, #FFF3F8 100%)",
+      backgroundImage: difficultyRatioImg,
+      color: "#5d1638",
+    },
+    {
+      label: "试卷区分度",
+      value: "",
+      background: "linear-gradient(99.67deg, #D2C6FF 0%, #F7F1FF 100%)",
+      backgroundImage: testPaperDistinctionImg,
+      color: "#41155e",
+    },
+    {
+      label: "区分度比例(优良中差)",
+      value: "",
+      background: "linear-gradient(99.67deg, #F7E595 0%, #FFFBED 100%)",
+      backgroundImage: discriminationRatioImg,
+      color: "#7a581d",
+    },
+    {
+      label: "试卷信度",
+      value: "",
+      background: "linear-gradient(99.67deg, #BCF5C9 0%, #ECFFF5 100%)",
+      backgroundImage: testPaperReliabilityImg,
+      color: "#0f4c2e",
+    },
+  ] as PaperItem[],
+  keyWord: "",
+  checkList: [] as string[],
+  lineChartData: {
+    datax: [],
+    datay: [[], []],
+    title: ["难度", "区分度"],
+    colors: ["", "", "", ""],
+    markNumber: [],
   },
-  {
-    date: '2016-05-02',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+  difficultyChart: {
+    data: [],
+    refresh: false,
+    tableData: [],
   },
-  {
-    date: '2016-05-04',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+  discriminationChart: {
+    data: [],
+    refresh: false,
+    tableData: [],
+    headerData: [],
   },
-  {
-    date: '2016-05-01',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles',
+  questionData: {
+    totalTableData: [],
+    tableData: [],
+    pageSize: 10,
+    total: 0,
+    currentPage: 1,
   },
-]
-const state = reactive({
-  keyWord: '',
-  checkList: ['group']
+  exportLoading: false,
+  dataLoading: false,
+  loadingText: "加载中,请稍后……",
 });
-onMounted(() => { });
-</script>
+const questionTable = computed(() => {
+  const start =
+    (state.questionData.currentPage - 1) * state.questionData.pageSize;
+  const end = start + state.questionData.pageSize;
+  return state.questionData.tableData.slice(start, end);
+});
+const PageInit = async (isRefresh) => {
+  if (isRefresh) {
+    state.difficultyChart.refresh = true;
+    state.discriminationChart.refresh = true;
+  }
+  state.dataLoading = true;
+  try {
+    //命题分析
+    let { code, data } = await propositionAnalysis(analysisStore.filterObject);
+    state.questionData.currentPage = 1;
+    if (code == 200 && data) {
+      const {
+        difficultyList,
+        discriminationList,
+        paperInfo,
+        questionInfoList,
+      } = data;
+      const difficultyChart = difficultyList || []; //难度分析数据
+      const discriminationChart =
+        discriminationList.map((item) => {
+          item.name == "及格"
+            ? (item.name = "一般")
+            : item.name == "低分"
+              ? (item.name = "较低")
+              : item.name;
+          return item;
+        }) || []; //3、区分度分布数据
+      state.questionData.totalTableData = questionInfoList || []; //题目列表 总数据 原始数据
+      state.questionData.tableData = questionInfoList || []; //题目列表 表数据
+      const length = state.questionData.totalTableData.length;
+      state.paperItems[0].value = paperInfo.paperDifficulty; //试卷难度
+      state.paperItems[1].value = paperInfo.difficultyRate; //难度比例
+      state.paperItems[2].value = paperInfo.paperDiscrimination; //试卷区分度
+      state.paperItems[3].value = paperInfo.discriminationRate; //区分度比例
+      state.paperItems[4].value = paperInfo.paperReliability; //试卷信度
+      state.lineChartData.datax = [];
+      state.lineChartData.datay = [[], []];
 
-<style lang="scss" scoped>
-.input_with {
-  margin-right: 10px;
-}
+      state.lineChartData.markNumber = [
+        state.questionData.totalTableData[length - 1].difficulty,
+        state.questionData.totalTableData[length - 1].discrimination,
+      ]; //全卷难度,全卷区分度
+      state.questionData.totalTableData.forEach((item, index) => {
+        if (index != length - 1) {
+          state.lineChartData.datax.push(item.questionCode);
+          state.lineChartData.datay[0].push(item.difficulty); //难度
+          state.lineChartData.datay[1].push(item.discrimination); //区分度
+        }
+      });
+      state.difficultyChart.data = difficultyChart.map((item) => {
+        return {
+          name: item.name,
+          value: item.scoreRate,
+        };
+      }); //难度分析 饼状图
+      state.difficultyChart.tableData = difficultyChart; //难度分析 表格
+      state.difficultyChart.headerData = data.headerList || []; //难度分析 表头数据
+      state.discriminationChart.data = discriminationChart.map((item) => {
+        return {
+          name: item.name,
+          value: item.scoreRate,
+        };
+      }); //区分度分布 饼状图
+      state.discriminationChart.headerData = data.headerList || []; //区分度分布 表头数据
+      state.discriminationChart.tableData = discriminationChart; //区分度分布 表格
+      state.questionData.total = length; //总条数
+    } else {
+      state.lineChartData.datax = [];
+      state.lineChartData.datay = [[], []];
+      state.lineChartData.title = ["难度", "区分度"];
+      state.lineChartData.colors = [];
+      state.lineChartData.markNumber = [];
 
-.count_item {
-  font-weight: 400;
-  font-size: 16px;
-  color: #333333;
-  line-height: 24px;
-  margin-left: 10px;
+      state.paperItems.map((item) => {
+        item.value = "";
+      });
 
-  &.orange {
-    color: #FB9F34;
-  }
-}
+      state.difficultyChart.data = [];
+      state.difficultyChart.tableData = []; //难度分析 表格
+      state.difficultyChart.headerData = []; //难度分析 表头数据
 
-.checkbox_group {
-  :deep(.el-checkbox) {
-    margin-right: 10px;
+      state.discriminationChart.data = [];
+      state.discriminationChart.headerData = [];
+      state.discriminationChart.tableData = [];
 
-    &:nth-child(1) {
-      margin-right: 20px;
+      state.questionData.totalTableData = []; //题目列表 总数据 原始数据
+      state.questionData.tableData = [];
     }
+  } finally {
+    state.dataLoading = false;
+  }
+};
+const GetFirstPieRefresh = (value) => {
+  state.difficultyChart.refresh = value;
+};
+const GetSecondPieRefresh = (value) => {
+  state.discriminationChart.refresh = value;
+};
+// 4、命题分析表 表格分页切换事件
+const handleCurrentChange = (val) => {
+  state.questionData.currentPage = val;
+};
+const handleSizeChange = (val: number) => {
+  state.questionData.pageSize = val;
+  state.questionData.currentPage = 1;
+};
+// 监听筛选条件
+watch(
+  () => analysisStore.filterObject,
+  async () => {
+    PageInit(true);
+  },
+  { deep: true },
+);
+
+onMounted(() => {
+  PageInit(false);
+});
+</script>
+
+<style lang="scss" scoped>
+.card_container {
+  width: 100%;
+  display: flex;
+  flex-wrap: wrap;
+  /* 允许换行 */
+  /* 设置元素之间间隔 */
+  gap: 10px;
+  padding: 10px;
+  box-sizing: border-box;
+  .paper_card {
+    //   flex: 0 1 calc((100% - 240px) / 5);
+    flex-grow: 1; /* 使每个div平分可用空间 */
+    flex-basis: 0; /* 初始基础大小为0 */
+    min-width: 180px;
+    //   max-width: 320px;
+    // width: 238px;
+    /* 每个卡片占据 18% 宽度,确保能在一行显示五个,考虑间隔 */
+    // flex: 0 0 16.5%; /* 每个卡片占据 18% 宽度,确保能在一行显示五个,考虑间隔 */
+    // border: 2px dashed #ccc;
+    border-radius: 10px;
+    padding: 20px 16px;
+    text-align: center;
+    color: #fff;
+    /* 文字颜色可以根据背景调整 */
+    background-size: cover;
+    background-position: center;
+    height: 100px;
+    /* 可调整高度 */
+    // margin-bottom: 20px; /* 卡片底部间距 */
+    position: relative;
+  }
+  .background-image {
+    position: absolute;
+    top: 0;
+    right: 0;
+    /* 背景图片宽度 */
+    width: 100%;
+    height: 100px;
+    background-size: contain;
+    background-repeat: no-repeat;
+    background-position: center right;
+    z-index: 1;
+    /* 确保背景图片在文本之下 */
+    border-radius: 10px;
+  }
+  .statistic,
+  .value {
+    position: relative;
+    z-index: 2;
+    /* 确保文本在背景图片之上 */
+  }
+  .statistic {
+    font-size: 16px;
+    font-weight: 400;
+    line-height: 22px;
+    text-align: left;
+    z-index: 2;
+  }
+  .value {
+    font-size: 26px;
+    font-weight: 600;
+    line-height: 42px;
+    text-align: left;
+    margin-top: 5px;
+    z-index: 2;
+  }
+}
+.module_qita {
+  align-items: center;
+  .content_left {
+    width: 40%;
+  }
+  .content_right {
+    width: 60%;
   }
 }
 </style>

+ 0 - 2
src/views/analysis/questionAnalysis.vue

@@ -1218,8 +1218,6 @@ const pageInit = () => {
   state.questionScoreStatsData.chartType = "vertical_bar";
   state.majorTableData.currentPage = 1;
   state.majorAnswerData.rowIndex = 0;
-  state.studentPreviousExamData.selectVal = "standardScore";
-  state.studentPreviousExamData.selectName = "标准分";
   GetQuestionAnalysisData(); //获取小题分析数据
 };
 // 监听筛选条件

+ 1 - 1
vite.config.ts

@@ -17,7 +17,7 @@ export default defineConfig({
      
       '/api':{
         target:'https://dev3.k12100.net/teaching/api/',//测试环境
-        // target:'http://192.168.1.133:47001/api/',//测试环境
+        // target:'http://192.168.1.48:47001/api/',//测试环境
         // target:'https://www.k12100.com/teaching/api/',//正式环境
         changeOrigin:true,
         rewrite:path => path.replace(/^\/api/, '')