SetBlueDialog.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <template>
  2. <div>
  3. <el-dialog
  4. :visible.sync="modalIsShow"
  5. title="设置试卷蓝图"
  6. top="10px"
  7. width="660px"
  8. :close-on-click-modal="false"
  9. :close-on-press-escape="false"
  10. append-to-body
  11. @open="visibleChange"
  12. >
  13. <div class="mb-2 box-justify">
  14. <div class="box-grow mr-2">
  15. <span v-for="(target, index) in treeData" :key="target.id">
  16. <span>{{ target.name }}占比</span>
  17. <span
  18. v-if="targetRates[target.id]"
  19. :class="[
  20. 'mlr-1',
  21. targetRates[target.id].valid ? 'color-success' : 'color-danger',
  22. ]"
  23. >{{ targetRates[target.id].rate }}%</span
  24. >
  25. <span>({{ target.totalWeight || 0 }}%)</span>
  26. <span>{{ index === treeData.length - 1 ? "。" : "," }}</span>
  27. </span>
  28. </div>
  29. <el-button type="primary" :loading="loading" @click="toSync"
  30. >同步</el-button
  31. >
  32. </div>
  33. <el-table :data="dataList" border height="400">
  34. <el-table-column
  35. prop="mainNumber"
  36. label="大题号"
  37. width="80px"
  38. ></el-table-column>
  39. <el-table-column
  40. prop="subNumber"
  41. label="小题号"
  42. width="80px"
  43. ></el-table-column>
  44. <el-table-column
  45. prop="score"
  46. label="小题满分"
  47. width="80px"
  48. ></el-table-column>
  49. <el-table-column prop="courseTargetName" label="所属课程目标">
  50. </el-table-column>
  51. <!-- <el-table-column prop="dimensionList" label="知识点">
  52. <template slot-scope="scope">
  53. <template v-for="target in scope.row.targetList">
  54. <p
  55. v-for="item in target.dimensionList"
  56. :key="`${target.targetId}_${item.dimensionId}`"
  57. >
  58. {{ item.dimensionName }}
  59. </p>
  60. </template>
  61. </template>
  62. </el-table-column> -->
  63. <el-table-column class-name="action-column" label="操作" width="110px">
  64. <template slot-scope="scope">
  65. <el-button
  66. class="btn-primary"
  67. type="text"
  68. @click="toLink(scope.row)"
  69. >关联知识点</el-button
  70. >
  71. </template>
  72. </el-table-column>
  73. </el-table>
  74. <div slot="footer">
  75. <el-button type="primary" :disabled="isSubmit" @click="submit"
  76. >确认</el-button
  77. >
  78. <el-button @click="cancel">取消</el-button>
  79. </div>
  80. </el-dialog>
  81. <!-- 设置知识点 -->
  82. <select-blue-dimension-dialog
  83. ref="SelectBlueDimensionDialog"
  84. :tree-data="treeData"
  85. :selected-data="selectedData"
  86. @confirm="dimensionSelected"
  87. ></select-blue-dimension-dialog>
  88. </div>
  89. </template>
  90. <script>
  91. import { calcSum } from "@/plugins/utils";
  92. import { courseTargetList } from "../../base/api";
  93. import {
  94. endScorePaperPositiveDetail,
  95. endScorePaperPositiveSave,
  96. endScorePaperPositiveSync,
  97. } from "../api";
  98. import SelectBlueDimensionDialog from "./SelectBlueDimensionDialog.vue";
  99. export default {
  100. name: "SetBlueDialog",
  101. components: { SelectBlueDimensionDialog },
  102. props: {
  103. course: {
  104. type: Object,
  105. default() {
  106. return {};
  107. },
  108. },
  109. },
  110. data() {
  111. return {
  112. modalIsShow: false,
  113. isSubmit: false,
  114. dataList: [],
  115. curRow: {},
  116. selectedData: [],
  117. treeData: [],
  118. targetRates: {},
  119. loading: false,
  120. };
  121. },
  122. watch: {
  123. course: {
  124. immediate: true,
  125. handler(val, oldVal) {
  126. if (!val) return;
  127. if (val !== oldVal) this.getTree();
  128. },
  129. },
  130. },
  131. methods: {
  132. async getTree() {
  133. const data = await courseTargetList({
  134. examId: this.course.examId,
  135. courseCode: this.course.courseCode,
  136. teachCourseId: this.course.teachCourseId,
  137. });
  138. this.treeData = (data || []).map((item) => {
  139. return {
  140. id: item.id,
  141. name: item.targetName,
  142. totalWeight: item.totalWeight,
  143. disabled: false,
  144. children: item.dimensionList.map((elem) => {
  145. return { ...elem, disabled: false };
  146. }),
  147. };
  148. });
  149. },
  150. async getBlueDetail() {
  151. const res = await endScorePaperPositiveDetail({
  152. examId: this.course.examId,
  153. courseCode: this.course.courseCode,
  154. teachCourseId: this.course.teachCourseId,
  155. });
  156. this.dataList = res || [];
  157. this.updateTargetRates();
  158. },
  159. visibleChange() {
  160. this.getBlueDetail();
  161. },
  162. cancel() {
  163. this.modalIsShow = false;
  164. },
  165. open() {
  166. this.modalIsShow = true;
  167. },
  168. async toSync() {
  169. if (this.loading) return;
  170. this.loading = true;
  171. const res = await endScorePaperPositiveSync({
  172. examId: this.course.examId,
  173. courseCode: this.course.courseCode,
  174. teachCourseId: this.course.teachCourseId,
  175. paperNumber: this.dataList[0]?.paperNumber,
  176. }).catch(() => {});
  177. this.loading = false;
  178. if (!res) return;
  179. this.$message.success(`${res.success},错误:${res.error}`);
  180. this.getBlueDetail();
  181. },
  182. checkData() {
  183. const valid = !this.dataList.some(
  184. (item) => !item.targetList || !item.targetList.length
  185. );
  186. if (!valid) {
  187. this.$message.error("还有小题未设置知识点,请完成设置!");
  188. return;
  189. }
  190. const unvalidTargets = [];
  191. Object.keys(this.targetRates).forEach((tid) => {
  192. const target = this.targetRates[tid];
  193. if (!target.valid) unvalidTargets.push(target.name);
  194. });
  195. if (unvalidTargets.length) {
  196. this.$message.error(`${unvalidTargets.join("、")}占比不符合要求`);
  197. return;
  198. }
  199. return true;
  200. },
  201. toLink(row) {
  202. this.curRow = row;
  203. this.selectedData = [];
  204. row.targetList.forEach((target) => {
  205. target.dimensionList.forEach((dimension) => {
  206. this.selectedData.push(dimension.dimensionId);
  207. });
  208. });
  209. this.$refs.SelectBlueDimensionDialog.open();
  210. },
  211. dimensionSelected(targetList) {
  212. this.curRow.targetList = targetList;
  213. this.curRow.courseTargetName = targetList[0].targetName;
  214. this.updateTargetRates();
  215. },
  216. updateTargetRates() {
  217. const scoreData = {};
  218. this.dataList.forEach((item) => {
  219. if (!item.targetList || !item.targetList.length) return;
  220. const targetId = item.targetList[0].targetId;
  221. if (!scoreData[targetId]) scoreData[targetId] = 0;
  222. scoreData[targetId] += item.score;
  223. });
  224. const totalScore = calcSum(this.dataList.map((item) => item.score));
  225. const targetRates = {};
  226. this.treeData.forEach((target) => {
  227. const targetScore = scoreData[target.id] || 0;
  228. const rate = !totalScore ? 0 : (100 * targetScore) / totalScore;
  229. targetRates[target.id] = {
  230. rate: Number.isInteger(rate) ? rate : rate.toFixed(2),
  231. valid: rate == target.totalWeight,
  232. name: target.name,
  233. };
  234. });
  235. this.targetRates = targetRates;
  236. },
  237. async submit() {
  238. if (!this.checkData()) {
  239. return;
  240. }
  241. if (this.isSubmit) return;
  242. this.isSubmit = true;
  243. const datas = {
  244. examId: this.course.examId,
  245. courseCode: this.course.courseCode,
  246. teachCourseId: this.course.teachCourseId,
  247. paperStruct: this.dataList,
  248. };
  249. const data = await endScorePaperPositiveSave(datas).catch(() => {});
  250. this.isSubmit = false;
  251. if (!data) return;
  252. this.$message.success("修改成功!");
  253. this.$emit("modified");
  254. this.cancel();
  255. },
  256. },
  257. };
  258. </script>