optionAnalysis.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. <template>
  2. <ReportModule
  3. :showTitle="true"
  4. :titleList="[`1、${state.groupTitle}图`]"
  5. :showDescribe="true"
  6. tableOrChart="qita"
  7. :showPrintBtn="false"
  8. :showExportBtn="false"
  9. >
  10. <template #module_qita>
  11. <div class="module_chart_query">
  12. <div class="query_left">
  13. <span
  14. >满分:{{
  15. state.question?.resData?.[state.question.selectIndex]?.fullScore
  16. ? `${state.question.resData[state.question.selectIndex].fullScore}分`
  17. : "-"
  18. }}</span
  19. >
  20. <span
  21. >平均分:{{
  22. state.question?.resData?.[state.question.selectIndex]?.avgScore
  23. ? `${state.question.resData[state.question.selectIndex].avgScore}分`
  24. : "-"
  25. }}</span
  26. >
  27. <span
  28. >得分率:{{
  29. state.question?.resData?.[state.question.selectIndex]?.scoreRate
  30. ? `${state.question.resData[state.question.selectIndex].scoreRate}%`
  31. : "-"
  32. }}</span
  33. >
  34. <span
  35. >答案:{{
  36. state.question?.resData?.[state.question.selectIndex]
  37. ?.answerValue || "-"
  38. }}</span
  39. >
  40. </div>
  41. <div class="query_right report_button">
  42. <div class="select_question">
  43. <span>当前小题:</span>
  44. <el-select
  45. v-model="state.question.select"
  46. placeholder="请选择小题"
  47. style="width: 140px"
  48. @change="ChangeQuestionSelect"
  49. >
  50. <el-option
  51. v-for="item in state.question.questionList"
  52. :key="item?.questionId"
  53. :label="item?.questionName"
  54. :value="item?.questionId"
  55. ></el-option>
  56. </el-select>
  57. </div>
  58. <el-button
  59. class="select_btn"
  60. :disabled="state.question.selectIndex === 0"
  61. @click="PrevQuestion"
  62. >上一题</el-button
  63. >
  64. <el-button
  65. class="select_btn"
  66. :disabled="
  67. state.question.selectIndex === state.question.lastQuestionIndex
  68. "
  69. @click="NextQuestion"
  70. >下一题</el-button
  71. >
  72. </div>
  73. </div>
  74. <div class="module_content">
  75. <div class="content_left">
  76. <BarChart
  77. :datax="state.barChartData.datax"
  78. :datay="state.barChartData.datay"
  79. :isShowMarkLine="false"
  80. color=""
  81. typeName="人数"
  82. unit="人"
  83. ></BarChart>
  84. </div>
  85. <div class="content_right">
  86. <PieChart
  87. :key="state.pieKey"
  88. v-if="state.pieChart.data.length > 0"
  89. :data="state.pieChart.data"
  90. :refresh="state.pieChart.refresh"
  91. @refresh="GetPieRefresh"
  92. height="380"
  93. />
  94. </div>
  95. </div>
  96. </template>
  97. <template #module_describe>
  98. 说明:柱图代表每个选项的选择人数,饼图代表每个选项选择人数的占比。通过分析选择题选项的人数和占比,可以评估试题的质量。如果某个选项的选择人数与预期不符,可能意味着试题存在质量问题,需要进一步分析和调整。
  99. </template>
  100. </ReportModule>
  101. <ReportModule
  102. ref="reportModuleRef"
  103. :showTitle="true"
  104. :titleList="[`2、${state.groupTitle}表`]"
  105. :showDescribe="false"
  106. tableOrChart="table"
  107. :showPrintBtn="false"
  108. :showExportBtn="true"
  109. :currentPage="state.objectiveAnalysisData.currentPage"
  110. :pageSize="state.objectiveAnalysisData.pageSize"
  111. :total="state.objectiveAnalysisData.total"
  112. @ChangePageSize="ChangePageSize"
  113. @ChangeCurrentPage="ChangeCurrentPage"
  114. @ExportExcel="ExportExcel"
  115. >
  116. <template #module_table_chart>
  117. <el-table
  118. :data="objectiveTable"
  119. border
  120. stripe
  121. align="left"
  122. row-key="showCode"
  123. >
  124. <el-table-column
  125. prop="questionName"
  126. label="题号"
  127. width="74"
  128. align="center"
  129. fixed
  130. show-overflow-tooltip
  131. v-if="state.objectiveAnalysisData.tableData.length"
  132. />
  133. <el-table-column
  134. prop="questionType"
  135. label="题型"
  136. width="114"
  137. align="center"
  138. fixed
  139. show-overflow-tooltip
  140. v-if="state.objectiveAnalysisData.tableData.length"
  141. />
  142. <el-table-column
  143. prop="answerScore"
  144. label="分数"
  145. width="80"
  146. align="center"
  147. fixed
  148. show-overflow-tooltip
  149. v-if="state.objectiveAnalysisData.tableData.length"
  150. />
  151. <el-table-column
  152. prop="averageScore"
  153. label="平均分"
  154. width="80"
  155. align="center"
  156. fixed
  157. show-overflow-tooltip
  158. v-if="state.objectiveAnalysisData.tableData.length"
  159. />
  160. <el-table-column
  161. prop="scoreRate"
  162. label="得分率"
  163. width="80"
  164. align="center"
  165. fixed
  166. show-overflow-tooltip
  167. v-if="state.objectiveAnalysisData.tableData.length"
  168. >
  169. <template #default="{ row: org }">
  170. <span>{{ org["scoreRate"] }}%</span>
  171. </template>
  172. </el-table-column>
  173. <el-table-column
  174. prop="answerValue"
  175. label="答案"
  176. width="80"
  177. align="center"
  178. fixed
  179. show-overflow-tooltip
  180. v-if="state.objectiveAnalysisData.tableData.length"
  181. />
  182. <el-table-column
  183. label="各选项选择人数"
  184. align="center"
  185. v-if="state.objectiveAnalysisData.tableData.length"
  186. >
  187. <el-table-column
  188. align="center"
  189. v-for="key in state.objectiveAnalysisData.headLabels"
  190. :key="key"
  191. :label="key"
  192. min-width="70"
  193. show-overflow-tooltip
  194. >
  195. <template #default="{ row: org }">
  196. <span
  197. :style="{
  198. color:
  199. org?.['answerMap']?.[key]?.['studentNum'] &&
  200. org?.['answerMap']?.[key]?.['studentNum'] !== '0' &&
  201. org?.['answerMap']?.[key]?.['studentNum'] != '-'
  202. ? '#2E64FA'
  203. : '',
  204. cursor:
  205. org?.['answerMap']?.[key]?.['studentNum'] &&
  206. org?.['answerMap']?.[key]?.['studentNum'] !== '0' &&
  207. org?.['answerMap']?.[key]?.['studentNum'] != '-'
  208. ? 'pointer'
  209. : '',
  210. }"
  211. @click="
  212. showDialogData(
  213. org.questionCode,
  214. org.questionId,
  215. org?.['answerMap']?.[key]?.['name'],
  216. org?.['answerMap']?.[key]?.['studentNum'],
  217. org?.['answerMap']?.[key]?.['studentNames'],
  218. )
  219. "
  220. >{{ org?.["answerMap"]?.[key]?.["studentNum"] ?? "-" }}</span
  221. >
  222. </template>
  223. </el-table-column>
  224. </el-table-column>
  225. <el-table-column
  226. label="各选项选择人数占比"
  227. align="center"
  228. v-if="state.objectiveAnalysisData.tableData.length"
  229. >
  230. <el-table-column
  231. align="center"
  232. v-for="key in state.objectiveAnalysisData.headLabels"
  233. :key="key"
  234. :label="key"
  235. min-width="70"
  236. show-overflow-tooltip
  237. >
  238. <template #default="{ row: org }">
  239. <span
  240. v-if="
  241. org?.['answerMap']?.[key]?.['rate'] &&
  242. org?.['answerMap']?.[key]?.['rate'] != '-'
  243. "
  244. >{{ org?.["answerMap"]?.[key]?.["rate"] }}%</span
  245. >
  246. <span v-else>{{
  247. org?.["answerMap"]?.[key]?.["rate"] ?? "-"
  248. }}</span>
  249. </template>
  250. </el-table-column>
  251. </el-table-column>
  252. </el-table>
  253. </template>
  254. </ReportModule>
  255. <!-- 各选项选择人数 弹框 -->
  256. <div class="page_dialog">
  257. <el-dialog :title="state.dialogData.title" v-model="state.dialogData.show">
  258. <div class="dialog_center" :key="state.dialogData.dialogKey">
  259. <div class="center_header">
  260. <div class="header_left left_center">
  261. <span class="left_count">总计:{{ state.dialogData.total }}人</span>
  262. </div>
  263. <div class="header_right report_button">
  264. <el-button
  265. class="button_task_select"
  266. :loading="state.dialogExportLoading"
  267. @click="DialogExportExcel()"
  268. >
  269. <img
  270. v-if="!state.dialogExportLoading"
  271. src="@/assets/icon/export_icon.webp"
  272. alt="导出Excel"
  273. />导出Excel
  274. </el-button>
  275. </div>
  276. </div>
  277. <div class="page_jg_20"></div>
  278. <div class="page_table table_row_42" style="width: 100%">
  279. <el-table
  280. :data="state.dialogData.list"
  281. height="471px"
  282. border
  283. stripe
  284. row-key="studentCode"
  285. >
  286. <el-table-column
  287. type="index"
  288. align="center"
  289. label="序号"
  290. width="50"
  291. fixed
  292. >
  293. <template #default="{ $index }">
  294. {{
  295. (state.dialogData.currentPage - 1) *
  296. state.dialogData.pageSize +
  297. $index +
  298. 1
  299. }}
  300. </template>
  301. </el-table-column>
  302. <el-table-column
  303. align="center"
  304. property="studentCode"
  305. label="学号"
  306. width="100"
  307. show-overflow-tooltip
  308. fixed
  309. ></el-table-column>
  310. <el-table-column
  311. align="center"
  312. property="studentName"
  313. label="姓名"
  314. width="80"
  315. show-overflow-tooltip
  316. fixed
  317. ></el-table-column>
  318. <el-table-column
  319. align="center"
  320. property="examCode"
  321. label="考号"
  322. width="80"
  323. show-overflow-tooltip
  324. >
  325. <template #default="{ row }">
  326. {{ row.examCode || "-" }}
  327. </template>
  328. </el-table-column>
  329. <el-table-column
  330. align="center"
  331. property="className"
  332. label="行政班"
  333. width="80"
  334. show-overflow-tooltip
  335. >
  336. <template #default="{ row }">
  337. {{ row.className || "-" }}
  338. </template>
  339. </el-table-column>
  340. <el-table-column
  341. align="center"
  342. property="teacherClassName"
  343. label="教学班"
  344. width="80"
  345. show-overflow-tooltip
  346. >
  347. <template #default="{ row }">
  348. {{ row.teacherClassName || "-" }}
  349. </template>
  350. </el-table-column>
  351. <el-table-column
  352. align="center"
  353. v-for="(item, index) in state.dialogData.headerList"
  354. :key="item"
  355. :label="item"
  356. min-width="60"
  357. show-overflow-tooltip
  358. >
  359. <template #header>
  360. <div class="table_header">
  361. <el-tooltip
  362. class="item"
  363. effect="dark"
  364. :content="item"
  365. placement="top"
  366. >
  367. <span>{{ item }}</span>
  368. </el-tooltip>
  369. </div>
  370. </template>
  371. <template #default="scope">
  372. <span
  373. :style="{
  374. color:
  375. scope.row.questionList?.[index]?.answerValue ===
  376. scope.row.questionList?.[index]?.studentAnswer
  377. ? '#606266'
  378. : '#EE6666',
  379. }"
  380. >{{ scope.row.questionList[index].studentAnswer }}</span
  381. >
  382. </template>
  383. </el-table-column>
  384. </el-table>
  385. <div class="page_pagination" v-if="state.dialogData.total > 10">
  386. <el-pagination
  387. @current-change="GetStudentTableDataChangePage"
  388. layout="prev, pager, next"
  389. :current-page="state.dialogData.currentPage"
  390. :page-size="state.dialogData.pageSize"
  391. :total="state.dialogData.total"
  392. >
  393. </el-pagination>
  394. </div>
  395. </div>
  396. </div>
  397. </el-dialog>
  398. </div>
  399. </template>
  400. <script lang="ts" setup>
  401. import ReportModule from "@/components/ReportModule.vue";
  402. import BarChart from "@/components/echarts/barChart.vue"; //单柱状图
  403. import PieChart from "@/components/echarts/pieChart.vue";
  404. import { onMounted, watch, computed, ref, reactive } from "vue";
  405. import { useAnalysisStore } from "@/store/analysis";
  406. import {
  407. selectQuestionAnalysis,
  408. selectQuestionAnalysisPage,
  409. selectQuestionStudentPageList,
  410. exportObjectAnalysis,
  411. publicExportSelectQuestion,
  412. } from "@/api/analysis";
  413. import {
  414. downloadExcel,
  415. GetExcelFileName,
  416. selectQuestionAnalysisStaticHeaderData,
  417. selectQuestionAnalysisDynamicsHeaderData,
  418. } from "@/utils/exportExcel";
  419. const analysisStore = useAnalysisStore();
  420. const reportModuleRef = ref<any>(null);
  421. const getExamName = computed(() => {
  422. return analysisStore.analysisExamInfo.examName || "";
  423. });
  424. const state = reactive({
  425. groupTitle: "选项分析",
  426. question: {
  427. resData: [] as any[], // 修复:显式声明为 any[]
  428. select: "",
  429. questionList: [] as any[], // 修复:显式声明为 any[]
  430. selectIndex: 0,
  431. lastQuestionIndex: null as number | null, // 修复:允许 number 或 null
  432. },
  433. barChartData: {
  434. datax: [] as string[], // 修复
  435. datay: [] as number[], // 修复
  436. },
  437. pieChart: {
  438. refresh: false,
  439. data: [] as any[], // 修复
  440. },
  441. objectiveAnalysisData: {
  442. tableData: [] as any[], // 修复
  443. originalTableData: [] as any[], // 修复
  444. data: [] as any[], // 修复
  445. headLabels: [] as string[], // 修复
  446. pageSize: 10,
  447. total: 0,
  448. currentPage: 1,
  449. },
  450. dialogData: {
  451. dialogKey: 1,
  452. title: "",
  453. questionId: "",
  454. answer: "",
  455. studentCodeList: [] as string[], // 修复
  456. show: false,
  457. pageSize: 10,
  458. total: 0,
  459. currentPage: 1,
  460. list: [] as any[], // 修复
  461. headerList: [] as string[], // 修复
  462. studentNum: 0,
  463. },
  464. exportLoading: false,
  465. dialogExportLoading: false,
  466. pieKey: 0,
  467. });
  468. const objectiveTable = computed(() => {
  469. const start =
  470. (state.objectiveAnalysisData.currentPage - 1) *
  471. state.objectiveAnalysisData.pageSize;
  472. const end = start + state.objectiveAnalysisData.pageSize;
  473. return state.objectiveAnalysisData.tableData.slice(start, end);
  474. });
  475. const PageInit = (isRefresh: boolean) => {
  476. state.objectiveAnalysisData.currentPage = 1;
  477. if (isRefresh) {
  478. state.pieChart.refresh = true;
  479. }
  480. GetSelectQuestionAnalysisChartData(); //客观题分析(选择题分析图)
  481. GetSelectQuestionAnalysisPage(); //客观题分析(选择题分析表-分页)
  482. };
  483. //客观题分析(选择题分析图)
  484. const GetSelectQuestionAnalysisChartData = () => {
  485. selectQuestionAnalysis({ ...analysisStore.filterObject }).then((res) => {
  486. state.question.questionList = []; //小题下拉选项
  487. state.question.select = ""; //当前选择小题的值
  488. state.question.selectIndex = 0; //当前选择小题的索引
  489. state.question.lastQuestionIndex = null; //最后一题索引
  490. state.question.resData = [];
  491. if (res.code == 200 && res.data && res.data.length) {
  492. const resData = res.data || [];
  493. state.question.resData = resData;
  494. state.question.questionList = resData.map((item: any) => {
  495. return {
  496. questionId: item.questionId,
  497. questionName: item.questionName,
  498. };
  499. });
  500. if (state.question.questionList.length > 0) {
  501. state.question.select = state.question.questionList[0].questionId;
  502. state.question.lastQuestionIndex =
  503. state.question.questionList.length - 1; //最后一题索引
  504. } else {
  505. state.question.lastQuestionIndex = 0;
  506. }
  507. state.pieKey++;
  508. GetChartData(); //1、选择题分析图 柱状图
  509. } else {
  510. state.barChartData.datax = []; // 1、选择题分析图 柱状图 X轴
  511. state.barChartData.datay = []; // 1、选择题分析图 柱状图 Y轴
  512. state.pieChart.data = []; //1、选择题分析图 饼状图
  513. state.question.questionList = []; //小题下拉选项
  514. state.question.select = ""; //当前选择小题的值
  515. state.question.selectIndex = 0; //当前选择小题的索引
  516. state.question.lastQuestionIndex = null; //最后一题索引
  517. state.question.resData = [];
  518. }
  519. });
  520. };
  521. //切换小题下拉框
  522. const ChangeQuestionSelect = () => {
  523. state.question.selectIndex = state.question.questionList.findIndex(
  524. (item) => item.questionId == state.question.select,
  525. );
  526. state.pieChart.refresh = true;
  527. GetChartData();
  528. };
  529. //上一题
  530. const PrevQuestion = () => {
  531. if (state.question.selectIndex > 0) {
  532. state.question.selectIndex--;
  533. state.question.select =
  534. state.question.questionList[state.question.selectIndex].questionId;
  535. state.pieChart.refresh = true;
  536. GetChartData();
  537. }
  538. };
  539. //下一题
  540. const NextQuestion = () => {
  541. if (state.question.selectIndex < state.question.lastQuestionIndex) {
  542. state.question.selectIndex++;
  543. state.question.select =
  544. state.question.questionList[state.question.selectIndex].questionId;
  545. state.pieChart.refresh = true;
  546. GetChartData();
  547. }
  548. };
  549. //获取当前选择的小题 柱状图 + 饼状图
  550. const GetChartData = () => {
  551. const answerScoreData =
  552. state.question?.resData[state.question.selectIndex]?.answerScore || [];
  553. state.barChartData.datax = []; // 1、选择题分析图 柱状图 X轴
  554. state.barChartData.datay = []; // 1、选择题分析图 柱状图 Y轴
  555. state.pieChart.data = []; //1、选择题分析图 饼状图
  556. answerScoreData.forEach((item: any) => {
  557. state.barChartData.datax.push(item.name);
  558. state.barChartData.datay.push(item.studentNum);
  559. state.pieChart.data.push({
  560. value: item.rate,
  561. name: item.name,
  562. });
  563. });
  564. };
  565. const GetPieRefresh = (val: boolean) => {
  566. state.pieChart.refresh = val;
  567. };
  568. //客观题分析(选择题分析表-分页)
  569. const GetSelectQuestionAnalysisPage = () => {
  570. selectQuestionAnalysisPage({
  571. ...analysisStore.filterObject,
  572. pageParam: {
  573. pageNum: state.objectiveAnalysisData.currentPage, //当前页码
  574. pageSize: 200, //每页显示行数
  575. },
  576. }).then((res) => {
  577. if (res.code == 200 && res.data) {
  578. const { total, records } = res.data;
  579. if (records?.length) {
  580. state.objectiveAnalysisData.tableData = records[0]?.pageResult || []; //表格数据
  581. state.objectiveAnalysisData.headLabels = records[0]?.label || []; //选项头部信息
  582. state.objectiveAnalysisData.total = Number(total); //总条数
  583. } else {
  584. state.objectiveAnalysisData.tableData = [];
  585. state.objectiveAnalysisData.headLabels = [];
  586. state.objectiveAnalysisData.total = 0;
  587. }
  588. } else {
  589. state.objectiveAnalysisData.tableData = [];
  590. state.objectiveAnalysisData.headLabels = [];
  591. state.objectiveAnalysisData.total = 0;
  592. }
  593. });
  594. };
  595. //分页切换
  596. const ChangeCurrentPage = (val: number) => {
  597. state.objectiveAnalysisData.currentPage = val;
  598. };
  599. const ChangePageSize = (val: number) => {
  600. state.objectiveAnalysisData.pageSize = val;
  601. state.objectiveAnalysisData.currentPage = 1;
  602. };
  603. // 显示 各选项选择人数 弹框
  604. const showDialogData = (
  605. questionCode: string,
  606. questionId: string,
  607. answer: string,
  608. studentNum: any,
  609. studentCodeList: any[], // 增加类型声明
  610. ) => {
  611. if (!studentNum || studentNum == "0" || studentNum == "-") {
  612. return false;
  613. }
  614. console.log("questionCode22");
  615. state.dialogData.title = `${questionCode} ${answer}选项人数`;
  616. state.dialogData.show = true;
  617. state.dialogData.currentPage = 1;
  618. state.dialogData.questionId = questionId;
  619. state.dialogData.answer = answer;
  620. state.dialogData.studentCodeList = studentCodeList || []; // 防止传入 undefined
  621. state.dialogData.studentNum = studentNum;
  622. GetSelectQuestionStudentPageList(studentCodeList || [], studentNum);
  623. };
  624. const GetSelectQuestionStudentPageList = (
  625. studentCodeList: any[],
  626. studentNum: any,
  627. ) => {
  628. state.dialogData.dialogKey += 1;
  629. const start = (state.dialogData.currentPage - 1) * state.dialogData.pageSize;
  630. const end = start + state.dialogData.pageSize;
  631. // 修复:使用可选链 ?. 防止 studentCodeList 为 undefined 时调用 slice 报错
  632. const studentCodeListData = studentCodeList?.slice(start, end) || [];
  633. state.dialogData.total = Number(studentNum);
  634. selectQuestionStudentPageList({
  635. ...analysisStore.filterObject,
  636. studentCodeList: studentCodeListData,
  637. pageParam: {
  638. pageNum: 1,
  639. pageSize: state.dialogData.pageSize,
  640. },
  641. }).then((res) => {
  642. const { code, data } = res;
  643. if (code == 200) {
  644. const { total, records } = data;
  645. state.dialogData.list = records[0]?.studentPageList || [];
  646. state.dialogData.headerList = records[0]?.headerList || [];
  647. }
  648. });
  649. };
  650. //弹框分页切换
  651. const GetStudentTableDataChangePage = (val: number) => {
  652. state.dialogData.currentPage = val;
  653. GetSelectQuestionStudentPageList(
  654. state.dialogData.studentCodeList,
  655. state.dialogData.studentNum,
  656. );
  657. };
  658. //弹框导出
  659. const DialogExportExcel = () => {
  660. state.dialogExportLoading = true;
  661. const examName = getExamName.value;
  662. const title = state.dialogData.title;
  663. const fileName = `${GetExcelFileName(examName, analysisStore.filterObject, `${state.groupTitle}表_${title.replace(/\s/g, "")}`)}`;
  664. const sheetName = title.replace(/\s/g, "");
  665. let params = {
  666. ...analysisStore.filterObject,
  667. studentCodeList: state.dialogData.studentCodeList,
  668. fileName,
  669. sheetName,
  670. };
  671. downloadExcel(publicExportSelectQuestion, params).finally(() => {
  672. state.dialogExportLoading = false;
  673. });
  674. };
  675. // 客观题分析表数据导出
  676. const ExportExcel = () => {
  677. reportModuleRef.value?.SetExportLoading?.(true);
  678. const examName = getExamName.value;
  679. const staticHeaderData = selectQuestionAnalysisStaticHeaderData();
  680. const dynamicsHeaderData = selectQuestionAnalysisDynamicsHeaderData();
  681. const childHeaderData = state.objectiveAnalysisData.headLabels.map(
  682. (item: string) => ({
  683. label: item,
  684. }),
  685. );
  686. let dataList: any[][] = [];
  687. state.objectiveAnalysisData.tableData.forEach((row) => {
  688. const staticData = staticHeaderData.map((header) => {
  689. if (header.prop == "scoreRate") {
  690. return `${row["scoreRate"]}%`;
  691. } else {
  692. return row[header.prop];
  693. }
  694. });
  695. const studentNumData = childHeaderData.map((child) => {
  696. const value = row?.["answerMap"]?.[child.label]?.["studentNum"] ?? "-";
  697. if (child.label == row.answerValue) {
  698. return `${value}答案`;
  699. } else {
  700. return value;
  701. }
  702. });
  703. const rateData = childHeaderData.map((child) => {
  704. const value = `${row?.["answerMap"]?.[child.label]?.["rate"]}${row?.["answerMap"]?.[child.label]?.["rate"] && row?.["answerMap"]?.[child.label]?.["rate"] != "-" ? "%" : ""}`;
  705. if (child.label == row.answerValue) {
  706. return `${value}答案`;
  707. } else {
  708. return value;
  709. }
  710. });
  711. dataList.push([...staticData, ...studentNumData, ...rateData]);
  712. });
  713. let params = {
  714. fileName: `${GetExcelFileName(examName, analysisStore.filterObject, state.groupTitle)}`,
  715. examName: examName, //考试名称
  716. sheetName: state.groupTitle, //sheet页名称
  717. staticHeaderData: staticHeaderData, //静态表头
  718. dynamicsHeaderData: dynamicsHeaderData, //动态表头的一级表头
  719. childHeaderData: childHeaderData, //二级表头
  720. dataList: dataList, //数据
  721. };
  722. downloadExcel(exportObjectAnalysis, params).finally(() => {
  723. reportModuleRef.value?.SetExportLoading?.(false);
  724. });
  725. };
  726. // 监听筛选条件
  727. watch(
  728. () => analysisStore.filterObject,
  729. async () => {
  730. PageInit(true);
  731. },
  732. { deep: true },
  733. );
  734. onMounted(() => {
  735. PageInit(false);
  736. });
  737. </script>
  738. <style lang="scss" scoped>
  739. :deep(.module_qita) {
  740. flex-direction: column;
  741. .module_chart_query {
  742. width: calc(100% - 40px);
  743. margin: auto;
  744. font-size: 14px;
  745. color: #333333;
  746. text-align: left;
  747. display: flex;
  748. align-items: center;
  749. justify-content: space-between;
  750. margin-bottom: 20px;
  751. .query_left {
  752. display: inline-flex;
  753. flex: 1;
  754. span {
  755. display: inline-flex;
  756. align-items: center;
  757. justify-content: center;
  758. min-width: 120px;
  759. padding: 0 8px;
  760. font-size: 14px;
  761. color: #2e64fa;
  762. box-sizing: border-box;
  763. height: 36px;
  764. background: rgba(46, 100, 250, 0.1);
  765. border-radius: 4px;
  766. margin-right: 20px;
  767. &:last-child {
  768. margin-right: 0;
  769. }
  770. }
  771. }
  772. .query_right {
  773. display: flex;
  774. justify-content: flex-end;
  775. align-items: center;
  776. .select_question {
  777. color: #333333;
  778. font-size: 14px;
  779. font-weight: 400;
  780. }
  781. }
  782. }
  783. .report_button {
  784. gap: 10px;
  785. .select_btn {
  786. border: 1px solid #2e64fa;
  787. color: #2e64fa;
  788. margin-left: 0;
  789. &:hover {
  790. border-color: #2e64fa;
  791. background-color: rgba(46, 100, 250, 0.1);
  792. }
  793. &.is-disabled {
  794. color: #bbbbbb;
  795. border-color: #bbbbbb;
  796. }
  797. }
  798. }
  799. .module_content {
  800. display: flex;
  801. width: 100%;
  802. .content_left {
  803. width: 50%;
  804. }
  805. .content_right {
  806. width: 50%;
  807. }
  808. }
  809. }
  810. //页面表格公共样式
  811. :deep(.page_table) {
  812. width: 100%;
  813. // 表格悬浮效果覆盖
  814. .el-table__body tr:hover > td {
  815. background-color: #eaf0ff !important; // 蓝色系悬浮背景
  816. }
  817. //表格竖着的边框线样式
  818. th.el-table__cell {
  819. border-right: 1px solid #e9e9e9;
  820. }
  821. .el-table th.el-table__cell.is-leaf,
  822. .el-table td.el-table__cell {
  823. border-bottom: 1px solid #e9e9e9;
  824. }
  825. .el-table {
  826. min-height: 463px;
  827. border: 0px;
  828. }
  829. }
  830. //表格42行高
  831. .table_row_42 {
  832. :deep(.el-table) {
  833. thead {
  834. color: #333 !important;
  835. font-weight: 500;
  836. font-size: 14px;
  837. height: 42px !important;
  838. }
  839. .el-table__cell {
  840. padding: 0;
  841. height: 42px !important;
  842. }
  843. }
  844. }
  845. :deep(.report_button) {
  846. display: flex;
  847. gap: 10px;
  848. .el-input-group__append {
  849. .el-button {
  850. width: 100%;
  851. justify-content: center;
  852. }
  853. }
  854. .el-icon-loading {
  855. color: #666;
  856. }
  857. .el-button {
  858. margin-left: 0px;
  859. height: 36px;
  860. line-height: 36px;
  861. padding: 0 10px;
  862. display: flex;
  863. align-items: center;
  864. span {
  865. // padding: 0px 10px;
  866. display: flex;
  867. align-items: center;
  868. }
  869. img {
  870. width: 16px;
  871. height: 16px;
  872. margin-right: 4px;
  873. }
  874. }
  875. .el-button--default {
  876. color: #666;
  877. font-weight: 400;
  878. &.is-disabled {
  879. color: #bbbbbb;
  880. border-color: #bbbbbb;
  881. }
  882. }
  883. .el-button--primary.is-plain:hover {
  884. color: #2e64fa;
  885. background-color: #c0d1fe;
  886. font-size: 14px;
  887. }
  888. //其他按钮可在下面追加
  889. //对标任务选择按钮
  890. .button_task_select {
  891. // 去掉按钮点击后的样式
  892. &:active,
  893. &:focus {
  894. outline: none; // 去掉点击后的轮廓
  895. box-shadow: none; // 去掉点击后的阴影
  896. background-color: inherit; // 保持背景颜色不变
  897. color: #666666; // 保持文字颜色不变
  898. border: 1px solid #dcdfe6;
  899. }
  900. }
  901. .button_task_select:hover {
  902. background-color: #f9f9f9;
  903. border: 1px solid #dcdfe6;
  904. color: #666;
  905. }
  906. }
  907. </style>