PaperParams.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. <template>
  2. <el-dialog
  3. class="paper-params"
  4. :visible.sync="modalIsShow"
  5. top="10px"
  6. width="785px"
  7. :close-on-click-modal="false"
  8. :close-on-press-escape="false"
  9. append-to-body
  10. @open="dialogOpen"
  11. >
  12. <h1 class="params-dialog-title" slot="title">
  13. 请上传阅卷参数
  14. <span
  15. >卷面总分合计:<em class="param-sum-score">{{ pageSumScore }}分</em>
  16. </span>
  17. </h1>
  18. <div class="params-main">
  19. <div class="params-head">
  20. <el-button
  21. :type="!topicType ? 'primary' : 'default'"
  22. @click="selectType(0)"
  23. >客观题区</el-button
  24. >
  25. <el-button
  26. :type="topicType ? 'primary' : 'default'"
  27. @click="selectType(1)"
  28. >主观题区</el-button
  29. >
  30. </div>
  31. <div class="params-body">
  32. <div style="text-align: right">
  33. <upload-button
  34. btn-icon="el-icon-upload"
  35. btn-content="选择上传主观题标答文件(doc/docx)"
  36. btn-type="warning"
  37. :upload-url="uploadUrl"
  38. :format="['doc', 'docx']"
  39. @valid-error="validError"
  40. @upload-success="uploadSuccess"
  41. style="margin: 0"
  42. v-if="topicType"
  43. >
  44. </upload-button>
  45. </div>
  46. <div class="params-part" v-for="topic in curTopicList" :key="topic.id">
  47. <h2 class="params-title">{{ topic.topicName }}</h2>
  48. <p class="params-subtitle">
  49. <span>题型:{{ topic.name }}</span>
  50. <span v-if="topic.type !== 'COMPOSITION'">
  51. 通配小题分值:
  52. <el-input-number
  53. v-model="topic.commonQuestionScore"
  54. size="small"
  55. :min="0.1"
  56. :max="200"
  57. :step="0.1"
  58. step-strictly
  59. :controls="false"
  60. @change="commonQuestionScoreChange(topic)"
  61. ></el-input-number
  62. >分,</span
  63. >
  64. <span
  65. >本大题总分合计:<em class="param-sum-score"
  66. >{{ topic.sumScore }}分</em
  67. ></span
  68. >
  69. </p>
  70. <div class="params-table" v-if="topic.type !== 'COMPOSITION'">
  71. <table class="table table-striped">
  72. <tr>
  73. <th>题号</th>
  74. <th>分值</th>
  75. <th>题号</th>
  76. <th>分值</th>
  77. </tr>
  78. <tr v-for="(group, gindex) in topic.questions" :key="gindex">
  79. <template v-for="question in group">
  80. <td :key="`${question.questionNo}-1`">
  81. <span>{{ question.questionNo }}</span>
  82. </td>
  83. <td :key="`${question.questionNo}-2`">
  84. <el-input-number
  85. v-model="question.score"
  86. size="small"
  87. :min="0.1"
  88. :max="200"
  89. :step="0.1"
  90. step-strictly
  91. :controls="false"
  92. @change="questionScoreChange(topic)"
  93. v-if="question.questionNo"
  94. ></el-input-number>
  95. </td>
  96. </template>
  97. </tr>
  98. </table>
  99. </div>
  100. <div class="params-table" v-else>
  101. <el-input-number
  102. v-model="topic.sumScore"
  103. :min="0.1"
  104. :max="200"
  105. :step="0.1"
  106. step-strictly
  107. :controls="false"
  108. @change="getPageSumScore"
  109. ></el-input-number>
  110. </div>
  111. </div>
  112. </div>
  113. </div>
  114. <div slot="footer" style="text-align: right">
  115. <el-button type="primary" @click="submit">确认</el-button>
  116. <el-button type="danger" @click="cancel" plain>取消</el-button>
  117. </div>
  118. </el-dialog>
  119. </template>
  120. <script>
  121. import { calcSum, isEmptyObject, objAssign } from "../plugins/utils";
  122. import UploadButton from "./UploadButton";
  123. export default {
  124. name: "paper-params",
  125. components: { UploadButton },
  126. props: {
  127. pages: {
  128. type: Array,
  129. default() {
  130. return [];
  131. },
  132. },
  133. paperParams: {
  134. type: Object,
  135. default() {
  136. return {};
  137. },
  138. },
  139. },
  140. data() {
  141. return {
  142. modalIsShow: false,
  143. topicType: 0,
  144. pageSumScore: 0,
  145. subjectiveAttachmentId: "",
  146. curTopicList: [],
  147. objectives: [],
  148. subjectives: [],
  149. cacheScores: {},
  150. initTopic: {
  151. id: "",
  152. topicNo: "",
  153. topicName: "",
  154. type: "",
  155. },
  156. // import
  157. uploadUrl: "/api/print/basic/sys/saveAttachment",
  158. };
  159. },
  160. methods: {
  161. selectType(topicType) {
  162. this.topicType = topicType;
  163. this.curTopicList = topicType ? this.subjectives : this.objectives;
  164. },
  165. listIncludeItem(list, item) {
  166. const index = list.findIndex((elem) => elem.id === item.id);
  167. return index !== -1;
  168. },
  169. dialogOpen() {
  170. if (!isEmptyObject(this.paperParams)) {
  171. this.getCacheScore([
  172. ...this.paperParams.objectives,
  173. ...this.paperParams.subjectives,
  174. ]);
  175. this.subjectiveAttachmentId = this.paperParams.subjectiveAttachmentId;
  176. }
  177. // 每次打开时,重新生成试题结构,以便避免题卡结构变化之后,当前页面的试题结构没有变的问题
  178. let objectiveList = [];
  179. let subjectiveList = [];
  180. let objectives = [];
  181. let subjectives = [];
  182. this.pages.forEach((page) => {
  183. page.columns.forEach((column) => {
  184. column.elements.forEach((element) => {
  185. if (
  186. element.sign &&
  187. element.type !== "TOPIC_HEAD" &&
  188. element.type !== "CARD_HEAD"
  189. ) {
  190. if (element.sign === "objective") objectiveList.push(element);
  191. if (element.sign === "subjective") subjectiveList.push(element);
  192. }
  193. });
  194. });
  195. });
  196. // 客观题
  197. objectiveList.forEach((item) => {
  198. const topic = item.parent || item;
  199. if (this.listIncludeItem(objectives, topic)) return;
  200. let data = {
  201. sumScore: 0,
  202. commonQuestionScore: 1,
  203. name: this.getObjectiveTopicName(topic),
  204. choiceList: this.getChoiceList(topic),
  205. questions: this.getQuestions(topic),
  206. ...objAssign(this.initTopic, topic),
  207. };
  208. data.sumScore = this.getTopicSumScore(data);
  209. objectives.push(data);
  210. });
  211. // 主观题
  212. subjectiveList.forEach((item) => {
  213. const topic = item.parent || item;
  214. if (this.listIncludeItem(subjectives, topic)) return;
  215. let data = {
  216. sumScore: 0,
  217. commonQuestionScore: 1,
  218. name: this.getSubjectiveTopicName(topic),
  219. ...objAssign(this.initTopic, topic),
  220. };
  221. if (topic.type === "COMPOSITION") {
  222. data.sumScore = this.cacheScores[topic.topicNo] || 1;
  223. } else {
  224. data.questions = this.getQuestions(topic);
  225. data.sumScore = this.getTopicSumScore(data);
  226. }
  227. subjectives.push(data);
  228. });
  229. this.subjectives = subjectives;
  230. this.objectives = objectives;
  231. this.getPageSumScore();
  232. this.selectType(0);
  233. },
  234. getQuestions(topic) {
  235. let questions = [];
  236. let numPerColumn = Math.ceil(topic.questionsCount / 2);
  237. for (let j = topic.startNumber; j <= numPerColumn; j++) {
  238. let group = [];
  239. let question = {
  240. questionNo: j,
  241. score: this.cacheScores[`${topic.topicNo}-${j}`] || 1,
  242. };
  243. if (topic.type === "FILL_QUESTION")
  244. question.answers = topic.isMultiply ? [] : "";
  245. group[0] = question;
  246. if (numPerColumn + j <= topic.questionsCount) {
  247. group[1] = {
  248. questionNo: numPerColumn + j,
  249. score:
  250. this.cacheScores[`${topic.topicNo}-${numPerColumn + j}`] || 1,
  251. answers:
  252. topic.type === "FILL_QUESTION" && topic.isMultiply ? [] : "",
  253. };
  254. } else {
  255. group[1] = { questionNo: "" };
  256. }
  257. questions.push(group);
  258. }
  259. return questions;
  260. },
  261. getObjectiveTopicName(data) {
  262. if (data.isMultiply) {
  263. return "选择题(多选)";
  264. } else if (data.isBoolean) {
  265. return "填空题";
  266. } else {
  267. return "选择题(单选)";
  268. }
  269. },
  270. getSubjectiveTopicName(data) {
  271. const names = {
  272. EXPLAIN: "解答题",
  273. COMPOSITION: "作文题",
  274. FILL_LINE: "填空题",
  275. };
  276. return names[data.type];
  277. },
  278. getChoiceList(data) {
  279. const options = data.isBoolean
  280. ? data.booleanType
  281. : "abcdefghijklmnopqrstuv";
  282. return options.toUpperCase().slice(0, data.optionCount).split("");
  283. },
  284. commonQuestionScoreChange(topic) {
  285. topic.questions.map((group) => {
  286. group.map((question) => {
  287. question.score = topic.commonQuestionScore;
  288. });
  289. });
  290. this.questionScoreChange(topic);
  291. },
  292. questionScoreChange(topic) {
  293. topic.sumScore = this.getTopicSumScore(topic);
  294. this.getPageSumScore();
  295. },
  296. getTopicSumScore(topic) {
  297. const scoreList = topic.questions.map((group) => {
  298. return calcSum(group.map((question) => question.score || 0));
  299. });
  300. return calcSum(scoreList);
  301. },
  302. getPageSumScore() {
  303. this.pageSumScore =
  304. calcSum(this.objectives.map((item) => item.sumScore)) +
  305. calcSum(this.subjectives.map((item) => item.sumScore));
  306. },
  307. getCacheScore(topics) {
  308. topics = topics || [...this.objectives, ...this.subjectives];
  309. let cacheScores = {};
  310. topics.map((topic) => {
  311. if (topic.type === "COMPOSITION") {
  312. cacheScores[`${topic.topicNo}`] = topic.sumScore;
  313. } else {
  314. topic.questions.map((group) => {
  315. group.map((question) => {
  316. cacheScores[`${topic.topicNo}-${question.questionNo}`] =
  317. question.score;
  318. });
  319. });
  320. }
  321. });
  322. this.cacheScores = cacheScores;
  323. },
  324. // import
  325. validError(errorData) {
  326. this.$message.error(errorData.message);
  327. },
  328. uploadSuccess(data) {
  329. this.$message.success("上传成功!");
  330. this.subjectiveAttachmentId = data.id;
  331. },
  332. // dialog
  333. cancel() {
  334. this.modalIsShow = false;
  335. },
  336. open() {
  337. this.modalIsShow = true;
  338. },
  339. submit() {
  340. this.$emit("confirm", {
  341. pageSumScore: this.pageSumScore,
  342. objectives: this.objectives,
  343. subjectives: this.subjectives,
  344. subjectiveAttachmentId: this.subjectiveAttachmentId,
  345. });
  346. this.modalIsShow = false;
  347. },
  348. },
  349. };
  350. </script>