SetBlueDialog.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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. teachCourseId: this.course.teachCourseId,
  135. });
  136. this.treeData = (data || []).map((item) => {
  137. return {
  138. id: item.id,
  139. name: item.targetName,
  140. totalWeight: item.totalWeight,
  141. disabled: false,
  142. children: item.dimensionList.map((elem) => {
  143. return { ...elem, disabled: false };
  144. }),
  145. };
  146. });
  147. },
  148. async getBlueDetail() {
  149. const res = await endScorePaperPositiveDetail({
  150. examId: this.course.examId,
  151. courseCode: this.course.courseCode,
  152. teachCourseId: this.course.teachCourseId,
  153. });
  154. this.dataList = res || [];
  155. this.updateTargetRates();
  156. },
  157. visibleChange() {
  158. this.getBlueDetail();
  159. },
  160. cancel() {
  161. this.modalIsShow = false;
  162. },
  163. open() {
  164. this.modalIsShow = true;
  165. },
  166. async toSync() {
  167. if (this.loading) return;
  168. this.loading = true;
  169. const res = await endScorePaperPositiveSync({
  170. examId: this.course.examId,
  171. courseCode: this.course.courseCode,
  172. teachCourseId: this.course.teachCourseId,
  173. paperNumber: this.dataList[0]?.paperNumber,
  174. }).catch(() => {});
  175. this.loading = false;
  176. if (!res) return;
  177. this.$message.success(`${res.success},错误:${res.error}`);
  178. this.getBlueDetail();
  179. },
  180. checkData() {
  181. const valid = !this.dataList.some(
  182. (item) => !item.targetList || !item.targetList.length
  183. );
  184. if (!valid) {
  185. this.$message.error("还有小题未设置知识点,请完成设置!");
  186. return;
  187. }
  188. const unvalidTargets = [];
  189. Object.keys(this.targetRates).forEach((tid) => {
  190. const target = this.targetRates[tid];
  191. if (!target.valid) unvalidTargets.push(target.name);
  192. });
  193. if (unvalidTargets.length) {
  194. this.$message.error(`${unvalidTargets.join("、")}占比不符合要求`);
  195. return;
  196. }
  197. return true;
  198. },
  199. toLink(row) {
  200. this.curRow = row;
  201. this.selectedData = [];
  202. row.targetList.forEach((target) => {
  203. target.dimensionList.forEach((dimension) => {
  204. this.selectedData.push(dimension.dimensionId);
  205. });
  206. });
  207. this.$refs.SelectBlueDimensionDialog.open();
  208. },
  209. dimensionSelected(targetList) {
  210. this.curRow.targetList = targetList;
  211. this.curRow.courseTargetName = targetList[0].targetName;
  212. this.updateTargetRates();
  213. },
  214. updateTargetRates() {
  215. const scoreData = {};
  216. this.dataList.forEach((item) => {
  217. if (!item.targetList || !item.targetList.length) return;
  218. const targetId = item.targetList[0].targetId;
  219. if (!scoreData[targetId]) scoreData[targetId] = 0;
  220. scoreData[targetId] += item.score;
  221. });
  222. const totalScore = calcSum(this.dataList.map((item) => item.score));
  223. const targetRates = {};
  224. this.treeData.forEach((target) => {
  225. const targetScore = scoreData[target.id] || 0;
  226. const rate = !totalScore ? 0 : (100 * targetScore) / totalScore;
  227. targetRates[target.id] = {
  228. rate: Number.isInteger(rate) ? rate : rate.toFixed(2),
  229. valid: rate == target.totalWeight,
  230. name: target.name,
  231. };
  232. });
  233. this.targetRates = targetRates;
  234. },
  235. async submit() {
  236. if (!this.checkData()) {
  237. return;
  238. }
  239. if (this.isSubmit) return;
  240. this.isSubmit = true;
  241. const datas = {
  242. examId: this.course.examId,
  243. courseCode: this.course.courseCode,
  244. teachCourseId: this.course.teachCourseId,
  245. paperStruct: this.dataList,
  246. };
  247. const data = await endScorePaperPositiveSave(datas).catch(() => {});
  248. this.isSubmit = false;
  249. if (!data) return;
  250. this.$message.success("修改成功!");
  251. this.$emit("modified");
  252. this.cancel();
  253. },
  254. },
  255. };
  256. </script>