errorAnalysis.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <template>
  2. <!-- 成绩查询 成绩单 -->
  3. <ReportModule
  4. ref="reportModuleRef"
  5. :titleList="['错题分析表']"
  6. :showDescribe="false"
  7. tableOrChart="table"
  8. :showTablePage="false"
  9. @ExportExcel="ExportExcel"
  10. @PrintPdf="PrintPdf"
  11. >
  12. <template #module_table_chart>
  13. <el-table
  14. :data="state.tableData"
  15. border
  16. stripe
  17. row-key="questionId"
  18. max-height="700"
  19. :span-method="SpanMethod"
  20. :row-class-name="ErrorTableRowClassName"
  21. :cell-class-name="ErrorTableCellClassName"
  22. v-loading="state.tableLoading"
  23. :element-loading-text="state.loadingText"
  24. :element-loading-spinner="loadingSvgIcon"
  25. element-loading-background="#ffffff"
  26. >
  27. <el-table-column
  28. prop="questionName"
  29. label="小题名称"
  30. width="80"
  31. align="center"
  32. show-overflow-tooltip
  33. />
  34. <el-table-column
  35. prop="questionType"
  36. label="题型"
  37. width="80"
  38. align="center"
  39. show-overflow-tooltip
  40. />
  41. <el-table-column
  42. prop="questionScore"
  43. label="满分"
  44. width="70"
  45. align="center"
  46. show-overflow-tooltip
  47. />
  48. <el-table-column
  49. prop="difficulty"
  50. label="难度"
  51. width="70"
  52. align="center"
  53. show-overflow-tooltip
  54. />
  55. <el-table-column
  56. prop="discrimination"
  57. label="区分度"
  58. width="80"
  59. align="center"
  60. show-overflow-tooltip
  61. />
  62. <el-table-column
  63. prop="averageScore"
  64. label="平均分"
  65. width="70"
  66. align="center"
  67. show-overflow-tooltip
  68. />
  69. <el-table-column
  70. prop="scoreRate"
  71. label="得分率"
  72. width="80"
  73. align="center"
  74. show-overflow-tooltip
  75. />
  76. <el-table-column
  77. prop="studentCount"
  78. label="答题人数"
  79. width="80"
  80. align="center"
  81. show-overflow-tooltip
  82. />
  83. <el-table-column
  84. prop="errorCount"
  85. label="错题人数"
  86. width="80"
  87. align="center"
  88. show-overflow-tooltip
  89. />
  90. <el-table-column
  91. prop="answerValue"
  92. label="正确答案"
  93. width="80"
  94. align="center"
  95. show-overflow-tooltip
  96. >
  97. <template #default="scope">
  98. {{ scope.row.answerValue ?? "-" }}
  99. </template>
  100. </el-table-column>
  101. <el-table-column prop="errorDetail" label="错题详情" align="center">
  102. <el-table-column
  103. :align="key == 'registrationCodeList' ? 'left' : 'center'"
  104. v-for="(key, index) in [
  105. 'name',
  106. 'rate',
  107. 'studentNum',
  108. 'registrationCodeList',
  109. ]"
  110. :key="key"
  111. label=""
  112. :prop="key"
  113. :min-width="[60, 70, 60, 384][index]"
  114. :show-overflow-tooltip="
  115. key == 'registrationCodeList' ? false : true
  116. "
  117. >
  118. <template #default="{ row: org }">
  119. <span v-if="key == 'registrationCodeList'">{{ org[key] }}</span>
  120. <span v-else-if="key == 'rate'">{{
  121. org[key] == "-" ? "-" : `${org[key]}%`
  122. }}</span>
  123. <span v-else>{{ org[key] }}</span>
  124. </template>
  125. </el-table-column>
  126. </el-table-column>
  127. </el-table>
  128. </template>
  129. </ReportModule>
  130. <ErrorsPdf
  131. ref="errPdfReport"
  132. :tableData="state.tableData"
  133. :className="analysisStore.filterObject.classGroupName"
  134. :subjectName="analysisStore.filterObject.subjectName || ''"
  135. :examName="getExamName"
  136. @PdfLoadEnd="PdfLoadEnd"
  137. />
  138. <!-- <ErrorsTablePdf
  139. ref="errPdfReport"
  140. :tableData="state.tableData"
  141. :className="analysisStore.filterObject.classGroupName"
  142. :subjectName="analysisStore.filterObject.subjectName || ''"
  143. :examName="getExamName"
  144. @PdfLoadEnd="PdfLoadEnd"
  145. /> -->
  146. </template>
  147. <script lang="ts" setup>
  148. import ReportModule from "@/components/ReportModule.vue";
  149. import { useAnalysisStore } from "@/store/analysis";
  150. import ErrorsPdf from "@/components/ErrorsPdf.vue";
  151. // import ErrorsTablePdf from "@/components/ErrorsTablePdf.vue";
  152. import { onMounted, reactive, computed, ref, watch } from "vue";
  153. import { errorQuestionAnalysis, exportErrorQuestion } from "@/api/analysis";
  154. import { loadingSvgIcon } from "@/utils/common";
  155. import {
  156. downloadExcel,
  157. GetExcelFileName,
  158. errorQuestionDataStaticHeaderData,
  159. errorQuestionDataChildHeaderData,
  160. } from "@/utils/exportExcel";
  161. // 定义表格行数据类型
  162. interface TableDataItem {
  163. questionId: string | number;
  164. questionName?: string;
  165. questionType?: string;
  166. questionScore?: number | string;
  167. difficulty?: number | string;
  168. discrimination?: number | string;
  169. averageScore?: number | string;
  170. scoreRate?: number | string;
  171. studentCount?: number;
  172. errorCount?: number;
  173. answerValue?: string;
  174. answerList?: any[];
  175. questionStatsList?: any[];
  176. rowspan?: number;
  177. colspan?: number;
  178. rowKey?: number;
  179. rowspanIndex?: number;
  180. [key: string]: any;
  181. }
  182. const state = reactive<{
  183. tableData: TableDataItem[];
  184. tableLoading: boolean;
  185. loadingText: string;
  186. }>({
  187. tableData: [],
  188. tableLoading: true,
  189. loadingText: "加载中,请稍后...",
  190. });
  191. const analysisStore = useAnalysisStore();
  192. const reportModuleRef = ref<any>(null);
  193. const errPdfReport = ref<any>(null);
  194. const getExamName = computed(() => {
  195. return analysisStore.analysisExamInfo.examName || "";
  196. });
  197. // 错题分析
  198. const GetErrorQuestionAnalysis = async () => {
  199. state.tableLoading = true;
  200. const res: any = await errorQuestionAnalysis({
  201. ...(analysisStore.filterObject as any),
  202. });
  203. if (res.code === 200) {
  204. const allTableData = res.data || [];
  205. state.tableData = [];
  206. const tableData = allTableData.filter((item: any) => {
  207. if (analysisStore.filterObject.classLevel == 0) {
  208. return item.classIdCode.indexOf("school") > -1;
  209. } else {
  210. return item.className == analysisStore.filterObject.classGroupName;
  211. }
  212. });
  213. if (tableData?.length) {
  214. tableData[0].questionStatsList.forEach((item: any, key: number) => {
  215. const answerListLen = item?.answerList?.length || 0;
  216. if (answerListLen == 0) {
  217. item.answerList = [
  218. {
  219. name: "-",
  220. rate: "-",
  221. registrationCodeList: "-",
  222. score: "-",
  223. studentNum: "-",
  224. },
  225. ];
  226. }
  227. const rowspan = item.answerList.length;
  228. item.answerList.forEach((answer: any, index: number) => {
  229. if (index == 0) {
  230. state.tableData.push({
  231. ...item,
  232. scoreRate: `${item.scoreRate}%`,
  233. ...answer,
  234. rowspan: rowspan, // 合并行
  235. colspan: 1,
  236. rowKey: index, // 行索引
  237. rowspanIndex: key, // 合并行之后的索引
  238. });
  239. } else {
  240. state.tableData.push({
  241. ...item,
  242. questionId: `${item.questionId}_${index}`,
  243. scoreRate: `${item.scoreRate}%`,
  244. ...answer,
  245. rowKey: index, // 行索引
  246. rowspanIndex: key, // 合并行之后的索引
  247. });
  248. }
  249. });
  250. });
  251. }
  252. } else {
  253. state.tableData = [];
  254. }
  255. state.tableLoading = false;
  256. };
  257. // 错题分析 合并单元格
  258. const SpanMethod = ({ row, column, rowIndex, columnIndex }: any) => {
  259. const props = [
  260. "errorDetail",
  261. "name",
  262. "rate",
  263. "studentNum",
  264. "registrationCodeList",
  265. ];
  266. if (props.indexOf(column.property) == -1) {
  267. if (row.rowspan) {
  268. return {
  269. rowspan: row.rowspan,
  270. colspan: row.colspan,
  271. };
  272. } else {
  273. return {
  274. rowspan: 0,
  275. colspan: 0,
  276. };
  277. }
  278. }
  279. };
  280. // 定义行样式
  281. const ErrorTableRowClassName = ({ row, rowIndex }: any) => {
  282. if (row.rowspanIndex % 2 === 0) {
  283. return "row_color_FFFFFF";
  284. } else {
  285. return "row_color_FAFAFA";
  286. }
  287. };
  288. // 设置单元格样式
  289. const ErrorTableCellClassName = ({
  290. row,
  291. column,
  292. rowIndex,
  293. columnIndex,
  294. }: any) => {
  295. if (column.property == "registrationCodeList") {
  296. return "white_space_normal";
  297. } else {
  298. return "";
  299. }
  300. };
  301. // 导出Excel
  302. const ExportExcel = () => {
  303. // 1. 设置加载状态
  304. reportModuleRef.value?.SetExportLoading?.(true);
  305. // 2. 参数
  306. const examName = getExamName.value;
  307. let params: any = {
  308. fileName: `${GetExcelFileName(examName, analysisStore.filterObject as any, "错题分析表")}`,
  309. examName: examName, // 考试名称
  310. sheetName: "错题分析表", // sheet页名称
  311. };
  312. const staticHeader: any[] = errorQuestionDataStaticHeaderData();
  313. const childHeader = ["name", "rate", "studentNum", "registrationCodeList"];
  314. const childHeaderData: any[] = errorQuestionDataChildHeaderData(childHeader);
  315. const staticHeaderData = [...staticHeader, ...childHeaderData];
  316. params.staticHeaderData = staticHeaderData;
  317. params.childHeaderData = [];
  318. params.dynamicsHeaderData = [];
  319. let dataList: any[] = [];
  320. state.tableData.forEach((item) => {
  321. const rowData: any[] = [];
  322. staticHeaderData.forEach((header: any) => {
  323. if (header.prop == "rate") {
  324. const rate = `${item[header.prop]}${item[header.prop] == "-" ? "" : "%"}`;
  325. rowData.push(rate);
  326. } else {
  327. rowData.push(item?.[header.prop] ?? "-");
  328. }
  329. });
  330. dataList.push(rowData);
  331. });
  332. params.dataList = dataList;
  333. params.mergeColumn = staticHeader.length - 1; // 合并的列
  334. // 3. 调用通用下载方法,并在完成后重置加载状态
  335. downloadExcel(exportErrorQuestion, params).finally(() => {
  336. reportModuleRef.value?.SetExportLoading?.(false);
  337. });
  338. };
  339. // 导出PDF
  340. const PrintPdf = () => {
  341. reportModuleRef.value?.SetPrintLoading?.(true);
  342. errPdfReport.value?.DownloadPdf?.();
  343. };
  344. // 导出PDF 加载完成
  345. const PdfLoadEnd = () => {
  346. reportModuleRef.value?.SetPrintLoading?.(false);
  347. };
  348. // 初始化
  349. const pageInit = () => {
  350. GetErrorQuestionAnalysis();
  351. };
  352. // 监听筛选条件
  353. watch(
  354. () => analysisStore.filterObject,
  355. async () => {
  356. pageInit();
  357. },
  358. { deep: true },
  359. );
  360. onMounted(() => {
  361. pageInit();
  362. });
  363. </script>
  364. <style lang="scss" scoped>
  365. :deep(.el-table) {
  366. .el-table__header-wrapper {
  367. .el-table__header {
  368. .is-group {
  369. tr {
  370. &:nth-child(2) {
  371. display: none;
  372. }
  373. }
  374. }
  375. }
  376. }
  377. .el-table__cell {
  378. &.white_space_normal {
  379. .cell {
  380. white-space: normal;
  381. padding: 10px 0 10px 10px !important;
  382. }
  383. }
  384. }
  385. &.el-table--striped {
  386. .el-table__body {
  387. tr {
  388. &.el-table__row--striped {
  389. &.row_color_FFFFFF {
  390. td.el-table__cell {
  391. background: #ffffff;
  392. }
  393. }
  394. }
  395. }
  396. }
  397. }
  398. &.el-table--striped
  399. .el-table__body
  400. tr.el-table__row.row_color_FAFAFA
  401. td.el-table__cell {
  402. background: #fafafa;
  403. }
  404. &.el-table--striped
  405. .el-table__body
  406. tr.el-table__row--striped.row_color_FAFAFA
  407. td.el-table__cell {
  408. background: #fafafa;
  409. }
  410. &.el-table--enable-row-hover .el-table__body tr.row_color_FFFFFF > td {
  411. background-color: transparent !important;
  412. }
  413. }
  414. </style>