TargetReportDetail.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. <template>
  2. <el-dialog
  3. class="page-dialog"
  4. :visible.sync="modalIsShow"
  5. :close-on-click-modal="false"
  6. :close-on-press-escape="false"
  7. :show-close="false"
  8. append-to-body
  9. fullscreen
  10. @opened="initData"
  11. >
  12. <div slot="title" class="box-justify">
  13. <div>
  14. 报告详情<span class="color-gray ml-2"
  15. >{{ course.courseName }}({{ course.courseCode }})</span
  16. >
  17. </div>
  18. <div>
  19. <el-button type="primary" :loading="downloading" @click="toSave"
  20. >保存报告</el-button
  21. >
  22. <el-button type="primary" :loading="downloading" @click="toExport"
  23. >导出报告</el-button
  24. >
  25. <el-button type="danger" @click="cancel">退出</el-button>
  26. </div>
  27. </div>
  28. <!-- base info -->
  29. <div class="page-head">
  30. <h2>
  31. <span>课程基本情况</span>
  32. </h2>
  33. </div>
  34. <div class="part-box part-box-pad">
  35. <table class="table table-tiny">
  36. <tr>
  37. <td class="td-bg">课程编码</td>
  38. <td>{{ commonInfo.courseCode }}</td>
  39. <td class="td-bg">课程名称</td>
  40. <td>{{ commonInfo.courseName }}</td>
  41. <td class="td-bg">课程类别</td>
  42. <td>
  43. <el-input
  44. v-model="courseBasicInfo.courseType"
  45. placeholder="请填写课程类别"
  46. size="small"
  47. ></el-input>
  48. </td>
  49. <td class="td-bg">学分</td>
  50. <td>
  51. <el-input-number
  52. v-model="courseBasicInfo.credit"
  53. placeholder="请录入学分"
  54. size="small"
  55. :min="0"
  56. :max="999"
  57. :controls="false"
  58. style="width: 100px"
  59. ></el-input-number>
  60. </td>
  61. </tr>
  62. <tr>
  63. <td class="td-bg">考核方式</td>
  64. <td>
  65. <el-input
  66. v-model="courseBasicInfo.evaluationMode"
  67. placeholder="请填写考核方式"
  68. size="small"
  69. ></el-input>
  70. </td>
  71. <td class="td-bg">开课时间</td>
  72. <td>{{ courseBasicInfo.openTime }}</td>
  73. <td class="td-bg">授课对象</td>
  74. <td>{{ courseBasicInfo.teachingObject }}</td>
  75. <td class="td-bg">学时</td>
  76. <td>
  77. <el-input-number
  78. v-model="courseBasicInfo.period"
  79. placeholder="请录入学时"
  80. :min="0"
  81. :max="999"
  82. :controls="false"
  83. size="small"
  84. style="width: 100px"
  85. ></el-input-number>
  86. </td>
  87. </tr>
  88. <tr>
  89. <td class="td-bg">选课人数</td>
  90. <td>
  91. <el-input-number
  92. v-model="courseBasicInfo.selectionCount"
  93. placeholder="请录入选课人数"
  94. :min="0"
  95. :max="999"
  96. :controls="false"
  97. size="small"
  98. style="width: 125px"
  99. ></el-input-number>
  100. </td>
  101. <td class="td-bg">参评人数</td>
  102. <td>{{ courseBasicInfo.participantCount }}</td>
  103. <td class="td-bg">课程目标达成度期望值</td>
  104. <td colspan="3">
  105. <el-select
  106. v-model="courseBasicInfo.courseDegree"
  107. size="small"
  108. style="width: 100%"
  109. >
  110. <el-option
  111. v-for="item in expectancyList"
  112. :key="item"
  113. :value="item"
  114. :label="item"
  115. ></el-option>
  116. </el-select>
  117. </td>
  118. </tr>
  119. <tr>
  120. <td class="td-bg">任课老师</td>
  121. <td>{{ courseBasicInfo.teacher }}</td>
  122. <td class="td-bg">评价负责人</td>
  123. <td>
  124. <el-input
  125. v-model="courseBasicInfo.director"
  126. placeholder="请填写评价负责人姓名"
  127. size="small"
  128. ></el-input>
  129. </td>
  130. <td class="td-bg">评价参与人</td>
  131. <td colspan="3">
  132. <el-input
  133. v-model="courseBasicInfo.participant"
  134. placeholder="请填写评价人姓名"
  135. size="small"
  136. ></el-input>
  137. </td>
  138. </tr>
  139. </table>
  140. </div>
  141. <div class="page-head">
  142. <h2>
  143. <span>课程目标考核分布</span>
  144. <el-popover
  145. popper-class="el-popper-dark"
  146. placement="right-start"
  147. width="500"
  148. trigger="hover"
  149. >
  150. <i class="el-icon-question" slot="reference"></i>
  151. <div>
  152. <p>
  153. 课程目标分布会自动按题库系统里试卷下试题标记的属性内容进行显示
  154. </p>
  155. </div>
  156. </el-popover>
  157. </h2>
  158. </div>
  159. <div class="part-box part-box-pad">
  160. <el-table :data="questionInfoTable">
  161. <el-table-column
  162. label="试题号"
  163. prop="name"
  164. width="140"
  165. align="center"
  166. fixed="left"
  167. ></el-table-column>
  168. <el-table-column
  169. label="合计"
  170. prop="totalScore"
  171. width="100"
  172. align="center"
  173. fixed="left"
  174. ></el-table-column>
  175. <el-table-column
  176. v-for="struct in paperStructs"
  177. :key="struct.mainNumber"
  178. :label="struct.mainNumber"
  179. align="center"
  180. >
  181. <el-table-column
  182. v-for="subNumber in struct.subNumbers"
  183. :key="subNumber"
  184. :label="subNumber"
  185. :prop="`${struct.mainNumber}_${subNumber}`"
  186. align="center"
  187. >
  188. </el-table-column>
  189. </el-table-column>
  190. </el-table>
  191. <div class="chart-box" style="height: 300px; margin-top: 20px">
  192. <v-chart
  193. v-if="questionInfoChartOption"
  194. :option="questionInfoChartOption"
  195. ></v-chart>
  196. </div>
  197. </div>
  198. <div class="page-head">
  199. <h2>
  200. <span>课程目标达成评价结果</span>
  201. <el-popover
  202. popper-class="el-popper-dark"
  203. placement="right-start"
  204. width="500"
  205. trigger="hover"
  206. >
  207. <i class="el-icon-question" slot="reference"></i>
  208. <div>
  209. <p>1.评价依据:填写通过考核什么内容实现课程目标的评价;</p>
  210. <p>2.目标分值:对应考核环节的满分;</p>
  211. <p>
  212. 3.权重:对应考核环节在对应的课程目标中的权重,目标下各项权重相加等于1;
  213. </p>
  214. <p>4.实际平均分:为参与评价的学生在该环节的平均分;</p>
  215. <p>5.目标达成评价值:∑(实际平均分/目标分值*权重);</p>
  216. <p>6.整体课程目标达成评价值:为课程分目标达成评价值的最小值。</p>
  217. </div>
  218. </el-popover>
  219. </h2>
  220. </div>
  221. <div class="part-box part-box-pad">
  222. <h4 class="part-title">课程考核成绩评价结果</h4>
  223. <table class="table">
  224. <tr class="td-bg">
  225. <th>课程目标</th>
  226. <th>评价依据</th>
  227. <th>评价环节</th>
  228. <th>权重</th>
  229. <th>目标分值</th>
  230. <th>实际平均分</th>
  231. <th>目标达成评价值</th>
  232. </tr>
  233. <template v-for="item in courseTargetList">
  234. <tr
  235. v-for="(evaluation, eindex) in item.evaluationList"
  236. :key="`${item.targetId}-${eindex}`"
  237. >
  238. <td
  239. v-if="!eindex"
  240. :rowspan="item.evaluationList.length"
  241. style="width: 240px"
  242. >
  243. {{ item.targetName }}
  244. </td>
  245. <td v-if="!eindex" :rowspan="item.evaluationList.length">
  246. <p v-for="edesc in item.evaluationDesc.split('\n')" :key="edesc">
  247. {{ edesc }}
  248. </p>
  249. </td>
  250. <td style="width: 140px">{{ evaluation.name }}</td>
  251. <td style="width: 80px">{{ evaluation.weight }}</td>
  252. <td style="width: 100px">{{ evaluation.score }}</td>
  253. <td style="width: 100px">{{ evaluation.avg }}</td>
  254. <td
  255. v-if="!eindex"
  256. :rowspan="item.evaluationList.length"
  257. style="width: 140px"
  258. >
  259. {{ item.evaluationValue }}
  260. </td>
  261. </tr>
  262. </template>
  263. <tr>
  264. <td colspan="6">课程总目标</td>
  265. <td>{{ courseTargetValue }}</td>
  266. </tr>
  267. </table>
  268. <div class="chart-box" style="height: 400px; margin-top: 20px">
  269. <v-chart
  270. v-if="courseTargetListChartOption"
  271. :option="courseTargetListChartOption"
  272. ></v-chart>
  273. </div>
  274. <h4 class="part-title">课程考核成绩评价明细结果</h4>
  275. <el-table :data="studentScoreTable" :span-method="studentScoreSpanMethod">
  276. <el-table-column
  277. label="姓名"
  278. prop="name"
  279. min-width="120"
  280. align="center"
  281. fixed="left"
  282. ></el-table-column>
  283. <el-table-column
  284. label="学号"
  285. prop="studentCode"
  286. width="100"
  287. align="center"
  288. fixed="left"
  289. ></el-table-column>
  290. <el-table-column
  291. v-for="(target, tindex) in courseTargets"
  292. :key="tindex"
  293. :label="target.name"
  294. align="center"
  295. >
  296. <el-table-column min-width="120" align="center">
  297. <template slot="header">
  298. 期末考试<br />({{ target.finalWeight | percentFilter }})
  299. </template>
  300. <el-table-column
  301. v-for="dimension in target.finalDimensions"
  302. :key="`${target.targetId}${dimension}`"
  303. :label="dimension"
  304. :prop="`${target.targetId}-final-${dimension}`"
  305. min-width="60"
  306. align="center"
  307. >
  308. </el-table-column>
  309. </el-table-column>
  310. <el-table-column min-width="120" align="center">
  311. <template slot="header">
  312. 平时考试<br />({{ target.usualWeight | percentFilter }})
  313. </template>
  314. <el-table-column
  315. v-for="work in target.usualWorks"
  316. :key="`${target.targetId}${work}`"
  317. :label="work"
  318. :prop="`${target.targetId}-usual-${work}`"
  319. min-width="100"
  320. align="center"
  321. >
  322. </el-table-column>
  323. </el-table-column>
  324. </el-table-column>
  325. <el-table-column
  326. label="学生成绩"
  327. prop="score"
  328. align="center"
  329. width="100"
  330. ></el-table-column>
  331. </el-table>
  332. </div>
  333. <div slot="footer"></div>
  334. </el-dialog>
  335. </template>
  336. <script>
  337. // import { reportData } from "./data";
  338. import {
  339. targetReportDetail,
  340. exportTargetReport,
  341. targetReportSave,
  342. } from "../api";
  343. import { downloadByApi } from "@/plugins/download";
  344. import { calcSum } from "@/plugins/utils";
  345. export default {
  346. name: "target-report-detail",
  347. props: {
  348. course: {
  349. type: Object,
  350. default() {
  351. return {};
  352. },
  353. },
  354. },
  355. filters: {
  356. percentFilter(val) {
  357. return ((val || 0) / 100).toFixed(2);
  358. },
  359. },
  360. data() {
  361. return {
  362. modalIsShow: false,
  363. expectancyList: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
  364. commonInfo: {},
  365. courseBasicInfo: {},
  366. paperStructs: [],
  367. questionInfoTable: [],
  368. courseTargetList: [],
  369. courseTargetValue: 0.67,
  370. questionInfoChartOption: null,
  371. courseTargetListChartOption: null,
  372. courseTargets: [],
  373. targetColumnCounts: [],
  374. studentScoreTable: [],
  375. downloading: false,
  376. };
  377. },
  378. methods: {
  379. cancel() {
  380. this.modalIsShow = false;
  381. },
  382. open() {
  383. this.modalIsShow = true;
  384. },
  385. buildData(data) {
  386. this.commonInfo = data.commonInfo;
  387. this.courseBasicInfo = data.courseBasicInfo;
  388. this.courseTargetValue = data.courseEvaluationResultInfo.targetValue;
  389. this.courseTargetList = data.courseEvaluationResultInfo.targetList;
  390. const {
  391. courseEvaluationSpreadInfo: { questionInfo, scoreList },
  392. } = data;
  393. this.parsePaperStructs(questionInfo);
  394. this.parseQuestionInfoTable(questionInfo);
  395. this.questionInfoChartOption = this.getQuestionInfoChartOption(scoreList);
  396. this.courseTargetListChartOption = this.getCourseTargetListChartOption();
  397. const {
  398. courseEvaluationResultDetailInfo: { examStudentList },
  399. } = data;
  400. this.parseCourseTargets(examStudentList);
  401. this.parseStudentScoreTable(examStudentList);
  402. },
  403. async initData() {
  404. const data = await targetReportDetail({
  405. examId: this.course.examId,
  406. courseCode: this.course.courseCode,
  407. paperNumber: this.course.paperNumber,
  408. });
  409. this.buildData(data);
  410. },
  411. parsePaperStructs(questionInfo) {
  412. if (!questionInfo || !questionInfo.length) return;
  413. const structMap = {};
  414. questionInfo.forEach((item) => {
  415. if (!structMap[item.mainNumber]) {
  416. structMap[item.mainNumber] = [];
  417. }
  418. structMap[item.mainNumber].push(item.subNumber + "");
  419. });
  420. this.paperStructs = Object.keys(structMap).map((mainNumber) => {
  421. return {
  422. mainNumber,
  423. subNumbers: structMap[mainNumber],
  424. };
  425. });
  426. },
  427. parseQuestionInfoTable(questionInfo) {
  428. const targetMap = {};
  429. const tData = {
  430. name: "目标分值",
  431. totalScore: calcSum(questionInfo.map((item) => item.score || 0)),
  432. };
  433. questionInfo.forEach((question) => {
  434. const qno = `${question.mainNumber}_${question.subNumber}`;
  435. tData[qno] = question.score;
  436. question.targetList.forEach((target) => {
  437. if (!targetMap[target.targetId]) {
  438. targetMap[target.targetId] = {
  439. name: target.targetName,
  440. totalScore: 0,
  441. };
  442. }
  443. if (target.dimensionList.length) {
  444. targetMap[target.targetId][qno] = question.score;
  445. targetMap[target.targetId].totalScore += question.score;
  446. } else {
  447. targetMap[target.targetId][qno] = "";
  448. }
  449. });
  450. });
  451. this.questionInfoTable = [tData, ...Object.values(targetMap)];
  452. },
  453. getQuestionInfoChartOption(scoreList) {
  454. const scoreRange = scoreList.scoreRange.map((item) => {
  455. return {
  456. name: `${item.minScore}~${item.maxScore}`,
  457. value: item.studentCount,
  458. };
  459. });
  460. scoreRange.unshift({
  461. name: "不及格",
  462. value: scoreList.failRate,
  463. });
  464. let options = {
  465. color: ["#556dff", "#3A5AE5"],
  466. title: {
  467. text: "成绩分布图",
  468. left: "center",
  469. },
  470. grid: {
  471. left: 40,
  472. top: 60,
  473. right: 80,
  474. bottom: 40,
  475. containLabel: true,
  476. },
  477. xAxis: {
  478. type: "category",
  479. name: "成绩(分)",
  480. nameTextStyle: {
  481. color: "#363D59",
  482. },
  483. data: scoreRange.map((item) => item.name),
  484. axisLabel: {
  485. color: "#6F7482",
  486. interval: 0,
  487. fontSize: 12,
  488. margin: 12,
  489. },
  490. axisLine: {
  491. show: true,
  492. lineStyle: {
  493. color: "#EFF0F5",
  494. },
  495. },
  496. splitLine: {
  497. show: false,
  498. },
  499. axisTick: {
  500. show: false,
  501. },
  502. axisPointer: {
  503. type: "shadow",
  504. },
  505. },
  506. yAxis: {
  507. type: "value",
  508. name: "百分比(%)",
  509. nameTextStyle: {
  510. color: "#363D59",
  511. },
  512. axisLabel: {
  513. color: "#6F7482",
  514. },
  515. axisLine: {
  516. lineStyle: {
  517. color: "#EFF0F5",
  518. },
  519. },
  520. splitLine: {
  521. lineStyle: {
  522. color: "#EFF0F5",
  523. },
  524. },
  525. },
  526. series: [
  527. {
  528. name: "占比",
  529. type: "bar",
  530. barWidth: 60,
  531. data: scoreRange.map((item) => item.value),
  532. label: {
  533. show: true,
  534. formatter: "{c}%",
  535. position: "top",
  536. },
  537. },
  538. ],
  539. };
  540. return options;
  541. },
  542. getCourseTargetListChartOption() {
  543. let options = {
  544. color: ["#556dff", "#f59a23"],
  545. title: {
  546. text: "课程目标达成评价值",
  547. left: "center",
  548. },
  549. grid: {
  550. left: 40,
  551. top: 40,
  552. right: 80,
  553. bottom: 30,
  554. containLabel: true,
  555. },
  556. legend: {
  557. top: 0,
  558. data: ["期望值", "达成值"],
  559. itemWidth: 12,
  560. itemHeight: 4,
  561. itemGap: 22,
  562. right: 40,
  563. },
  564. xAxis: {
  565. type: "category",
  566. name: "课程目标",
  567. nameTextStyle: {
  568. color: "#363D59",
  569. },
  570. data: this.courseTargetList.map((item) => item.targetName),
  571. axisLabel: {
  572. color: "#6F7482",
  573. interval: 0,
  574. fontSize: 12,
  575. margin: 12,
  576. },
  577. axisLine: {
  578. show: true,
  579. lineStyle: {
  580. color: "#EFF0F5",
  581. },
  582. },
  583. splitLine: {
  584. show: false,
  585. },
  586. axisTick: {
  587. show: false,
  588. },
  589. axisPointer: {
  590. type: "shadow",
  591. },
  592. },
  593. yAxis: {
  594. type: "value",
  595. name: "期望值",
  596. min: 0,
  597. max: 1,
  598. interval: 0.1,
  599. nameTextStyle: {
  600. color: "#363D59",
  601. },
  602. axisLabel: {
  603. color: "#6F7482",
  604. },
  605. axisLine: {
  606. lineStyle: {
  607. color: "#EFF0F5",
  608. },
  609. },
  610. splitLine: {
  611. lineStyle: {
  612. color: "#EFF0F5",
  613. },
  614. },
  615. },
  616. series: [
  617. {
  618. name: "达成值",
  619. type: "bar",
  620. barWidth: 60,
  621. data: this.courseTargetList.map((item) => item.evaluationValue),
  622. label: {
  623. show: true,
  624. position: "top",
  625. },
  626. },
  627. {
  628. name: "期望值",
  629. type: "line",
  630. symbol: "none",
  631. data: this.courseTargetList.map((item) => this.courseTargetValue),
  632. },
  633. ],
  634. };
  635. return options;
  636. },
  637. parseCourseTargets(examStudentList) {
  638. if (!examStudentList || !examStudentList.length) return;
  639. const targetList = examStudentList[0].targetList;
  640. const targetVals = {};
  641. this.courseTargetList.forEach((target) => {
  642. targetVals[target.targetId] = target.evaluationValue;
  643. });
  644. let tColumnCounts = [];
  645. this.courseTargets = targetList.map((target) => {
  646. const ntarget = {
  647. targetId: target.targetId,
  648. targetName: target.targetName,
  649. finalWeight: target.finalScore.weight,
  650. usualWeight: target.usualScore.weight,
  651. evaluationValue: targetVals[target.targetId],
  652. };
  653. ntarget.finalDimensions = target.finalScore.dimensionList.map(
  654. (item) => item.dimensionCode
  655. );
  656. ntarget.usualWorks = target.usualScore.scoreList.map(
  657. (item) => item.name
  658. );
  659. tColumnCounts.push(
  660. ntarget.finalDimensions.length + ntarget.usualWorks.length
  661. );
  662. return ntarget;
  663. });
  664. let tCount = 0;
  665. this.targetColumnCounts = tColumnCounts.map((item) => {
  666. tCount += item;
  667. return tCount;
  668. });
  669. },
  670. parseStudentScoreTable(examStudentList) {
  671. const studentScoreTable = examStudentList.map((student) => {
  672. const nitem = {
  673. name: student.name,
  674. studentCode: student.studentCode,
  675. score: student.score,
  676. };
  677. student.targetList.forEach((target) => {
  678. target.finalScore.dimensionList.forEach((dimension) => {
  679. nitem[`${target.targetId}-final-${dimension.dimensionCode}`] =
  680. dimension.dimensionScore;
  681. });
  682. target.usualScore.scoreList.forEach((work) => {
  683. nitem[`${target.targetId}-usual-${work.name}`] = work.score;
  684. });
  685. });
  686. return nitem;
  687. });
  688. const targetData = {
  689. name: "课程目标达成度",
  690. score: "",
  691. };
  692. this.courseTargets.forEach((target) => {
  693. targetData[`${target.targetId}-final-${target.finalDimensions[0]}`] =
  694. target.evaluationValue;
  695. });
  696. studentScoreTable.push(targetData);
  697. const fTarget = this.courseTargets[0];
  698. studentScoreTable.push({
  699. name: "课程达成度",
  700. [`${fTarget.targetId}-final-${fTarget.finalDimensions[0]}`]:
  701. this.courseTargetValue,
  702. });
  703. this.studentScoreTable = studentScoreTable;
  704. },
  705. studentScoreSpanMethod({ row, column, rowIndex, columnIndex }) {
  706. const lineCount = this.studentScoreTable.length - 1;
  707. const maxTargetColumnCount = this.targetColumnCounts.slice(-1)[0];
  708. if (rowIndex === lineCount) {
  709. if (columnIndex === 0) {
  710. return [1, 2];
  711. } else if (columnIndex === 2) {
  712. return [1, maxTargetColumnCount + 1];
  713. } else {
  714. return [0, 0];
  715. }
  716. }
  717. if (rowIndex === lineCount - 1) {
  718. const colsMap = {};
  719. const targetColumns = [0, ...this.targetColumnCounts.slice(0, -1)];
  720. targetColumns.map((item, index) => {
  721. colsMap[item] = this.targetColumnCounts[index] - item;
  722. });
  723. if (columnIndex === 0) {
  724. return [1, 2];
  725. } else {
  726. if (targetColumns.includes(columnIndex - 2)) {
  727. return [1, colsMap[columnIndex - 2]];
  728. } else if (columnIndex < maxTargetColumnCount + 2) {
  729. return [0, 0];
  730. } else {
  731. return [1, 1];
  732. }
  733. }
  734. }
  735. if (rowIndex === lineCount - 2) {
  736. if (columnIndex === 0) {
  737. return [1, 2];
  738. } else if (columnIndex === 1) {
  739. return [0, 0];
  740. } else {
  741. return [1, 1];
  742. }
  743. }
  744. },
  745. parseStudentScoreChartOption(examStudentTargets) {
  746. let options = {
  747. color: ["#556dff", "#f59a23"],
  748. title: {
  749. text: "课程目标达成评价值",
  750. left: "center",
  751. },
  752. grid: {
  753. left: 40,
  754. top: 40,
  755. right: 80,
  756. bottom: 30,
  757. containLabel: true,
  758. },
  759. legend: {
  760. top: 0,
  761. data: ["平时成绩", "期末成绩"],
  762. itemWidth: 12,
  763. itemHeight: 4,
  764. itemGap: 22,
  765. right: 40,
  766. },
  767. xAxis: {
  768. type: "category",
  769. name: "课程目标",
  770. nameTextStyle: {
  771. color: "#363D59",
  772. },
  773. data: examStudentTargets.map((item) => item.targetName),
  774. axisLabel: {
  775. color: "#6F7482",
  776. interval: 0,
  777. fontSize: 12,
  778. margin: 12,
  779. },
  780. axisLine: {
  781. show: true,
  782. lineStyle: {
  783. color: "#EFF0F5",
  784. },
  785. },
  786. splitLine: {
  787. show: false,
  788. },
  789. axisTick: {
  790. show: false,
  791. },
  792. axisPointer: {
  793. type: "shadow",
  794. },
  795. },
  796. yAxis: {
  797. type: "value",
  798. name: "期望值",
  799. min: 0,
  800. max: 1,
  801. interval: 0.1,
  802. nameTextStyle: {
  803. color: "#363D59",
  804. },
  805. axisLabel: {
  806. color: "#6F7482",
  807. },
  808. axisLine: {
  809. lineStyle: {
  810. color: "#EFF0F5",
  811. },
  812. },
  813. splitLine: {
  814. lineStyle: {
  815. color: "#EFF0F5",
  816. },
  817. },
  818. },
  819. series: [
  820. {
  821. name: "平时成绩",
  822. type: "bar",
  823. barWidth: 40,
  824. data: examStudentTargets.map((item) => item.usualScore),
  825. },
  826. {
  827. name: "期末成绩",
  828. type: "bar",
  829. barWidth: 40,
  830. data: examStudentTargets.map((item) => item.finalScore),
  831. },
  832. ],
  833. };
  834. return options;
  835. },
  836. async toSave() {
  837. if (this.downloading) return;
  838. this.downloading = true;
  839. const res = await targetReportSave({
  840. examId: this.course.examId,
  841. courseName: this.course.courseName,
  842. courseCode: this.course.courseCode,
  843. paperNumber: this.course.paperNumber,
  844. ...this.courseBasicInfo,
  845. }).catch(() => {});
  846. this.downloading = false;
  847. if (!res) return;
  848. this.$message.success("保存成功!");
  849. },
  850. async toExport() {
  851. if (this.downloading) return;
  852. this.downloading = true;
  853. const res = await downloadByApi(() => {
  854. const datas = {
  855. examId: this.course.examId,
  856. courseCode: this.course.courseCode,
  857. paperNumber: this.course.paperNumber,
  858. };
  859. return exportTargetReport(datas);
  860. }).catch((e) => {
  861. this.$message.error(e || "下载失败,请重新尝试!");
  862. });
  863. this.downloading = false;
  864. if (!res) return;
  865. this.$message.success("下载成功!");
  866. },
  867. },
  868. };
  869. </script>
  870. <style scoped>
  871. .td-bg {
  872. color: #8b8fa1;
  873. font-weight: 500;
  874. }
  875. </style>