CardBuildDialog.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. <template>
  2. <el-dialog
  3. class="card-build-dialog opacity-dialog"
  4. :visible.sync="modalIsShow"
  5. :close-on-click-modal="false"
  6. :close-on-press-escape="false"
  7. :show-close="false"
  8. append-to-body
  9. fullscreen
  10. @opened="initData"
  11. >
  12. <div slot="title"></div>
  13. <div slot="footer"></div>
  14. <div
  15. class="loading-tips"
  16. v-loading="loading"
  17. element-loading-text="生成题卡中"
  18. ></div>
  19. <div
  20. v-if="modalIsShow"
  21. id="card-build"
  22. class="card-build card-preview card-print"
  23. >
  24. <card-view
  25. v-if="pages.length"
  26. ref="CardView"
  27. class="preview-body"
  28. :pages="pages"
  29. :card-config="cardConfig"
  30. ></card-view>
  31. <!-- all topics -->
  32. <div v-else class="topic-list">
  33. <div :class="['page-box', `page-box-${cardConfig.pageSize}`]">
  34. <div class="page-main-inner">
  35. <div
  36. :class="['page-main', `page-main-${cardConfig.columnNumber}`]"
  37. :style="{ margin: `0 -${cardConfig.columnGap / 2}px` }"
  38. >
  39. <div
  40. class="page-column"
  41. :style="{ padding: `0 ${cardConfig.columnGap / 2}px` }"
  42. >
  43. <div class="page-column-main" id="topic-column">
  44. <div class="page-column-body">
  45. <!-- card-head-sample -->
  46. <card-head-sample
  47. :data="cardHeadSampleData"
  48. id="simple-card-head"
  49. v-if="topics.length && cardHeadSampleData"
  50. ></card-head-sample>
  51. <!-- topic element -->
  52. <topic-element-preview
  53. class="page-column-element"
  54. v-for="element in topics"
  55. :key="element.id"
  56. :data="element"
  57. ></topic-element-preview>
  58. </div>
  59. </div>
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. </div>
  65. </div>
  66. </el-dialog>
  67. </template>
  68. <script>
  69. import json5 from "json5";
  70. import { mapState, mapMutations, mapActions } from "vuex";
  71. import { saveCard, cardConfigInfos } from "../api";
  72. import { tikuPaperDetail } from "../../exam/api";
  73. import { getPaperJsonSimpleStructInfo } from "../autoBuild/paperStruct";
  74. import { buildCardFromPaperSimpleStruct } from "../autoBuild/simplePaperCard";
  75. // card components
  76. import { getCardHeadModel } from "../../../../card/elementModel";
  77. import TopicElementPreview from "../../../../card/components/TopicElementPreview";
  78. import CardView from "../../../../card/components/CardView.vue";
  79. import CardHeadSample from "../../../../card/elements/card-head/CardHead";
  80. import { deepCopy, objTypeOf } from "@/plugins/utils";
  81. // ceshi
  82. // import paperData from "./paper.json";
  83. export default {
  84. name: "CardBuild",
  85. components: { CardView, TopicElementPreview, CardHeadSample },
  86. props: {
  87. presetData: {
  88. type: Object,
  89. default() {
  90. return {
  91. paperId: "",
  92. };
  93. },
  94. },
  95. },
  96. data() {
  97. return {
  98. cardId: "",
  99. loading: false,
  100. modalIsShow: false,
  101. paperInfo: {},
  102. answers: {},
  103. cachePages: [],
  104. sysCardSize: "",
  105. };
  106. },
  107. computed: {
  108. ...mapState("card", ["cardConfig", "topics", "pages"]),
  109. cardHeadSampleData() {
  110. if (!this.cardConfig["pageSize"]) return;
  111. const data = getCardHeadModel(this.cardConfig);
  112. data.isSimple = true;
  113. return data;
  114. },
  115. },
  116. methods: {
  117. ...mapMutations("card", [
  118. "setCardConfig",
  119. "setTopics",
  120. "setPages",
  121. "initState",
  122. ]),
  123. ...mapActions("card", ["resetTopicSeries", "rebuildPages"]),
  124. async initData() {
  125. this.loading = true;
  126. this.sysCardSize = this.$ls.get("schoolInfo", {
  127. cardSize: this.sysCardSize,
  128. }).cardSize;
  129. this.initState();
  130. // 题卡规则
  131. const cardConfig = await this.getCardConfig(
  132. this.presetData.cardRuleId
  133. ).catch(() => {});
  134. if (!cardConfig) {
  135. this.emitResult({ success: false, message: "题卡规则获取失败" });
  136. return;
  137. }
  138. this.setCardConfig(cardConfig);
  139. // 试卷信息
  140. const res = await tikuPaperDetail({
  141. examId: this.presetData.examId,
  142. paperId: this.presetData.paperId,
  143. uuid: this.presetData.uuid,
  144. }).catch(() => {});
  145. if (!res) {
  146. this.emitResult({ success: false, message: "试卷内容获取失败" });
  147. return;
  148. }
  149. // const res = paperData;
  150. // 构建题卡
  151. try {
  152. this.paperInfo = {
  153. uuid: this.presetData.uuid,
  154. attachmentId: res.attachmentId,
  155. paperUrl: res.paperUrl,
  156. };
  157. const answerJson = res.answerJson
  158. ? json5.parse(res.answerJson)
  159. : { details: [] };
  160. const paperJson = res.paperJson
  161. ? json5.parse(res.paperJson)
  162. : { details: [] };
  163. this.rebuildPaperQuestionNumber(answerJson);
  164. this.rebuildPaperQuestionNumber(paperJson);
  165. this.parsePaperAnswers(paperJson, answerJson);
  166. const paperSimpleStruct = getPaperJsonSimpleStructInfo(paperJson);
  167. const elementTypePreSetInfo = {
  168. FILL_QUESTION: {
  169. pageSize: this.sysCardSize,
  170. columnNumber: 2,
  171. },
  172. };
  173. const elements = buildCardFromPaperSimpleStruct(
  174. paperSimpleStruct,
  175. elementTypePreSetInfo
  176. );
  177. this.setTopics([getCardHeadModel(this.cardConfig), ...elements]);
  178. this.resetTopicSeries();
  179. } catch (error) {
  180. console.dir(error);
  181. this.emitResult({
  182. success: false,
  183. message: error.message || "数据构建错误",
  184. });
  185. return;
  186. }
  187. this.$nextTick(() => {
  188. this.rebuildPages();
  189. this.cachePages = deepCopy(this.pages);
  190. console.log(this.cachePages);
  191. this.updatePageField();
  192. this.$nextTick(() => {
  193. this.buildData();
  194. });
  195. });
  196. },
  197. rebuildPaperQuestionNumber(paperJson) {
  198. // 如果一个大题有多个套题,那么套题的小题需要将强制转成为小题号连续
  199. paperJson.details.forEach((detail) => {
  200. let qno = 1;
  201. detail.questions.forEach((question) => {
  202. if (question.subQuestions && question.subQuestions.length) {
  203. question.subQuestions.forEach((subq) => {
  204. subq.number = qno++;
  205. });
  206. } else {
  207. question.number = qno++;
  208. }
  209. });
  210. });
  211. },
  212. getObjectiveQuestionAnswer(answer) {
  213. if (objTypeOf(answer) === "boolean") {
  214. return answer ? "A" : "B";
  215. }
  216. if (objTypeOf(answer) === "array") {
  217. const abc = "abcdefghijklmnopqrstuvwxyz".toUpperCase();
  218. return answer.map((item) => abc[item - 1]).join();
  219. }
  220. },
  221. parsePaperAnswers(paperJson, answerJson) {
  222. const infos = {};
  223. const objectiveQuestionTypes = [1, 2, 3];
  224. paperJson.details.forEach((detail) => {
  225. detail.questions.forEach((question) => {
  226. if (question.subQuestions && question.subQuestions.length) {
  227. question.subQuestions.forEach((subq) => {
  228. const qno = `${detail.number}-${subq.number}`;
  229. if (!infos[qno]) {
  230. infos[qno] = {
  231. answer: null,
  232. score: null,
  233. objective: false,
  234. };
  235. }
  236. infos[qno].score = subq.score;
  237. infos[qno].objective = objectiveQuestionTypes.includes(
  238. subq.structType
  239. );
  240. });
  241. } else {
  242. const qno = `${detail.number}-${question.number}`;
  243. if (!infos[qno]) {
  244. infos[qno] = { answer: null, score: null, objective: false };
  245. }
  246. infos[qno].score = question.score;
  247. infos[qno].objective = objectiveQuestionTypes.includes(
  248. question.structType
  249. );
  250. }
  251. });
  252. });
  253. answerJson.details.forEach((detail) => {
  254. detail.questions.forEach((question) => {
  255. if (question.subQuestions && question.subQuestions.length) {
  256. question.subQuestions.forEach((subq) => {
  257. const qno = `${detail.number}-${subq.number}`;
  258. if (infos[qno].objective) {
  259. infos[qno].answer = this.getObjectiveQuestionAnswer(
  260. subq.answer
  261. );
  262. }
  263. });
  264. } else {
  265. const qno = `${detail.number}-${question.number}`;
  266. if (infos[qno].objective) {
  267. infos[qno].answer = this.getObjectiveQuestionAnswer(
  268. question.answer
  269. );
  270. }
  271. }
  272. });
  273. });
  274. this.answers = infos;
  275. },
  276. async getCardConfig(cardRuleId) {
  277. const data = await cardConfigInfos(cardRuleId);
  278. if (!data) {
  279. this.$message.error("找不到题卡规则!");
  280. return Promise.reject();
  281. }
  282. let config = {
  283. ...data,
  284. ...{
  285. pageSize: this.sysCardSize,
  286. columnNumber: 2,
  287. columnGap: 20,
  288. showForbidArea: false,
  289. makeMethod: "SELF",
  290. courseCodeBarcodeSrc: "${courseCodeBarcodeSrc}",
  291. courseCodeBarcodeName: "${courseCodeBarcodeName}",
  292. },
  293. };
  294. config.fillNumber = data.examNumberDigit;
  295. config.aOrB = false;
  296. config.requiredFields = JSON.parse(config.requiredFields);
  297. config.extendFields = JSON.parse(config.extendFields);
  298. config.cardTitle = this.getCardTitle(config.titleRule);
  299. return config;
  300. },
  301. getCardTitle(titleRule) {
  302. const fieldMap = {
  303. courseCode: this.presetData.courseCode,
  304. courseName: this.presetData.courseName,
  305. schoolName: this.presetData.schoolName,
  306. };
  307. Object.entries(fieldMap).forEach(([key, val]) => {
  308. titleRule = titleRule.replace("${" + key + "}", val);
  309. });
  310. return titleRule;
  311. },
  312. updatePageField() {
  313. const config = this.cardConfig;
  314. // 变量
  315. let fieldInfos = {};
  316. [...config.requiredFields, ...config.extendFields]
  317. .filter((item) => item.enable)
  318. .map((item) => {
  319. fieldInfos[item.code] = "${" + item.code + "}";
  320. });
  321. const isPrintExamNumber = config.examNumberStyle === "PRINT";
  322. if (isPrintExamNumber) {
  323. fieldInfos.examNumber = "data:image/png;base64,${examNumber}";
  324. fieldInfos.examNumberStr = "${examNumberStr}";
  325. }
  326. if (config.aOrB && config.paperType === "PRINT") {
  327. fieldInfos.paperType = "data:image/png;base64,${paperType}";
  328. fieldInfos.paperTypeName = "${paperTypeName}";
  329. }
  330. const pages = this.pages.map((page, pageNo) => {
  331. if (pageNo % 2) return page;
  332. const cardHeadElement = page.columns[0].elements[0];
  333. if (cardHeadElement.type === "CARD_HEAD") {
  334. cardHeadElement.fieldInfos = fieldInfos;
  335. }
  336. return page;
  337. });
  338. this.setPages(pages);
  339. },
  340. async buildData() {
  341. const model = this.$refs.CardView.getPageModel({
  342. cardConfig: this.cardConfig,
  343. pages: this.cachePages,
  344. answers: this.answers,
  345. });
  346. const htmlContent = this.$refs.CardView.getPreviewTemp(
  347. document.getElementById("card-build").outerHTML
  348. );
  349. const datas = {
  350. content: model,
  351. htmlContent,
  352. title: this.presetData.paperName,
  353. status: "SUBMIT",
  354. ...this.presetData,
  355. };
  356. const result = await saveCard(datas).catch((error) => {
  357. this.emitResult({
  358. success: false,
  359. message: error.message || "保存题卡错误",
  360. });
  361. });
  362. if (!result) return;
  363. this.emitResult({
  364. success: true,
  365. data: { ...result, ...this.paperInfo },
  366. });
  367. },
  368. emitResult(data) {
  369. // console.log(data);
  370. this.initState();
  371. this.loading = false;
  372. this.$emit("confirm", data);
  373. this.cancel();
  374. },
  375. cancel() {
  376. this.modalIsShow = false;
  377. },
  378. open() {
  379. this.modalIsShow = true;
  380. },
  381. },
  382. };
  383. </script>