groupAnalysis.vue 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591
  1. <template>
  2. <ReportModule
  3. :showTitle="true"
  4. :titleList="[state.groupTitle]"
  5. :showDescribe="true"
  6. tableOrChart="chart"
  7. :showPrintBtn="false"
  8. :showExportBtn="false"
  9. >
  10. <template #title_right>
  11. <EchartType
  12. :chartTypeList="state.problemAnalysisData.chartTypeList"
  13. :current="state.problemAnalysisData.chartType"
  14. @ChangeEchartType="
  15. (val) => ChangeEchartType(val, 'problemAnalysisData')
  16. "
  17. />
  18. </template>
  19. <template #module_table_chart>
  20. <template v-if="state.problemAnalysisData.data.length > 0">
  21. <BarLineCharts
  22. v-if="state.problemAnalysisData.chartType == 'line_bar_chart'"
  23. :legendList="state.problemAnalysisData.legendList"
  24. :showBarLegendIndex="state.problemAnalysisData.showBarLegendIndex"
  25. title="得分率"
  26. :data="state.problemAnalysisData.data"
  27. @HandleChartClick="HandleChartClick"
  28. @ChangeChartOrder="
  29. (sortType, legendData, barIndex) =>
  30. ChangeChartOrder(sortType, legendData, barIndex, 1)
  31. "
  32. >
  33. </BarLineCharts>
  34. <BarsCharts
  35. v-if="state.problemAnalysisData.chartType == 'vertical_bar'"
  36. :key="state.chartKey"
  37. :data="state.problemAnalysisData.data"
  38. :legendList="state.problemAnalysisData.legendList"
  39. :showSortSelectbox="true"
  40. unit="%"
  41. title="得分率"
  42. :isClick="true"
  43. @HandleChartClick="HandleChartClick"
  44. @ChangeChartOrder="
  45. (sortType, legendData, barIndex) =>
  46. ChangeChartOrder(sortType, legendData, barIndex, 1)
  47. "
  48. >
  49. </BarsCharts>
  50. <RadarCharts
  51. v-if="state.problemAnalysisData.chartType == 'radar_chart'"
  52. :key="state.chartKey"
  53. :data="state.problemAnalysisData.data"
  54. :legendList="state.problemAnalysisData.legendList"
  55. :showCheckBox="true"
  56. :openShowAllLegend="true"
  57. :isClick="true"
  58. @HandleChartClick="HandleChartClick"
  59. >
  60. </RadarCharts>
  61. </template>
  62. <div
  63. v-else
  64. class="no_content_data"
  65. v-loading="state.dataLoading"
  66. :element-loading-text="state.loadingText"
  67. element-loading-spinner="el-icon-loading"
  68. element-loading-background="#ffffff"
  69. >
  70. <span>暂无数据</span>
  71. </div>
  72. </template>
  73. <template #module_describe>
  74. 展示每道试题的得分率图。得分率指实际得分/考核分的比值,换算成的百分数。可以用于分析每道试题的难易程度和质量,试题得分率高意味着试题难度低或者学生整体水平高;得分率低意味着试题难度较高或者考生整体水平较低。点击每道试题的柱或雷达图的题号可在下方查看该题每个班的得分率情况。
  75. </template>
  76. </ReportModule>
  77. <ReportModule
  78. :showTitle="true"
  79. :titleList="[state.groupTitle, state.groupName]"
  80. :showDescribe="true"
  81. tableOrChart="chart"
  82. :showPrintBtn="false"
  83. :showExportBtn="false"
  84. >
  85. <template #title_right>
  86. <EchartType
  87. :chartTypeList="state.majorQuestionData.chartTypeList"
  88. :current="state.majorQuestionData.chartType"
  89. @ChangeEchartType="(val) => ChangeEchartType(val, 'majorQuestionData')"
  90. />
  91. </template>
  92. <template #module_table_chart>
  93. <template v-if="state.majorQuestionData.data.length > 0">
  94. <BarLineCharts
  95. ref="majorQuestionCharts"
  96. v-if="state.majorQuestionData.chartType == 'line_bar_chart'"
  97. :legendList="state.majorQuestionData.legendList"
  98. :showBarLegendIndex="state.majorQuestionData.showBarLegendIndex"
  99. title="得分率"
  100. :data="state.majorQuestionData.data"
  101. @HandleChartClick="HandleQuestionChartClick"
  102. @ChangeChartOrder="
  103. (sortType, legendData, barIndex) =>
  104. ChangeChartOrder(sortType, legendData, barIndex, 2)
  105. "
  106. >
  107. </BarLineCharts>
  108. <BarsCharts
  109. ref="majorQuestionCharts"
  110. :key="state.chartKey"
  111. v-if="state.majorQuestionData.chartType == 'vertical_bar'"
  112. :data="state.majorQuestionData.data"
  113. :showSortSelectbox="true"
  114. :legendList="state.majorQuestionData.legendList"
  115. unit="%"
  116. title="得分率"
  117. :isClick="true"
  118. @HandleChartClick="HandleQuestionChartClick"
  119. @ChangeChartOrder="
  120. (sortType, legendData, barIndex) =>
  121. ChangeChartOrder(sortType, legendData, barIndex, 2)
  122. "
  123. ></BarsCharts>
  124. <RadarCharts
  125. v-if="state.majorQuestionData.chartType == 'radar_chart'"
  126. :key="state.chartKey"
  127. :data="state.majorQuestionData.data"
  128. :legendList="state.majorQuestionData.legendList"
  129. :showCheckBox="true"
  130. :openShowAllLegend="true"
  131. :isClick="true"
  132. @HandleChartClick="HandleQuestionChartClick"
  133. >
  134. </RadarCharts>
  135. </template>
  136. <div
  137. v-else
  138. class="no_content_data"
  139. v-loading="state.dataLoading"
  140. :element-loading-text="state.loadingText"
  141. element-loading-spinner="el-icon-loading"
  142. element-loading-background="#ffffff"
  143. >
  144. <span>暂无数据</span>
  145. </div>
  146. </template>
  147. <template #module_describe>
  148. 展示每道试题的得分率图。得分率指实际得分/考核分的比值,换算成的百分数。可以用于分析每道试题的难易程度和质量,试题得分率高意味着试题难度低或者学生整体水平高;得分率低意味着试题难度较高或者考生整体水平较低。点击每道试题的柱或雷达图的题号可在下方查看该题每个班的得分率情况。
  149. </template>
  150. </ReportModule>
  151. <ReportModule
  152. :showTitle="true"
  153. :titleList="[state.groupTitle, state.questionTitle]"
  154. :showDescribe="true"
  155. tableOrChart="chart"
  156. :showPrintBtn="false"
  157. :showExportBtn="false"
  158. >
  159. <template #title_right>
  160. <EchartType
  161. :chartTypeList="state.questionScoreStatsData.chartTypeList"
  162. :current="state.questionScoreStatsData.chartType"
  163. @ChangeEchartType="
  164. (val) => ChangeEchartType(val, 'questionScoreStatsData')
  165. "
  166. />
  167. </template>
  168. <template #module_table_chart>
  169. <template v-if="state.questionScoreStatsData.datax.length > 0">
  170. <BarScoringRateVertical
  171. v-if="state.questionScoreStatsData.chartType == 'vertical_bar'"
  172. :datax="state.questionScoreStatsData.datax"
  173. :datay="state.questionScoreStatsData.dataStackY"
  174. :tooltipData="state.questionScoreStatsData.tooltipData"
  175. :isShowMarkLine="true"
  176. :average="state.questionScoreStatsData.rate"
  177. :markLineData="state.questionScoreStatsData.markLineData"
  178. :color="['#3BA272', '#EE6666']"
  179. typeName="得分率"
  180. :isClick="true"
  181. @HandleChartClick="HandleQuestionScoreChartClick"
  182. ></BarScoringRateVertical>
  183. <DifferenceChart
  184. v-if="state.questionScoreStatsData.chartType == 'difference_chart'"
  185. :datax="state.questionScoreStatsData.datax"
  186. :datay="state.questionScoreStatsData.datay"
  187. unit="%"
  188. type="1"
  189. title="得分率"
  190. :rate="state.questionScoreStatsData.rate"
  191. @HandleChartClick="HandleQuestionScoreChartClick"
  192. >
  193. </DifferenceChart>
  194. </template>
  195. <div
  196. v-else
  197. class="no_content_data"
  198. v-loading="state.dataLoading"
  199. :element-loading-text="state.loadingText"
  200. element-loading-spinner="el-icon-loading"
  201. element-loading-background="#ffffff"
  202. >
  203. <span>暂无数据</span>
  204. </div>
  205. </template>
  206. <template #module_describe>
  207. 说明:通过上面图形,可查看该题每个班的得分率情况,由图中可以看出,得分率最高为<span
  208. style="color: #3ba272"
  209. >{{ state.questionScoreStatsData.maxClass }}</span
  210. >,最低为<span style="color: #ee6666">{{
  211. state.questionScoreStatsData.minClass
  212. }}</span
  213. >。点击每个班级的柱可在下方查看该班各个选项/得分的人数分布。
  214. </template>
  215. </ReportModule>
  216. <ReportModule
  217. :showTitle="true"
  218. :titleList="[state.groupTitle, state.questionTitle, state.classTitle]"
  219. :showDescribe="true"
  220. tableOrChart="qita"
  221. :showPrintBtn="false"
  222. :showExportBtn="false"
  223. >
  224. <template #module_qita>
  225. <div class="content_left answer">
  226. <BarChart
  227. v-if="state.questionAnswerData.datax.length"
  228. :datax="state.questionAnswerData.datax"
  229. :datay="state.questionAnswerData.datay"
  230. typeName="人数"
  231. :showNuitY="false"
  232. unit="人"
  233. :unitX="
  234. state.questionAnswerData.questionType == '单选题' ||
  235. state.questionAnswerData.questionType == '多选题' ||
  236. state.questionAnswerData.questionType == '判断题'
  237. ? ''
  238. : '分'
  239. "
  240. :showMarkPoint="false"
  241. :isShowMarkLine="false"
  242. :color="state.questionAnswerData.color"
  243. :answerValue="state.questionAnswerData.answerValue"
  244. :answerScore="state.questionAnswerData.answerScore"
  245. :fullMark="state.questionAnswerData.fullMark"
  246. :isClick="true"
  247. @HandleChartClick="HandleQuestionAnswerChartClick"
  248. style="height: 300px"
  249. ></BarChart>
  250. <div
  251. v-else
  252. class="module_chart no_content_data"
  253. v-loading="state.dataLoading"
  254. :element-loading-text="state.loadingText"
  255. element-loading-spinner="el-icon-loading"
  256. element-loading-background="#ffffff"
  257. >
  258. <span>暂无数据</span>
  259. </div>
  260. </div>
  261. <div class="content_right answer table_42">
  262. <el-table
  263. border
  264. :data="state.questionAnswerData.tableData"
  265. stripe
  266. align="left"
  267. :header-row-style="HeaderRowStyle"
  268. >
  269. <el-table-column :label="state.classTitle" align="center">
  270. <el-table-column
  271. prop="title"
  272. align="center"
  273. label="名称"
  274. ></el-table-column>
  275. <el-table-column
  276. prop="value"
  277. align="center"
  278. label="数值"
  279. ></el-table-column>
  280. </el-table-column>
  281. </el-table>
  282. </div>
  283. </template>
  284. <template #module_describe>
  285. 说明:通过柱图,可查看该题下该班所有学生的选项或试题得分情况。点击每个柱可查看该选项或分数的学生明细和答题卡答题情况。
  286. </template>
  287. </ReportModule>
  288. <ReportModule
  289. :showTitle="true"
  290. :titleList="[
  291. state.groupTitle,
  292. state.questionTitle,
  293. state.classTitle,
  294. state.optionTitle,
  295. ]"
  296. :showDescribe="true"
  297. tableOrChart="qita"
  298. :showPrintBtn="false"
  299. :showExportBtn="false"
  300. >
  301. <template #title_right>
  302. <el-button
  303. class="default_button"
  304. v-if="
  305. state.questionAnswerData.questionType != '单选题' &&
  306. state.questionAnswerData.questionType != '多选题' &&
  307. state.questionAnswerData.questionType != '判断题'
  308. "
  309. @click="VisibleQuestionCard"
  310. >
  311. <img src="@/assets/icon/card_view.webp" />批量查看
  312. </el-button>
  313. </template>
  314. <template #module_qita>
  315. <div class="content_left paper_card">
  316. <StudentQuestionImg :paperInfo="state.paperInfos"></StudentQuestionImg>
  317. </div>
  318. <div class="content_right paper_card table_42">
  319. <el-table
  320. border
  321. :data="state.majorAnswerData.tableData"
  322. stripe
  323. highlight-current-row
  324. align="left"
  325. height="401px"
  326. row-key="studentRegistrationCode"
  327. :row-style="{ cursor: 'pointer' }"
  328. :row-class-name="TableRowClassName"
  329. @row-click="HandleRowClick"
  330. >
  331. <el-table-column
  332. prop="studentUserName"
  333. align="center"
  334. label="学生"
  335. show-overflow-tooltip
  336. ></el-table-column>
  337. <el-table-column
  338. label="班级"
  339. prop="className"
  340. align="center"
  341. min-width="70"
  342. show-overflow-tooltip
  343. ></el-table-column>
  344. <el-table-column
  345. prop="questionScore"
  346. align="center"
  347. label="得分"
  348. width="70"
  349. show-overflow-tooltip
  350. ></el-table-column>
  351. <el-table-column
  352. prop="paperScore"
  353. align="center"
  354. label="成绩"
  355. width="70"
  356. show-overflow-tooltip
  357. ></el-table-column>
  358. <el-table-column align="center" label="操作">
  359. <template #default="scope">
  360. <el-button type="text" @click.stop="handleClick(scope.row)"
  361. >查看答题卡</el-button
  362. >
  363. </template>
  364. </el-table-column>
  365. </el-table>
  366. </div>
  367. </template>
  368. <template #module_describe>
  369. 说明:点击左侧表格列可切换学生查看具体学生答题图片,点击学生姓名可在下方查看学生均衡分析,点击学生成绩可查看学生答题卡原卷。
  370. </template>
  371. </ReportModule>
  372. <ReportModule
  373. ref="reportModuleRef"
  374. :showTitle="true"
  375. :titleList="[state.groupTitle]"
  376. :showDescribe="true"
  377. tableOrChart="table"
  378. :showPrintBtn="false"
  379. :showExportBtn="true"
  380. :currentPage="state.majorTableData.currentPage"
  381. :pageSize="state.majorTableData.pageSize"
  382. :total="state.majorTableData.total"
  383. @ChangePageSize="ChangePageSize"
  384. @ChangeCurrentPage="ChangeCurrentPage"
  385. @ExportExcel="ExportExcel"
  386. >
  387. <template #module_table_chart>
  388. <el-table
  389. :data="state.majorTableData.tableData"
  390. ref="majorTable"
  391. border
  392. stripe
  393. align="left"
  394. :key="state.majorTableData.tableKey"
  395. >
  396. <template v-for="item in state.majorTableData.headerData">
  397. <el-table-column
  398. v-if="item.display"
  399. :key="item.prop"
  400. align="center"
  401. :prop="item.prop"
  402. :min-width="item.label.length > 4 ? 110 : 90"
  403. :label="item.label"
  404. fixed="left"
  405. show-overflow-tooltip
  406. >
  407. <template #default="scope">
  408. {{ scope.row[item.prop] || "-"
  409. }}{{
  410. item.prop == "estimatedScoreRate" && scope.row[item.prop]
  411. ? "%"
  412. : ""
  413. }}
  414. </template>
  415. </el-table-column>
  416. </template>
  417. <template v-for="parent in state.majorTableData.changeHeaderData">
  418. <el-table-column
  419. v-if="parent.display"
  420. align="center"
  421. :prop="parent.prop"
  422. :label="parent.label"
  423. :key="parent.prop"
  424. >
  425. <template v-for="item in state.majorTableData.childHeaderData">
  426. <el-table-column
  427. v-if="item.display"
  428. :prop="`${parent.prop}_${item.prop}`"
  429. :key="`${parent.prop}_${item.prop}`"
  430. align="center"
  431. :label="item.label"
  432. :min-width="item.label.length > 4 ? 100 : 80"
  433. show-overflow-tooltip
  434. >
  435. <template #default="scope">
  436. <span v-if="item.prop.indexOf('Rate') > -1">{{
  437. scope.row[`${parent.prop}_${item.prop}`]
  438. ? `${scope.row[`${parent.prop}_${item.prop}`]}%`
  439. : "-"
  440. }}</span>
  441. <span
  442. v-else
  443. @click="OpenStudentDialog(item, parent, scope.row)"
  444. :class="{
  445. table_row_blue:
  446. (item.prop.indexOf('Count') > -1 ||
  447. item.prop.indexOf('Number') > -1) &&
  448. scope.row?.[`${parent.prop}_${item.prop}`] > 0,
  449. }"
  450. >{{
  451. scope.row?.[`${parent.prop}_${item.prop}`] ?? "-"
  452. }}</span
  453. >
  454. </template>
  455. </el-table-column>
  456. </template>
  457. </el-table-column>
  458. </template>
  459. </el-table>
  460. </template>
  461. </ReportModule>
  462. <!-- 批量查看小题答题卡 -->
  463. <QuestionCard
  464. :subjectId="analysisStore.filterObject.subjectId"
  465. :questionId="state.cardQuestionId"
  466. :platformNumbers="state.cardRegistrationCodeList"
  467. :groupTitle="state.groupTitle"
  468. :groupName="state.groupName"
  469. :questionTitle="state.questionTitle"
  470. :classTitle="state.classTitle"
  471. :optionTitle="state.optionTitle"
  472. :showDialog="state.showQuestionCardDialog"
  473. @CloseDialog="CloseQuestionCardDialog"
  474. ></QuestionCard>
  475. <!-- 学生答题卡组件 -->
  476. <StudentPaper
  477. :modelValue="state.showStudentPaperDialog"
  478. :paperInfo="state.paperInfo"
  479. :pageTitle="state.paperTitle"
  480. @updateModelValue="UpdateModelValue"
  481. ></StudentPaper>
  482. </template>
  483. <script lang="ts" setup>
  484. import ReportModule from "@/components/ReportModule.vue";
  485. import EchartType from "@/components/EchartType.vue";
  486. import QuestionCard from "@/components/QuestionCard.vue";
  487. import StudentPaper from "@/components/StudentPaper.vue"; //学生答题卡组件
  488. import { useAnalysisStore } from "@/store/analysis";
  489. import {
  490. questionGroupAnalysis,
  491. queryAnswerListByAnswerAndScore,
  492. publicExport
  493. } from "@/api/analysis";
  494. import BarLineCharts from "@/components/echarts/barLineCharts.vue"; //柱状图折线图组合图组件
  495. import RadarCharts from "@/components/echarts/radarCharts.vue"; //雷达图
  496. import BarsCharts from "@/components/echarts/barsCharts.vue"; //多柱状图组件
  497. import BarScoringRateVertical from "@/components/echarts/barScoringRate_vertical.vue"; //得分率 纵向柱状图
  498. import DifferenceChart from "@/components/echarts/differenceChart.vue"; //率差图
  499. import BarChart from "@/components/echarts/barChart_answer.vue"; //单柱状图
  500. import StudentQuestionImg from "@/components/StudentQuestionImg.vue"; // 学生小题答题卡组件
  501. import { downloadExcel, GetExcelFileName } from "@/utils/exportExcel";
  502. import { onMounted, reactive, watch, ref, computed, nextTick } from "vue";
  503. import { cloneDeep } from "lodash-es";
  504. const analysisStore = useAnalysisStore();
  505. const reportModuleRef = ref<any>(null);
  506. const getExamName = computed(() => {
  507. return analysisStore.analysisExamInfo.examName || "";
  508. });
  509. const state = reactive({
  510. questionGroupDefault: [
  511. {
  512. name: "小题分析",
  513. code: "problem",
  514. },
  515. {
  516. name: "题型分析",
  517. code: 11,
  518. },
  519. {
  520. name: "错题分析",
  521. code: "errors",
  522. },
  523. {
  524. name: "客观题分析",
  525. code: "selectQuestion",
  526. },
  527. {
  528. name: "命题分析",
  529. code: "proposition",
  530. },
  531. ],
  532. questionGroupList: [], //试题分组标签 动态接口获取
  533. tagActive: "problem", //选择的试题分组标签
  534. groupTitle: "分组分析",
  535. groupPreviousTitle: "",
  536. knowledgeLayeredTitle: "", //知识点分层标题
  537. groupName: "", // 分组名称
  538. questionTitle: "", //题目名称
  539. classTitle: "", //班级名称
  540. optionTitle: "", //选项名称
  541. studentUserName: "", //学生名称
  542. studentRegistrationCode: "", //学生code
  543. questionTypesData: {
  544. chartKey: 0,
  545. refresh: false,
  546. data: [],
  547. tableData: [],
  548. },
  549. problemAnalysisData: {
  550. chartType: "line_bar_chart", //默认显示折线图柱状图line_bar_chart
  551. chartTypeList: [
  552. {
  553. label: "组合图",
  554. value: "line_bar_chart",
  555. },
  556. {
  557. label: "柱状图",
  558. value: "vertical_bar",
  559. },
  560. {
  561. label: "雷达图",
  562. value: "radar_chart",
  563. },
  564. ],
  565. legendList: [],
  566. defaultLegendList: [],
  567. showBarLegendIndex: 1,
  568. questionList: [], // 题目列表
  569. questionTableList: [], // 小题分析table
  570. groupList: [], // 分组题目列表
  571. groupTableList: [], // 分组table
  572. headerList: [],
  573. changeHeaderList: [],
  574. childHeaderList: [],
  575. questionListIndex: 0, //题目 索引
  576. data: [], //柱状图
  577. }, //小题分析数据
  578. majorQuestionData: {
  579. chartType: "line_bar_chart", //默认显示折线图柱状图line_bar_chart
  580. chartTypeList: [
  581. {
  582. label: "组合图",
  583. value: "line_bar_chart",
  584. },
  585. {
  586. label: "柱状图",
  587. value: "vertical_bar",
  588. },
  589. {
  590. label: "雷达图",
  591. value: "radar_chart",
  592. },
  593. ],
  594. legendList: [],
  595. defaultLegendList: [],
  596. showBarLegendIndex: 1,
  597. questionListIndex: 0, //题目 索引
  598. data: [], //柱状图
  599. }, //分组题目 对应的题目列表
  600. questionScoreStatsData: {
  601. checked: false,
  602. isIndeterminate: true,
  603. markLineData: [], //平均分
  604. chartTypeList: [
  605. {
  606. label: "柱状图",
  607. value: "vertical_bar",
  608. },
  609. {
  610. label: "率差图",
  611. value: "difference_chart",
  612. },
  613. ],
  614. chartType: "vertical_bar", //默认显示率差图vertical_bar
  615. datax: [], //x轴数据
  616. datay: [], //Y轴数据
  617. dataStackY: [], //Y轴数据
  618. tooltipData: [], //
  619. rate: 60, //中间x的刻度线的值 数字类型
  620. maxClass: "", // 得分率最高班
  621. minClass: "", // 得分率最低班
  622. }, //小题分析 /第N题
  623. questionAnswerData: {
  624. datax: [], //x轴数据
  625. datay: [], //Y轴数据
  626. color: "#5470C6",
  627. answerValue: "", //正确答案 柱状图显示绿色
  628. answerScore: [], //答案所能得的分数
  629. questionType: "", //类型
  630. fullMark: "", //满分
  631. // average:0,//平均分辅助线
  632. tableData: [],
  633. classListIndex: 0, // 索引
  634. }, //小题分析 /第N题 / N班
  635. majorAnswerData: {
  636. paperUrl: "", //试卷地址
  637. tableData: [], //表格数据
  638. rowIndex: 0,
  639. }, // 通过答案或者分数查询某题作答情况
  640. majorStudentData: {
  641. tableData: [],
  642. legendList: [],
  643. radarChartData: [],
  644. excellentCourseList: "", //优秀科目
  645. inferiorCourseList: "", //弱势科目
  646. }, //科目得分率
  647. majorTableData: {
  648. tableKey: 0,
  649. tableData: [], //小题分析表数据
  650. allTableData: [],
  651. headerData: [], //固定表头数据
  652. changeHeaderData: [], //动态表头数据
  653. childHeaderData: [], //子级表头数据
  654. pageSize: 10, //每页显示数据
  655. total: 0, //总数
  656. currentPage: 1, //当前页
  657. }, //小题分析表 + 题目分组分析
  658. errorQuestionData: {
  659. tableKey: 0,
  660. allData: [], //
  661. gradeValue: "", //选中的年级
  662. className: "", //选中的年级名称
  663. type: "",
  664. gradeData: [], //年级下拉选项
  665. tableData: [], //错题分析表数据
  666. }, //错题分析表
  667. studentPreviousExamData: {
  668. data: null,
  669. chartType: "line_chart",
  670. selectList: [
  671. {
  672. value: "standardScore",
  673. name: "标准分",
  674. },
  675. {
  676. value: "scoreRate",
  677. name: "得分率",
  678. },
  679. {
  680. value: "classRank",
  681. name: "班排",
  682. },
  683. {
  684. value: "schoolRank",
  685. name: "校排",
  686. },
  687. {
  688. value: "examRank",
  689. name: "联排",
  690. },
  691. ],
  692. selectVal: "standardScore",
  693. selectName: "标准分",
  694. lineChartData: {
  695. datax: [],
  696. datay: [],
  697. title: [],
  698. tooltipData: [],
  699. },
  700. barChartData: {
  701. //柱状图
  702. legendList: [],
  703. data: [],
  704. },
  705. }, //学生成绩历次考试分析数据
  706. groupPrevious: {
  707. loading: false,
  708. examNameList: [],
  709. examChartList: [],
  710. selectedLegendList: [],
  711. examChartIndex: [],
  712. headerList: [],
  713. dataList: [],
  714. }, //题目分组历次分析(图表数据)
  715. stuGroupPreviousChart: {
  716. examNameList: [],
  717. examChartList: [],
  718. examChartIndex: [],
  719. }, //学生组块历次图
  720. stuGroupPreviousChartStuInfo: {
  721. studentName: "", //学生姓名搜索条件
  722. studentCodeList: [],
  723. }, //学生历次分析图 搜索学生信息
  724. stuGroupPrevious: {
  725. total: 0,
  726. pageNum: 1,
  727. pageSize: 10,
  728. headerList: [],
  729. tableList: [],
  730. }, //题目分组历次分析学生数据表(分页)
  731. classNameList: [],
  732. showHeaderSet: false, //是否显示设置表头
  733. showBenchTaskSelect: false, //历次考试弹框
  734. exportLoading: false, // 题型导出loading
  735. exportErrorLoading: false, // 错题导出loading
  736. chartKey: 0,
  737. dataLoading: false,
  738. loadingText: "加载中,请稍后……",
  739. errorPdfLoading: false, //错题导出PDF loading
  740. paperInfo: {
  741. examPaperId: "", //考试科目id
  742. platformNumber: "", //学籍号平台号
  743. questionId: "", //题目id
  744. }, //学生试卷信息
  745. paperTitle: "", //学生试卷标题
  746. showStudentPaperDialog: false, //是否显示学生答题卡弹窗
  747. paperInfos: {
  748. examPaperId: "", //考试科目id
  749. platformNumber: "", //学籍号平台号
  750. questionId: "", //题目id
  751. },
  752. knowledgeInputDialog: false,
  753. dialogData: {
  754. apiName: "",
  755. showDialog: false,
  756. title: "",
  757. tableTitle: "",
  758. fiveRateName: "", //当前选择的五率的名称
  759. selectSubjectName: "", //当前选择的科目名称
  760. selectSchoolLevel: "", //当前选择的学校级别:0-联考 1-学校分组 2-具体学校
  761. selectSchoolName: "", //当前选择的学校名称:联校 组合学校 单校名称
  762. selectClassLevel: "", //当前选择的班级级别:0-年级 1-组合班级 2-具体班级
  763. selectClassName: "", //当前选择的班级名称:年级 组合班级 单班
  764. questionId: "", //试题ID
  765. isPaper: "", //是否为全卷0-非1-是全卷
  766. quesScoreType: "", //选择的类型0-满分人数 1-0分人数 3-优秀率、良好率等
  767. knowledgeId: "", //如果选择的是知识点,则需要返回知识点ID
  768. questionGroupName: "", //试题分组的名称
  769. groupTitle: "",
  770. },
  771. showQuestionCardDialog: false,
  772. cardQuestionId: "", // 批量查看答题卡试题id
  773. cardRegistrationCodeList: [], // 批量查看答题卡学生账号数组
  774. });
  775. const majorQuestionCharts = ref(null);
  776. const majorTable = ref(null);
  777. //排序
  778. const ChangeChartOrder = (sortType, legendData, barIndex, number) => {
  779. if (number == 1) {
  780. const fullVolume = state.problemAnalysisData.groupList.filter(
  781. (item) => item.groupCode == 999,
  782. ); //全卷
  783. const groupList = state.problemAnalysisData.groupList.filter(
  784. (item) => item.groupCode != 999,
  785. );
  786. //题目分组排序
  787. if (sortType == "2") {
  788. //从低到高
  789. groupList.sort(function (a, b) {
  790. return (
  791. (a?.classList?.[barIndex]?.questionStats?.scoreRate || 0) -
  792. (b?.classList?.[barIndex]?.questionStats?.scoreRate || 0)
  793. );
  794. });
  795. } else if (sortType == "3") {
  796. //从高到低
  797. groupList.sort(function (a, b) {
  798. return (
  799. (b?.classList?.[barIndex]?.questionStats?.scoreRate || 0) -
  800. (a?.classList?.[barIndex]?.questionStats?.scoreRate || 0)
  801. );
  802. });
  803. } else {
  804. //默认排序 按题号排序
  805. groupList.sort(function (a, b) {
  806. return (a?.groupCode || 0) - (b.groupCode || 0);
  807. });
  808. }
  809. state.problemAnalysisData.groupList = [...groupList, ...fullVolume];
  810. state.problemAnalysisData.data = [];
  811. state.problemAnalysisData.groupList.forEach((group) => {
  812. const scoreRateArr =
  813. group.classList?.map((item) => item?.questionStats?.scoreRate ?? 0) ||
  814. [];
  815. state.problemAnalysisData.data.push([group.groupName, ...scoreRateArr]);
  816. });
  817. let chartTitle = ["group"];
  818. state.problemAnalysisData.changeHeaderList.forEach((item) => {
  819. chartTitle.push(item.label);
  820. });
  821. state.problemAnalysisData.data.unshift(chartTitle); // 柱状图 图例标题
  822. state.problemAnalysisData.data.pop(); // 删除最后一行 全卷
  823. state.problemAnalysisData.legendList = legendData;
  824. //大题分析 /阅读表达
  825. GetClassQuestionData(0, state.problemAnalysisData.groupList[0].groupName);
  826. } else {
  827. const fullVolume = state.problemAnalysisData.questionList.filter(
  828. (item) => item.showCode == 999,
  829. ); //全卷
  830. const questionList = state.problemAnalysisData.questionList.filter(
  831. (item) => item.showCode != 999,
  832. );
  833. if (sortType == "2") {
  834. //从低到高
  835. questionList.sort(function (a, b) {
  836. return (
  837. (a?.classList?.[barIndex]?.questionStats?.scoreRate || 0) -
  838. (b?.classList?.[barIndex]?.questionStats?.scoreRate || 0)
  839. );
  840. });
  841. } else if (sortType == "3") {
  842. //从高到低
  843. questionList.sort(function (a, b) {
  844. return (
  845. (b?.classList?.[barIndex]?.questionStats?.scoreRate || 0) -
  846. (a?.classList?.[barIndex]?.questionStats?.scoreRate || 0)
  847. );
  848. });
  849. } else {
  850. //默认排序 按题号排序
  851. questionList.sort(function (a, b) {
  852. return (a?.showCode || 0) - (b.showCode || 0);
  853. });
  854. }
  855. state.problemAnalysisData.questionList = [...questionList, ...fullVolume];
  856. //Y轴数据
  857. state.majorQuestionData.data = [];
  858. state.problemAnalysisData.questionList.forEach((question) => {
  859. const scoreRateArr = question.classList.map(
  860. (item) => item?.questionStats?.scoreRate || 0,
  861. );
  862. state.majorQuestionData.data.push([
  863. question.questionName,
  864. ...scoreRateArr,
  865. ]);
  866. });
  867. let chartTitle = [];
  868. state.problemAnalysisData.changeHeaderList.forEach((item) => {
  869. chartTitle.push(item.label);
  870. });
  871. chartTitle.unshift("group");
  872. state.majorQuestionData.legendList = legendData;
  873. state.majorQuestionData.data.unshift(chartTitle); // 柱状图 图例标题
  874. //大题分析 /阅读表达 / 第32题
  875. GetQuestionStatsData(0);
  876. }
  877. };
  878. //试题图表切换公共方法
  879. const ChangeEchartType = (value, prop) => {
  880. if (prop == "problemAnalysisData") {
  881. ChangeChartOrder("1", state.problemAnalysisData.defaultLegendList, "", 1);
  882. } else if (prop == "majorQuestionData") {
  883. ChangeChartOrder("1", state.majorQuestionData.defaultLegendList, "", 2);
  884. }
  885. state[prop].chartType = value;
  886. };
  887. //题目分组分析
  888. const GetQuestionGroupAnalysisData = () => {
  889. state.problemAnalysisData.data = []; //柱状图折线图
  890. state.dataLoading = true;
  891. questionGroupAnalysis({
  892. ...analysisStore.filterObject,
  893. analysisType: 4,
  894. })
  895. .then((res) => {
  896. // console.log("打印题目分组分析接口返回数据",res)
  897. state.problemAnalysisData.data = []; //柱状图折线图
  898. if (res.code == 200 && res.data && res.data.groupList.length) {
  899. const { data } = res;
  900. const groupList = data.groupList || [];
  901. const changeHeaderList = data.changeHeaderList || []; //柱状图折线图 班级名称
  902. state.problemAnalysisData.groupList = groupList;
  903. state.problemAnalysisData.groupTableList = cloneDeep(groupList);
  904. state.problemAnalysisData.headerList = data.headerList || [];
  905. state.problemAnalysisData.changeHeaderList = changeHeaderList;
  906. state.problemAnalysisData.childHeaderList = data.childHeaderList || [];
  907. state.chartKey++;
  908. //Y轴数据
  909. state.problemAnalysisData.data = groupList.map((item) => {
  910. return [item.groupName];
  911. });
  912. groupList.forEach((group, key) => {
  913. const scoreRateArr =
  914. group.classList?.map(
  915. (item) => item?.questionStats?.scoreRate ?? 0,
  916. ) || [];
  917. state.problemAnalysisData.data[key].push(...scoreRateArr);
  918. });
  919. let chartTitle = [],
  920. titleType = [],
  921. classSelectLegend = [];
  922. changeHeaderList.forEach((item) => {
  923. chartTitle.push(item.label);
  924. titleType.push(item.type ? item.type : ""); //1柱状图 2折线
  925. if (item.prop != "0" && item.prop.indexOf("school_group") == -1) {
  926. classSelectLegend.push(item.label);
  927. }
  928. });
  929. chartTitle.unshift("group");
  930. if (analysisStore.filterObject.classLevel != 2) {
  931. //0 年级 1 组合班级 2具体班级
  932. let startIndex = 1,
  933. endIndex = 3;
  934. if (analysisStore.filterObject.schoolLevel == 2) {
  935. //0-联考 1-学校分组 2-具体学校
  936. startIndex =
  937. titleType.lastIndexOf(1) > -1 ? titleType.lastIndexOf(1) + 1 : 1;
  938. endIndex = titleType.indexOf(2) > -1 ? titleType.indexOf(2) + 2 : 3; //单校时显示联校、组合校、年级是柱子;班级组合、班级是折线。
  939. } else {
  940. //联校 组合学校 默认当前的为第一个选中的柱子
  941. const barFirtIndex =
  942. titleType.lastIndexOf(1) > -1 ? titleType.lastIndexOf(1) + 1 : 1;
  943. startIndex = barFirtIndex;
  944. endIndex = barFirtIndex + 2;
  945. }
  946. state.problemAnalysisData.legendList =
  947. chartTitle.length > 1 ? chartTitle.slice(startIndex, endIndex) : [];
  948. state.problemAnalysisData.showBarLegendIndex = endIndex - 2;
  949. } else {
  950. //单班 联校和组合学校不默认显示
  951. state.problemAnalysisData.legendList = classSelectLegend;
  952. state.problemAnalysisData.showBarLegendIndex =
  953. changeHeaderList.length - 1;
  954. }
  955. state.problemAnalysisData.defaultLegendList = cloneDeep(
  956. state.problemAnalysisData.legendList,
  957. );
  958. state.problemAnalysisData.data.unshift(chartTitle); // 柱状图 图例标题
  959. state.problemAnalysisData.data.pop(); // 删除最后一行 全卷
  960. //大题分析 /阅读表达
  961. GetClassQuestionData(0, groupList[0].groupName);
  962. // 大题分析表
  963. GetMajorTableData();
  964. } else {
  965. state.questionTypesData.data = [];
  966. state.questionTypesData.tableData = [];
  967. state.problemAnalysisData.showBarLegendIndex = 1;
  968. state.problemAnalysisData.groupList = [];
  969. state.problemAnalysisData.questionList = [];
  970. state.problemAnalysisData.headerList = [];
  971. state.problemAnalysisData.changeHeaderList = [];
  972. state.problemAnalysisData.childHeaderList = [];
  973. state.majorQuestionData.data = [];
  974. state.majorQuestionData.legendList = [];
  975. state.questionScoreStatsData.datax = [];
  976. state.questionScoreStatsData.datay = [];
  977. state.questionScoreStatsData.dataStackY = [];
  978. state.questionScoreStatsData.tooltipData = [];
  979. state.questionScoreStatsData.rate = 0;
  980. state.questionScoreStatsData.maxClass = "";
  981. state.questionScoreStatsData.minClass = "";
  982. state.questionAnswerData.datax = [];
  983. state.questionAnswerData.datay = [];
  984. state.questionAnswerData.answerValue = "";
  985. state.questionAnswerData.answerScore = [];
  986. state.questionAnswerData.questionType = "";
  987. state.questionAnswerData.fullMark = "";
  988. state.questionAnswerData.tableData = [];
  989. state.questionAnswerData.classListIndex = 0;
  990. state.majorAnswerData.tableData = [];
  991. state.majorAnswerData.paperUrl = "";
  992. state.majorAnswerData.rowIndex = 0;
  993. state.majorStudentData.tableData = [];
  994. state.majorStudentData.legendList = [];
  995. state.majorStudentData.radarChartData = [];
  996. state.majorStudentData.excellentCourseList = "";
  997. state.majorStudentData.inferiorCourseList = "";
  998. state.majorTableData.tableData = [];
  999. state.majorTableData.headerData = [];
  1000. state.majorTableData.changeHeaderData = [];
  1001. state.majorTableData.childHeaderData = [];
  1002. state.majorTableData.pageSize = 10;
  1003. state.majorTableData.total = 0;
  1004. state.majorTableData.currentPage = 1;
  1005. }
  1006. })
  1007. .finally(() => {
  1008. state.dataLoading = false;
  1009. });
  1010. };
  1011. // 点击柱状图折线图
  1012. const HandleChartClick = (index, name) => {
  1013. if (majorQuestionCharts.value) {
  1014. majorQuestionCharts.value.ChangeSelectValue();
  1015. }
  1016. GetClassQuestionData(index, name); //题目分组分析
  1017. };
  1018. //大题分析 /阅读表达 点击柱状图
  1019. const HandleQuestionChartClick = (index, name) => {
  1020. GetQuestionStatsData(index); //小题分析
  1021. };
  1022. // 获取小题分析 /第N题
  1023. const GetQuestionStatsData = (index) => {
  1024. const questionData = cloneDeep(state.problemAnalysisData.questionList[index]);
  1025. state.problemAnalysisData.questionListIndex = index;
  1026. state.questionTitle = questionData?.questionName || "";
  1027. if (questionData) {
  1028. state.questionScoreStatsData.datax = []; // x轴数据
  1029. state.questionScoreStatsData.datay = []; // Y轴数据
  1030. state.questionScoreStatsData.dataStackY = []; // Y轴数据
  1031. state.questionScoreStatsData.tooltipData = []; // 提示框内容
  1032. let dataStackY = [];
  1033. const isHasEstimatedScore = state.problemAnalysisData.headerList.find(
  1034. (item) => item.prop == "estimatedScore",
  1035. ); //是否存在预估满分
  1036. if (questionData.classList.length > 1) {
  1037. const key = state.problemAnalysisData.showBarLegendIndex;
  1038. state.questionScoreStatsData.markLineData = []; //辅助线
  1039. const classList = questionData?.classList || [];
  1040. if (
  1041. analysisStore.filterObject.classLevel == 0 ||
  1042. analysisStore.filterObject.classLevel == 1
  1043. ) {
  1044. //0 年级 1 组合班级 2具体班级
  1045. state.questionScoreStatsData.rate = Number(
  1046. classList?.[key - 1]?.questionStats?.scoreRate || 0,
  1047. );
  1048. } else {
  1049. state.questionScoreStatsData.rate = Number(
  1050. classList?.[key]?.questionStats?.scoreRate || 0,
  1051. );
  1052. }
  1053. //辅助线
  1054. classList.forEach((item, index) => {
  1055. const keyIndex =
  1056. analysisStore.filterObject.classLevel == 0 ||
  1057. analysisStore.filterObject.classLevel == 1
  1058. ? key - (isHasEstimatedScore ? 2 : 1)
  1059. : key;
  1060. if (index <= keyIndex) {
  1061. const rate = Number(item?.questionStats?.scoreRate || 0);
  1062. state.questionScoreStatsData.markLineData.push({
  1063. legendName: item.groupName,
  1064. value: rate,
  1065. isShow: index == keyIndex ? true : false, //是否显示
  1066. });
  1067. }
  1068. });
  1069. questionData.classList = questionData.classList.slice(
  1070. isHasEstimatedScore ? key - 1 : key,
  1071. ); // 取折线图数据
  1072. } else {
  1073. state.questionScoreStatsData.rate = 60;
  1074. }
  1075. questionData.classList.forEach((item) => {
  1076. state.questionScoreStatsData.datax.push(item.groupName);
  1077. state.questionScoreStatsData.datay.push(item.questionStats.scoreRate);
  1078. dataStackY.push(item.questionStats.lossRate);
  1079. state.questionScoreStatsData.tooltipData.push({
  1080. list: [
  1081. {
  1082. name: "失分率",
  1083. value: `${item.questionStats.lossRate}%`,
  1084. },
  1085. ],
  1086. });
  1087. });
  1088. // 获取最大值
  1089. const max = Math.max(...state.questionScoreStatsData.datay);
  1090. // 获取最小值
  1091. const min = Math.min(...state.questionScoreStatsData.datay);
  1092. const maxValue = max.toFixed(2);
  1093. const minValue = min.toFixed(2);
  1094. let maxClass = [],
  1095. minClass = [];
  1096. state.questionScoreStatsData.datay.forEach((item, index) => {
  1097. if (Number(item) == Number(maxValue)) {
  1098. maxClass.push(state.questionScoreStatsData.datax[index]);
  1099. }
  1100. if (Number(item) == Number(minValue)) {
  1101. minClass.push(state.questionScoreStatsData.datax[index]);
  1102. }
  1103. });
  1104. state.questionScoreStatsData.minClass = minClass.join("、");
  1105. state.questionScoreStatsData.maxClass = maxClass.join("、");
  1106. state.questionScoreStatsData.dataStackY = [
  1107. state.questionScoreStatsData.datay,
  1108. dataStackY,
  1109. ];
  1110. }
  1111. // 获取小题分析 /第N题 / 第N班
  1112. GetQuestionAnswerData(state.problemAnalysisData.showBarLegendIndex);
  1113. };
  1114. //大题分析 /阅读表达
  1115. const GetClassQuestionData = (index, name) => {
  1116. state.majorQuestionData.data = [];
  1117. state.groupName = name;
  1118. const groupList = state.problemAnalysisData.groupList[index];
  1119. const questionList = groupList.questionList;
  1120. state.problemAnalysisData.questionList = questionList;
  1121. //Y轴数据
  1122. state.majorQuestionData.data = questionList.map((item) => {
  1123. return [item.questionName];
  1124. });
  1125. questionList.forEach((question, key) => {
  1126. const scoreRateArr = question.classList.map(
  1127. (item) => item?.questionStats?.scoreRate || 0,
  1128. );
  1129. state.majorQuestionData.data[key].push(...scoreRateArr);
  1130. });
  1131. let chartTitle = [],
  1132. titleType = [],
  1133. classSelectLegend = [];
  1134. state.problemAnalysisData.changeHeaderList.forEach((item) => {
  1135. chartTitle.push(item.label);
  1136. titleType.push(item.type ? item.type : ""); //1柱状图 2折线
  1137. if (item.prop != "0" && item.prop.indexOf("school_group") == -1) {
  1138. classSelectLegend.push(item.label);
  1139. }
  1140. });
  1141. chartTitle.unshift("group");
  1142. if (analysisStore.filterObject.classLevel != 2) {
  1143. //0 年级 1 组合班级 2具体班级
  1144. let startIndex = 1,
  1145. endIndex = 3;
  1146. if (analysisStore.filterObject.schoolLevel == 2) {
  1147. //0-联考 1-学校分组 2-具体学校
  1148. startIndex =
  1149. titleType.lastIndexOf(1) > -1 ? titleType.lastIndexOf(1) + 1 : 1;
  1150. endIndex = titleType.indexOf(2) > -1 ? titleType.indexOf(2) + 2 : 3; //单校时显示联校、组合校、年级是柱子;班级组合、班级是折线。
  1151. } else {
  1152. //联校 组合学校 默认当前的为第一个选中的柱子
  1153. const barFirtIndex =
  1154. titleType.lastIndexOf(1) > -1 ? titleType.lastIndexOf(1) + 1 : 1;
  1155. startIndex = barFirtIndex;
  1156. endIndex = barFirtIndex + 2;
  1157. }
  1158. state.majorQuestionData.legendList =
  1159. chartTitle.length > 1 ? chartTitle.slice(startIndex, endIndex) : [];
  1160. state.majorQuestionData.showBarLegendIndex = endIndex - 2;
  1161. } else {
  1162. //联校和组合学校不默认显示
  1163. state.majorQuestionData.legendList = classSelectLegend;
  1164. }
  1165. state.majorQuestionData.defaultLegendList = cloneDeep(
  1166. state.majorQuestionData.legendList,
  1167. );
  1168. state.majorQuestionData.data.unshift(chartTitle); // 柱状图 图例标题
  1169. //大题分析 /阅读表达 / 第32题
  1170. GetQuestionStatsData(0);
  1171. };
  1172. //切换 获取小题分析 /第N题 获取第N班答题列表
  1173. const HandleQuestionScoreChartClick = (index) => {
  1174. GetQuestionAnswerData(index + state.problemAnalysisData.showBarLegendIndex);
  1175. };
  1176. // 获取小题分析 /第N题 / 第N班 选项
  1177. const GetQuestionAnswerData = (index) => {
  1178. const isHasEstimatedScore = state.problemAnalysisData.headerList.find(
  1179. (item) => item.prop == "estimatedScore",
  1180. ); //是否存在预估满分
  1181. const newINdex = isHasEstimatedScore ? index - 1 : index;
  1182. const classList =
  1183. state.problemAnalysisData.questionList?.[
  1184. state.problemAnalysisData.questionListIndex
  1185. ]?.classList?.[newINdex];
  1186. // console.log("打印获取小题分析班级列表",classList)
  1187. state.questionAnswerData.answerValue =
  1188. state.problemAnalysisData.questionList[
  1189. state.problemAnalysisData.questionListIndex
  1190. ].answerValue; //正确答案
  1191. state.questionAnswerData.questionType =
  1192. state.problemAnalysisData.questionList[
  1193. state.problemAnalysisData.questionListIndex
  1194. ].questionType; //类型
  1195. state.questionAnswerData.fullMark =
  1196. state.problemAnalysisData.questionList[
  1197. state.problemAnalysisData.questionListIndex
  1198. ].fullMark; //满分
  1199. state.questionAnswerData.classListIndex = newINdex;
  1200. if (classList) {
  1201. const answerList = classList.questionStats.answerList || [];
  1202. state.classTitle = classList.groupName;
  1203. state.questionAnswerData.datax = []; // x轴数据
  1204. state.questionAnswerData.datay = []; // Y轴数据
  1205. let sum = 0;
  1206. answerList.forEach((item) => {
  1207. sum += item.studentNum;
  1208. state.questionAnswerData.datax.push(item.name);
  1209. state.questionAnswerData.datay.push(item.studentNum);
  1210. state.questionAnswerData.answerScore.push(item.score);
  1211. });
  1212. state.questionAnswerData.tableData = [
  1213. {
  1214. title: "得分率",
  1215. value: `${classList.questionStats.scoreRate}%`,
  1216. },
  1217. {
  1218. title: "平均分",
  1219. value: `${classList.questionStats.averageScore}`,
  1220. },
  1221. {
  1222. title: "最高分",
  1223. value: `${classList.questionStats.maxScore}`,
  1224. },
  1225. {
  1226. title: "最低分",
  1227. value: `${classList.questionStats.minScore}`,
  1228. },
  1229. {
  1230. title: "人数",
  1231. value: `${sum}人`,
  1232. },
  1233. ];
  1234. if (analysisStore.filterObject.schoolLevel == 2) {
  1235. //单校时展示
  1236. // 获取答题情况
  1237. GetAnswerListByAnswerAndScore(
  1238. 0,
  1239. answerList && answerList[0] ? answerList[0].name : "",
  1240. );
  1241. }
  1242. }
  1243. };
  1244. // 点击柱状图小题分析 /第N题 / 第N班 选项 获取答题情况
  1245. const HandleQuestionAnswerChartClick = (index, name) => {
  1246. if (analysisStore.filterObject.schoolLevel == 2) {
  1247. //单校时展示
  1248. GetAnswerListByAnswerAndScore(index, name);
  1249. }
  1250. };
  1251. //通过答案或者分数查询某题作答情况
  1252. const GetAnswerListByAnswerAndScore = (index, name) => {
  1253. state.optionTitle = name; //选项名称
  1254. const question =
  1255. state.problemAnalysisData.questionList[
  1256. state.problemAnalysisData.questionListIndex
  1257. ];
  1258. const classItem = question.classList[state.questionAnswerData.classListIndex];
  1259. const classItemKeys = Object.keys(classItem);
  1260. const reportParam = {
  1261. ...analysisStore.filterObject,
  1262. };
  1263. Object.keys(analysisStore.filterObject).forEach((item) => {
  1264. if (classItemKeys.indexOf(item) > -1) {
  1265. reportParam[item] = classItem[item];
  1266. }
  1267. });
  1268. const answer =
  1269. question.classList[state.questionAnswerData.classListIndex].questionStats
  1270. .answerList[index];
  1271. const params = {
  1272. ...reportParam,
  1273. questionId: question.questionId, // 试题id
  1274. registrationCodeList: answer?.registrationCodeList || [], // 学生账号数组
  1275. };
  1276. state.cardQuestionId = params.questionId; // 批量查看答题卡试题id
  1277. state.cardRegistrationCodeList = params.registrationCodeList; // 批量查看答题卡学生账号数组
  1278. queryAnswerListByAnswerAndScore(params).then((res) => {
  1279. if (res.code == 200) {
  1280. state.majorAnswerData.tableData = res.data || [];
  1281. HandleRowClick(
  1282. state.majorAnswerData.tableData.length > 0
  1283. ? state.majorAnswerData.tableData[0]
  1284. : {},
  1285. );
  1286. } else {
  1287. state.majorAnswerData.tableData = [];
  1288. }
  1289. });
  1290. };
  1291. // 点击某行学生某题作答情况
  1292. const HandleRowClick = (row) => {
  1293. if (row?.studentRegistrationCode) {
  1294. //答题卡
  1295. const question =
  1296. state.problemAnalysisData.questionList[
  1297. state.problemAnalysisData.questionListIndex
  1298. ];
  1299. state.paperInfos = {
  1300. examPaperId: analysisStore.filterObject.subjectId, //考试科目id
  1301. platformNumber: row.studentRegistrationCode, //学籍号平台号
  1302. questionId: question.questionId, //题目id
  1303. };
  1304. state.majorAnswerData.rowIndex = state.majorAnswerData.tableData.findIndex(
  1305. (item) => item.studentRegistrationCode == row?.studentRegistrationCode,
  1306. );
  1307. } else {
  1308. state.paperInfos = {};
  1309. }
  1310. };
  1311. // 获取项目分析表
  1312. const GetMajorTableData = () => {
  1313. state.majorTableData.tableKey += 1;
  1314. state.majorTableData.headerData = state.problemAnalysisData.headerList;
  1315. state.majorTableData.changeHeaderData =
  1316. state.problemAnalysisData.changeHeaderList;
  1317. state.majorTableData.childHeaderData =
  1318. state.problemAnalysisData.childHeaderList;
  1319. const headerPropData = state.problemAnalysisData.headerList.map(
  1320. (item) => item.prop,
  1321. ); //表头字段名
  1322. const childHeaderPropData = state.problemAnalysisData.childHeaderList.map(
  1323. (item) => item.prop,
  1324. ); //动态表头字段名
  1325. let allTableData = [];
  1326. const allList = state.problemAnalysisData.groupTableList;
  1327. allList.forEach((item) => {
  1328. let itemObj = {
  1329. questionId: item?.questionId || "",
  1330. knowledgeId: item?.knowledgeId || "",
  1331. };
  1332. const classList = item.classList;
  1333. headerPropData.forEach((title) => {
  1334. itemObj[title] = Array.isArray(item[title])
  1335. ? item[title].join("、")
  1336. : item[title];
  1337. });
  1338. classList.forEach((el) => {
  1339. if (
  1340. el.questionStats?.headDataBOList &&
  1341. el.questionStats.headDataBOList.length > 0
  1342. ) {
  1343. el.questionStats.headDataBOList.forEach((bo) => {
  1344. el.questionStats[`${bo.name}Rate`] = bo.rate;
  1345. el.questionStats[`${bo.name}StudentNumber`] = bo.studentNumber;
  1346. });
  1347. } else {
  1348. el.questionStats = [];
  1349. }
  1350. childHeaderPropData.forEach((field) => {
  1351. itemObj[`${el.groupId}_${field}`] = el.questionStats[field];
  1352. });
  1353. });
  1354. allTableData.push(itemObj);
  1355. });
  1356. state.majorTableData.total = allTableData.length; //总条数
  1357. state.majorTableData.allTableData = allTableData;
  1358. GetPageMajorTableData();
  1359. //重置表格滚动条位置
  1360. ResetTableScroll(); //重置表格滚动条位置
  1361. };
  1362. const GetPageMajorTableData = () => {
  1363. const start =
  1364. (state.majorTableData.currentPage - 1) * state.majorTableData.pageSize;
  1365. const end = start + state.majorTableData.pageSize;
  1366. state.majorTableData.tableData = state.majorTableData.allTableData.slice(
  1367. start,
  1368. end,
  1369. );
  1370. };
  1371. //重置表格滚动条位置
  1372. const ResetTableScroll = () => {
  1373. nextTick(() => {
  1374. if (majorTable.value) {
  1375. const tableBody = majorTable.value.$el.querySelector(
  1376. ".el-table__body-wrapper",
  1377. );
  1378. if (tableBody) {
  1379. tableBody.scrollTop = 0; //清除纵向滚动条位置
  1380. tableBody.scrollLeft = 0; // 清除横向滚动条位置
  1381. }
  1382. }
  1383. });
  1384. };
  1385. const ChangeCurrentPage = (val) => {
  1386. state.majorTableData.currentPage = val;
  1387. GetMajorTableData(); //加载分析表格数据
  1388. };
  1389. const ChangePageSize = (val: number) => {
  1390. state.majorTableData.pageSize = val;
  1391. state.majorTableData.currentPage = 1;
  1392. GetMajorTableData(); //加载分析表格数据
  1393. };
  1394. // 导出Excel
  1395. const ExportExcel = () => {
  1396. // 1. 设置加载状态
  1397. reportModuleRef.value?.SetExportLoading?.(true);
  1398. // 2. 参数
  1399. const examName = getExamName.value;
  1400. let params = {
  1401. fileName: `${GetExcelFileName(examName, analysisStore.filterObject, state.groupTitle)}`,
  1402. examName: examName, //考试名称
  1403. sheetName: state.groupTitle, //sheet页名称
  1404. };
  1405. const staticHeaderData = state.problemAnalysisData.headerList.filter(
  1406. (item) => item.display,
  1407. );
  1408. const childHeaderData = state.problemAnalysisData.childHeaderList.filter(
  1409. (item) => item.display,
  1410. );
  1411. const dynamicsHeaderData = state.problemAnalysisData.changeHeaderList.filter(
  1412. (item) => item.display,
  1413. );
  1414. params.staticHeaderData = staticHeaderData;
  1415. params.childHeaderData = childHeaderData;
  1416. params.dynamicsHeaderData = dynamicsHeaderData;
  1417. let dataList = [];
  1418. state.majorTableData.allTableData.forEach((item) => {
  1419. const rowData = [];
  1420. staticHeaderData.forEach((header) => {
  1421. rowData.push(item?.[header.prop] || "-");
  1422. });
  1423. dynamicsHeaderData.forEach((parent) => {
  1424. childHeaderData.forEach((child) => {
  1425. const itemValue = item[`${parent.prop}_${child.prop}`];
  1426. if (child.prop.indexOf("Rate") > -1) {
  1427. const rate = itemValue ? `${itemValue}%` : "-";
  1428. rowData.push(rate);
  1429. } else {
  1430. rowData.push(itemValue ?? "-");
  1431. }
  1432. });
  1433. });
  1434. dataList.push(rowData);
  1435. });
  1436. params.dataList = dataList;
  1437. // 3. 调用通用下载方法,并在完成后重置加载状态
  1438. downloadExcel(publicExport, params).finally(() => {
  1439. reportModuleRef.value?.SetExportLoading?.(false);
  1440. });
  1441. };
  1442. //设置表头样式
  1443. const HeaderRowStyle = ({ row, rowIndex }) => {
  1444. if (rowIndex === 1) {
  1445. return {
  1446. display: "none",
  1447. };
  1448. }
  1449. };
  1450. const TableRowClassName = ({ row, rowIndex }) => {
  1451. if (rowIndex === state.majorAnswerData.rowIndex) {
  1452. return "current-row";
  1453. }
  1454. return "";
  1455. };
  1456. //查看答题卡
  1457. const handleClick = (row) => {
  1458. state.paperInfo = {
  1459. examPaperId: analysisStore.filterObject.subjectId, //考试科目id
  1460. platformNumber: row.studentRegistrationCode, //学籍号平台号
  1461. questionId: "", //题目id
  1462. };
  1463. state.paperTitle = `${getExamName.value}-${analysisStore.filterObject.subjectName}-${row.className}-${row.studentUserName}`; //学生姓名
  1464. state.showStudentPaperDialog = true;
  1465. };
  1466. //更新弹窗状态
  1467. const UpdateModelValue = (val: boolean) => {
  1468. state.showStudentPaperDialog = val;
  1469. };
  1470. //批量查看小题答题卡
  1471. const VisibleQuestionCard = () => {
  1472. state.showQuestionCardDialog = true;
  1473. };
  1474. //关闭弹框
  1475. const CloseQuestionCardDialog = () => {
  1476. state.showQuestionCardDialog = false;
  1477. };
  1478. const pageInit = () => {
  1479. const chartTypeLists = [
  1480. {
  1481. label: "组合图",
  1482. value: "line_bar_chart",
  1483. },
  1484. {
  1485. label: "柱状图",
  1486. value: "vertical_bar",
  1487. },
  1488. {
  1489. label: "雷达图",
  1490. value: "radar_chart",
  1491. },
  1492. ];
  1493. const chartTypeList = [
  1494. {
  1495. label: "柱状图",
  1496. value: "vertical_bar",
  1497. },
  1498. {
  1499. label: "雷达图",
  1500. value: "radar_chart",
  1501. },
  1502. ];
  1503. state.problemAnalysisData.chartTypeList =
  1504. analysisStore.filterObject.classLevel != 2 ? chartTypeLists : chartTypeList;
  1505. state.problemAnalysisData.chartType =
  1506. analysisStore.filterObject.classLevel != 2
  1507. ? "line_bar_chart"
  1508. : "vertical_bar";
  1509. state.majorQuestionData.chartTypeList =
  1510. analysisStore.filterObject.classLevel != 2 ? chartTypeLists : chartTypeList;
  1511. state.majorQuestionData.chartType =
  1512. analysisStore.filterObject.classLevel != 2
  1513. ? "line_bar_chart"
  1514. : "vertical_bar";
  1515. state.questionScoreStatsData.chartType = "vertical_bar";
  1516. state.majorTableData.currentPage = 1;
  1517. state.majorAnswerData.rowIndex = 0;
  1518. GetQuestionGroupAnalysisData(); //题目分组分析
  1519. };
  1520. // 监听筛选条件
  1521. watch(
  1522. () => analysisStore.filterObject,
  1523. async () => {
  1524. pageInit();
  1525. },
  1526. { deep: true },
  1527. );
  1528. onMounted(() => {
  1529. pageInit();
  1530. });
  1531. </script>
  1532. <style lang="scss" scoped>
  1533. .content_left {
  1534. &.answer {
  1535. width: calc(100% - 220px);
  1536. }
  1537. &.paper_card {
  1538. width: calc(100% - 480px);
  1539. height: 400px;
  1540. background-color: #f5f5f5;
  1541. border-radius: 10px;
  1542. }
  1543. }
  1544. .content_right {
  1545. height: 100%;
  1546. display: flex;
  1547. align-items: center;
  1548. margin: auto 0 auto auto;
  1549. padding-bottom: 0;
  1550. &.answer {
  1551. width: 200px;
  1552. }
  1553. &.paper_card {
  1554. width: 460px;
  1555. }
  1556. :deep(.el-table) {
  1557. border-left: 0px;
  1558. border-right: 0px;
  1559. .el-table__cell {
  1560. height: 44px;
  1561. }
  1562. }
  1563. }
  1564. </style>