MarkPaperStructure.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <template>
  2. <div class="mark-paper-structure">
  3. <p class="structure-desc">
  4. <span>课程名称:</span>
  5. <span class="mr-4">{{ datas.basicPaperInfo.courseName }}</span>
  6. <span>课程代码:</span>
  7. <span>{{ datas.basicPaperInfo.courseCode }}</span>
  8. </p>
  9. <el-table
  10. ref="TableList"
  11. :data="tableData"
  12. border
  13. :row-class-name="getRowClassName"
  14. >
  15. <el-table-column width="50" align="center">
  16. <template slot-scope="scope" v-if="scope.row.mainFirstSub">
  17. <div
  18. :class="[
  19. 'expand-btn',
  20. { 'expand-btn-unexpand': !scope.row.expandSub },
  21. ]"
  22. @click="switchExpandSub(scope.row)"
  23. >
  24. <i
  25. :class="scope.row.expandSub ? 'el-icon-minus' : 'el-icon-plus'"
  26. ></i>
  27. </div>
  28. </template>
  29. </el-table-column>
  30. <el-table-column prop="mainTitle" label="大题名称">
  31. <span slot-scope="scope" v-if="scope.row.mainFirstSub">
  32. {{ scope.row.mainTitle }}
  33. </span>
  34. </el-table-column>
  35. <el-table-column prop="typeName" label="题型" width="160">
  36. <template slot-scope="scope" v-if="scope.row.mainFirstSub">
  37. {{ scope.row.typeName }}
  38. </template>
  39. </el-table-column>
  40. <el-table-column label="每题分值" width="160">
  41. <template slot-scope="scope" v-if="scope.row.mainFirstSub">
  42. <el-input-number
  43. v-model="scoresPerTopic[scope.row.mainId]"
  44. class="width-80"
  45. size="small"
  46. :min="0.5"
  47. :max="500"
  48. :step="0.5"
  49. step-strictly
  50. :controls="false"
  51. @change="(val) => scorePerTopicChange(val, scope.row)"
  52. ></el-input-number>
  53. </template>
  54. </el-table-column>
  55. <el-table-column prop="mainNumber" label="大题号" width="80">
  56. <template slot-scope="scope" v-if="scope.row.mainFirstSub">
  57. <span>{{ scope.row.mainNumber }}</span>
  58. </template>
  59. </el-table-column>
  60. <el-table-column
  61. prop="subNumber"
  62. label="小题号"
  63. width="80"
  64. ></el-table-column>
  65. <el-table-column prop="totalScore" label="小题满分" width="105">
  66. <template slot-scope="scope">
  67. <el-input-number
  68. v-model="scope.row.totalScore"
  69. class="width-80"
  70. size="small"
  71. :min="0.5"
  72. :max="500"
  73. :step="0.5"
  74. step-strictly
  75. :controls="false"
  76. ></el-input-number>
  77. </template>
  78. </el-table-column>
  79. </el-table>
  80. <div class="total-info">
  81. 试卷总分:<span>{{ paperTotalScore }}</span
  82. >分
  83. </div>
  84. </div>
  85. </template>
  86. <script>
  87. import { calcSum } from "@/plugins/utils";
  88. export default {
  89. name: "mark-paper-structure",
  90. props: {
  91. datas: {
  92. type: Object,
  93. default() {
  94. return {
  95. basicPaperInfo: {},
  96. paperStructureInfo: [],
  97. groupInfo: [],
  98. };
  99. },
  100. },
  101. },
  102. data() {
  103. return {
  104. tableData: [],
  105. Q_TYPE: {
  106. objective: "客观题",
  107. subjective: "主观题",
  108. },
  109. scoresPerTopic: {},
  110. };
  111. },
  112. computed: {
  113. paperTotalScore() {
  114. return calcSum(this.tableData.map((item) => item.totalScore || 0));
  115. },
  116. },
  117. mounted() {
  118. this.initData();
  119. },
  120. methods: {
  121. initData() {
  122. let curMainId = null;
  123. let scoresPerTopic = {};
  124. this.tableData = this.datas.paperStructureInfo.map((item) => {
  125. if (curMainId !== item.mainId) {
  126. curMainId = item.mainId;
  127. scoresPerTopic[curMainId] = undefined;
  128. }
  129. return { ...item };
  130. });
  131. this.scoresPerTopic = scoresPerTopic;
  132. this.$emit("on-ready");
  133. },
  134. createMain() {
  135. this.tableData.push({
  136. id: this.$randomCode(),
  137. qType: "objective",
  138. mainId: this.$randomCode(),
  139. mainTitle: "",
  140. mainNumber: 1,
  141. subNumber: 1,
  142. totalScore: undefined,
  143. mainFirstSub: true,
  144. expandSub: true,
  145. });
  146. },
  147. getRowClassName({ row }) {
  148. let classNames = [];
  149. if (row.mainFirstSub) {
  150. classNames.push("row-main-first-sub");
  151. }
  152. if (!row.mainFirstSub && !row.expandSub) {
  153. classNames.push("row-unexpand-sub");
  154. }
  155. return classNames.join(" ");
  156. },
  157. getNextMainStartPos(startPos, curMainId) {
  158. let nextMainStartPos = null;
  159. for (let i = startPos, len = this.tableData.length; i < len; i++) {
  160. const element = this.tableData[i];
  161. if (element.mainId !== curMainId) {
  162. nextMainStartPos = i;
  163. return nextMainStartPos;
  164. }
  165. }
  166. if (nextMainStartPos === null) return this.tableData.length;
  167. },
  168. toAddMain(row) {
  169. const startPos = this.tableData.findIndex((item) => item.id === row.id);
  170. let nextMainStartPos = this.getNextMainStartPos(startPos, row.mainId);
  171. this.tableData.splice(nextMainStartPos, 0, {
  172. id: this.$randomCode(),
  173. qType: row.qType,
  174. mainId: this.$randomCode(),
  175. mainTitle: "",
  176. mainNumber: row.mainNumber + 1,
  177. subNumber: 1,
  178. totalScore: row.totalScore,
  179. mainFirstSub: true,
  180. expandSub: true,
  181. });
  182. this.updateMainData();
  183. },
  184. updateMainData() {
  185. let curMainNumber = 0,
  186. curMainId = null;
  187. this.tableData.forEach((item) => {
  188. if (item.mainId !== curMainId) {
  189. curMainId = item.mainId;
  190. curMainNumber++;
  191. }
  192. item.mainNumber = curMainNumber;
  193. });
  194. },
  195. toAddSub(row) {
  196. const subPos = this.tableData.findIndex((item) => item.id === row.id);
  197. this.tableData.splice(subPos + 1, 0, {
  198. id: this.$randomCode(),
  199. qType: row.qType,
  200. mainId: row.mainId,
  201. mainTitle: row.mainTitle,
  202. mainNumber: row.mainNumber,
  203. subNumber: row.subNumber + 1,
  204. totalScore: row.totalScore,
  205. mainFirstSub: false,
  206. expandSub: row.expandSub,
  207. });
  208. this.updateSubData(row.mainId);
  209. },
  210. updateSubData(mainId) {
  211. this.tableData
  212. .filter((item) => item.mainId === mainId)
  213. .forEach((item, index) => {
  214. item.subNumber = index + 1;
  215. });
  216. },
  217. toDeleteSub(row) {
  218. const subPos = this.tableData.findIndex((item) => item.id === row.id);
  219. this.tableData.splice(subPos, 1);
  220. this.tableData
  221. .filter((item) => item.mainId === row.mainId)
  222. .forEach((item, index) => {
  223. item.mainFirstSub = !index;
  224. });
  225. this.updateSubData(row.mainId);
  226. this.updateMainData();
  227. },
  228. switchExpandSub(row) {
  229. row.expandSub = !row.expandSub;
  230. this.tableData
  231. .filter((item) => item.mainId === row.mainId && !item.mainFirstSub)
  232. .forEach((item) => (item.expandSub = row.expandSub));
  233. },
  234. mainTitleChange(row) {
  235. this.tableData
  236. .filter((item) => item.mainId === row.mainId && !item.mainFirstSub)
  237. .forEach((item) => (item.mainTitle = row.mainTitle));
  238. },
  239. qTypeChange(row) {
  240. this.tableData
  241. .filter((item) => item.mainId === row.mainId && !item.mainFirstSub)
  242. .forEach((item) => (item.qType = row.qType));
  243. },
  244. scorePerTopicChange(val, row) {
  245. if (!val) return;
  246. this.tableData
  247. .filter((item) => item.mainId === row.mainId)
  248. .forEach((item) => (item.totalScore = val));
  249. },
  250. checkData() {
  251. // if (
  252. // this.tableData.some(item => item.qType === "objective") &&
  253. // this.tableData[0].qType !== "objective"
  254. // ) {
  255. // this.$message.error("请保持客观题在前,主观题在后!");
  256. // return;
  257. // }
  258. // const objectiveNos = this.tableData
  259. // .filter(item => item.mainFirstSub && item.qType === "objective")
  260. // .map(item => item.mainNumber);
  261. // const unValid = objectiveNos.some(
  262. // (no, index) => objectiveNos[0] + index !== no
  263. // );
  264. // if (unValid) {
  265. // this.$message.error("请保持主客观题题号连续");
  266. // return;
  267. // }
  268. let errorMessages = [];
  269. this.tableData.forEach((item) => {
  270. let errorMsg = ``;
  271. if (item.mainFirstSub) {
  272. let errorFields = [];
  273. if (!item.mainTitle) {
  274. errorFields.push("大题名称");
  275. }
  276. if (!item.mainNumber) {
  277. errorFields.push("大题号");
  278. }
  279. if (errorFields.length) {
  280. errorMsg += `${errorFields.join("、")}不能为空,`;
  281. }
  282. }
  283. let errorFields = [];
  284. if (!item.subNumber) {
  285. errorFields.push("小题号");
  286. }
  287. if (!item.totalScore) {
  288. errorFields.push("小题满分");
  289. }
  290. if (errorFields.length) {
  291. errorMsg += `第${item.subNumber}小题,${errorFields.join(
  292. "、"
  293. )}不能为空,`;
  294. }
  295. if (errorMsg) {
  296. errorMsg = `第${item.mainNumber}大题,${errorMsg}`;
  297. errorMessages.push(errorMsg);
  298. }
  299. });
  300. if (errorMessages.length) {
  301. this.$message.error(errorMessages.join("。"));
  302. return;
  303. }
  304. this.updateData();
  305. this.$emit("next-step");
  306. },
  307. getData() {
  308. return this.tableData.map((item) => {
  309. return { ...item };
  310. });
  311. },
  312. updateData() {
  313. this.$emit("data-change", {
  314. paperStructureInfo: this.getData(),
  315. });
  316. },
  317. },
  318. };
  319. </script>