MarkParamClass.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <template>
  2. <div class="mark-param-class">
  3. <div class="part-box part-box-pad box-justify">
  4. <div class="flex-center">
  5. <span>是否开启分班阅卷:</span>
  6. <el-radio-group v-model="classMarkIsOpen" @change="markClassChange">
  7. <el-radio-button :label="true">开启</el-radio-button>
  8. <el-radio-button :label="false">关闭</el-radio-button>
  9. </el-radio-group>
  10. <el-breadcrumb v-if="classMarkIsOpen" class="ml-2" separator="|">
  11. <el-breadcrumb-item
  12. >班级数:{{ stat.classCount }}个</el-breadcrumb-item
  13. >
  14. <el-breadcrumb-item
  15. >未分配评卷员班级数:<span class="color-danger">{{
  16. stat.unsignClassCount
  17. }}</span
  18. >个</el-breadcrumb-item
  19. >
  20. <el-breadcrumb-item
  21. >设置完成进度:<span class="color-primary"
  22. >{{ stat.completeRate }}%</span
  23. ></el-breadcrumb-item
  24. >
  25. </el-breadcrumb>
  26. </div>
  27. <div>
  28. <el-button type="primary" @click="toPrev(1)">上一步</el-button>
  29. <el-button type="primary" @click="toNext(1)">下一步</el-button>
  30. </div>
  31. </div>
  32. <div v-if="classMarkIsOpen" class="part-box part-box-pad class-body">
  33. <el-table :data="dataList" border>
  34. <el-table-column type="index" width="50"> </el-table-column>
  35. <el-table-column label="评卷员" width="200">
  36. <template slot-scope="scope">
  37. <el-tag size="medium">
  38. {{ scope.row.marker.name }}({{ scope.row.marker.loginName }})
  39. </el-tag>
  40. </template>
  41. </el-table-column>
  42. <el-table-column label="评阅题目">
  43. <template slot-scope="scope">
  44. {{ scope.row.userQuesions.join(",") }}
  45. </template>
  46. </el-table-column>
  47. <el-table-column prop="markerClassList" label="评卷班级">
  48. <template slot-scope="scope">
  49. {{ scope.row.markerClassList.join() }}
  50. </template>
  51. </el-table-column>
  52. <el-table-column class-name="action-column" label="操作" width="120">
  53. <template slot-scope="scope">
  54. <el-button
  55. class="btn-primary"
  56. type="text"
  57. @click="toSelectClass(scope.row)"
  58. >选择班级</el-button
  59. >
  60. </template>
  61. </el-table-column>
  62. </el-table>
  63. <div class="tips-info tips-error mt-2">
  64. <template v-if="subjectiveTaskList.length <= 1">
  65. <p v-if="unsignData.length">
  66. 未分配班级:{{ unsignData.map((item) => item.className).join() }}
  67. </p>
  68. </template>
  69. <template v-else>
  70. <p
  71. v-for="item in unsignData"
  72. :key="item.className"
  73. style="white-space: pre-wrap"
  74. >
  75. {{ item.content }}
  76. </p>
  77. </template>
  78. </div>
  79. </div>
  80. <div v-else class="part-box part-box-pad">
  81. <p class="tips-info">1.分班阅卷即评卷老师评指定班级的卷子;</p>
  82. <p class="tips-info">
  83. 2.如果需要进行分班阅卷,请在点击开启,不需要可以直接点击下一步,进行主观题评卷设置;
  84. </p>
  85. </div>
  86. <!-- SelectClassByCourse -->
  87. <select-class-by-course
  88. ref="SelectClassByCourse"
  89. :selected-ids="selectedClassIds"
  90. :class-list="classList"
  91. @confirm="classSelected"
  92. ></select-class-by-course>
  93. </div>
  94. </template>
  95. <script>
  96. import { markClassList, markClassSave, markClassStatusUpdate } from "../../api";
  97. import { mapState, mapMutations } from "vuex";
  98. import SelectClassByCourse from "./SelectClassByCourse.vue";
  99. import { toPrecision } from "@/plugins/utils";
  100. import { MD5 } from "@/plugins/md5";
  101. export default {
  102. name: "mark-param-class",
  103. components: { SelectClassByCourse },
  104. data() {
  105. return {
  106. dataList: [],
  107. cacheDataMd5: "",
  108. curRow: {},
  109. classList: [],
  110. selectedClassIds: [],
  111. loading: false,
  112. unsignData: [],
  113. classMarkIsOpen: false,
  114. };
  115. },
  116. computed: {
  117. ...mapState("markParam", [
  118. "basicInfo",
  119. "subjectiveTaskList",
  120. "openClassMark",
  121. ]),
  122. stat() {
  123. const classCount = this.classList.length;
  124. const unsignClassCount = this.unsignData.length;
  125. return {
  126. classCount,
  127. unsignClassCount,
  128. completeRate: classCount
  129. ? toPrecision((100 * (classCount - unsignClassCount)) / classCount, 2)
  130. : 0,
  131. };
  132. },
  133. },
  134. mounted() {
  135. this.initData();
  136. },
  137. methods: {
  138. ...mapMutations("markParam", ["setOpenClassMark"]),
  139. async initData() {
  140. this.classMarkIsOpen = this.openClassMark;
  141. if (!this.classMarkIsOpen) return;
  142. const params = {
  143. examId: this.basicInfo.examId,
  144. paperNumber: this.basicInfo.paperNumber,
  145. };
  146. const res = await markClassList(params);
  147. this.dataList = res.markerClass || [];
  148. this.classList = res.classNames || [];
  149. this.updateUserQuestion();
  150. this.updateUnsignData();
  151. this.cacheDataMd5 = this.getSubmitDataMd5();
  152. },
  153. resetData() {
  154. this.dataList = [];
  155. this.classList = [];
  156. this.selectedClassIds = [];
  157. this.unsignData = [];
  158. this.cacheDataMd5 = "";
  159. },
  160. updateUserQuestion() {
  161. const userQuesions = {};
  162. this.subjectiveTaskList.forEach((group) => {
  163. const qNo = `${group.mainNumber}-${group.subNumber}`;
  164. group.markers.forEach((marker) => {
  165. if (!userQuesions[marker.userId]) userQuesions[marker.userId] = [];
  166. userQuesions[marker.userId].push(qNo);
  167. });
  168. });
  169. this.dataList.forEach((row) => {
  170. row.userQuesions = userQuesions[row.marker.userId] || [];
  171. });
  172. },
  173. getSubmitDataMd5() {
  174. return MD5(JSON.stringify(this.dataList));
  175. },
  176. async markClassChange() {
  177. const name = this.classMarkIsOpen ? "开启" : "关闭";
  178. const confirm = await this.$confirm(`确定要${name}分班阅卷吗?`, "提示", {
  179. type: "warning",
  180. }).catch(() => {});
  181. if (confirm !== "confirm") {
  182. this.classMarkIsOpen = !this.classMarkIsOpen;
  183. return;
  184. }
  185. const res = await markClassStatusUpdate({
  186. examId: this.basicInfo.examId,
  187. paperNumber: this.basicInfo.paperNumber,
  188. classMark: this.classMarkIsOpen,
  189. }).catch(() => {
  190. this.classMarkIsOpen = !this.classMarkIsOpen;
  191. });
  192. if (!res) return;
  193. this.setOpenClassMark(this.classMarkIsOpen);
  194. if (this.classMarkIsOpen) {
  195. this.initData();
  196. } else {
  197. this.resetData();
  198. }
  199. },
  200. toSelectClass(row) {
  201. this.curRow = row;
  202. this.selectedClassIds = row.markerClassList;
  203. this.$refs.SelectClassByCourse.open();
  204. },
  205. classSelected(markerClassList) {
  206. this.curRow.markerClassList = markerClassList;
  207. this.updateUnsignData();
  208. },
  209. updateUnsignData() {
  210. this.unsignData = this.classList
  211. .map((className) => {
  212. const markIds = this.dataList
  213. .filter((row) => row.markerClassList.includes(className))
  214. .map((row) => row.marker.userId);
  215. const unsignGroups = this.subjectiveTaskList
  216. .filter(
  217. (group) =>
  218. !group.markers.some((marker) => markIds.includes(marker.userId))
  219. )
  220. .map((group) => {
  221. return {
  222. id: group.id,
  223. question: `${group.mainNumber}-${group.subNumber}`,
  224. };
  225. });
  226. const nrow = {
  227. className,
  228. unsignGroups,
  229. content: "",
  230. };
  231. if (unsignGroups.length) {
  232. const groupCont = unsignGroups
  233. .map((group) => group.question)
  234. .join("、");
  235. nrow.content = `${className}班级,${groupCont},未分配评卷员`;
  236. }
  237. return nrow;
  238. })
  239. .filter((item) => item.unsignGroups.length);
  240. },
  241. checkData() {
  242. if (!this.classMarkIsOpen) return true;
  243. if (this.unsignData.length) {
  244. this.$message.error("存在未分配班级,请完成分配!");
  245. return false;
  246. }
  247. return true;
  248. },
  249. async submit() {
  250. if (!this.checkData()) return;
  251. if (this.loading) return;
  252. this.loading = true;
  253. const unvalid = this.dataList.some(
  254. (item) => !item.markerClassList.length
  255. );
  256. if (unvalid) {
  257. this.$message.error("有评卷员未设置班级");
  258. return;
  259. }
  260. const res = await markClassSave({
  261. examId: this.basicInfo.examId,
  262. paperNumber: this.basicInfo.paperNumber,
  263. questionMarkerClass: this.dataList,
  264. }).catch(() => {});
  265. this.loading = false;
  266. if (!res) return;
  267. this.$message.success("保存成功!");
  268. this.cacheDataMd5 = this.getSubmitDataMd5();
  269. return true;
  270. },
  271. async toPrev(step = 1) {
  272. if (
  273. !this.classMarkIsOpen ||
  274. this.cacheDataMd5 === this.getSubmitDataMd5()
  275. ) {
  276. this.$emit("prev", step);
  277. return;
  278. }
  279. const confirm = await this.$confirm(
  280. `存在未保存的数据,是否保存后返回上一步?`,
  281. "提示",
  282. {
  283. type: "warning",
  284. }
  285. ).catch(() => {});
  286. if (confirm !== "confirm") {
  287. this.$emit("prev", step);
  288. return;
  289. }
  290. const res = await this.submit();
  291. if (!res) return;
  292. this.$emit("prev", step);
  293. },
  294. async toNext(step = 1) {
  295. if (!this.checkData()) return;
  296. if (
  297. !this.classMarkIsOpen ||
  298. this.cacheDataMd5 === this.getSubmitDataMd5()
  299. ) {
  300. this.$emit("next", step);
  301. return;
  302. }
  303. const res = await this.submit();
  304. if (!res) return;
  305. this.$emit("next", step);
  306. },
  307. },
  308. };
  309. </script>