PreviewPaperDialog.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. <template>
  2. <el-dialog
  3. class="preview-paper-dialog"
  4. :visible.sync="visible"
  5. title="查看原卷"
  6. top="0"
  7. :close-on-click-modal="false"
  8. :close-on-press-escape="false"
  9. append-to-body
  10. fullscreen
  11. destroy-on-close
  12. @open="visibleChange"
  13. >
  14. <div ref="PaperBody" class="paper-body">
  15. <div
  16. v-if="renderStructList.length"
  17. class="paper-content paper-content-long"
  18. >
  19. <cont-item
  20. v-for="(item, index) in renderStructList"
  21. :key="index"
  22. :data="item"
  23. ></cont-item>
  24. </div>
  25. <div
  26. v-for="(group, gindex) in renderGroupStructList"
  27. :key="gindex"
  28. class="paper-content"
  29. >
  30. <cont-item
  31. v-for="(item, index) in group"
  32. :key="index"
  33. :data="item"
  34. ></cont-item>
  35. </div>
  36. </div>
  37. <div slot="footer"></div>
  38. </el-dialog>
  39. </template>
  40. <script>
  41. import { studentExamDetailInfo } from "@/api/examwork-student";
  42. import { numberToChinese, numberToUpperCase } from "./spins/renderJSON";
  43. import ContItem from "./spins/ContItem.vue";
  44. import previewTem from "./spins/previewTem";
  45. import { randomCode } from "@/utils/utils";
  46. import { STRUCT_TYPES } from "./spins/paperSetting";
  47. export default {
  48. name: "preview-paper-dialog",
  49. components: { ContItem },
  50. props: {
  51. examRecordId: {
  52. type: String,
  53. },
  54. },
  55. data() {
  56. return {
  57. visible: false,
  58. paperStruct: {},
  59. renderStructList: [],
  60. renderGroupStructList: [],
  61. };
  62. },
  63. methods: {
  64. visibleChange() {
  65. this.paperStruct = {};
  66. this.renderStructList = [];
  67. this.renderGroupStructList = [];
  68. this.getRecordDetail();
  69. },
  70. openDialog() {
  71. this.visible = true;
  72. },
  73. closeDialog() {
  74. this.visible = false;
  75. },
  76. async getRecordDetail() {
  77. const res = await studentExamDetailInfo(this.examRecordId);
  78. this.parsePaperStruct(res.data.data);
  79. this.parseRenderStructList();
  80. const loadres = await this.waitAllImgLoaded().catch(() => {});
  81. if (!loadres) {
  82. this.$message.error("图片加载有误!");
  83. return;
  84. }
  85. this.$nextTick(() => {
  86. this.buildRenderGroupStructList();
  87. });
  88. },
  89. parsePaperStruct(data) {
  90. const paperStructJson = JSON.parse(data.paperStructJson);
  91. const answerJson = JSON.parse(data.answerJson);
  92. const examStudentAnswerJson = data.examStudentAnswerJson
  93. ? JSON.parse(data.examStudentAnswerJson)
  94. : [];
  95. // console.log(paperStructJson);
  96. // console.log(answerJson);
  97. // console.log(examStudentAnswerJson);
  98. const answerMap = {};
  99. answerJson.details.forEach((detail) => {
  100. detail.questions.forEach((question) => {
  101. const k = `${detail.number}-${question.number}`;
  102. if (question.subQuestions) {
  103. question.subQuestions.forEach((sq) => {
  104. answerMap[`${k}-${sq.number}`] = sq.answer;
  105. });
  106. } else {
  107. answerMap[k] = question.answer;
  108. }
  109. });
  110. });
  111. const studentAnsMap = {};
  112. examStudentAnswerJson.forEach((question) => {
  113. const k = `${question.mainNumber}-${question.subNumber}`;
  114. if (question.subQuestions) {
  115. question.subQuestions.forEach((sq) => {
  116. studentAnsMap[`${k}-${sq.number}`] = {
  117. answer: JSON.parse(sq.answer),
  118. score: sq.score,
  119. };
  120. });
  121. } else {
  122. studentAnsMap[k] = {
  123. answer: JSON.parse(question.answer),
  124. score: question.score,
  125. };
  126. }
  127. });
  128. // detail
  129. paperStructJson.details.forEach((detail) => {
  130. detail.questions.forEach((question) => {
  131. let k = `${detail.number}-${question.number}`;
  132. if (question.subQuestions) {
  133. question.subQuestions.forEach((sq) => {
  134. k += `-${sq.number}`;
  135. sq.answer = answerMap[k];
  136. const stdAns = studentAnsMap[k] || { answer: null, score: null };
  137. sq.studentAnswer = stdAns.answer;
  138. sq.studentScore = stdAns.score;
  139. });
  140. } else {
  141. question.answer = answerMap[k];
  142. const stdAns = studentAnsMap[k] || { answer: null, score: null };
  143. question.studentAnswer = stdAns.answer;
  144. question.studentScore = stdAns.score;
  145. }
  146. });
  147. });
  148. // console.log(JSON.stringify(paperStructJson));
  149. this.paperStruct = paperStructJson;
  150. },
  151. parseRenderStructList() {
  152. let renderStructList = [];
  153. renderStructList.push({
  154. cls: "paper-name",
  155. type: "text",
  156. content: this.paperStruct.name,
  157. });
  158. this.paperStruct.details.forEach((detail) => {
  159. renderStructList.push({
  160. cls: "detail-name",
  161. type: "text",
  162. content: `${numberToChinese(detail.number)}、${detail.name}`,
  163. });
  164. detail.questions.forEach((question) => {
  165. if (question.subQuestions) {
  166. if (question.body) {
  167. renderStructList.push({
  168. cls: "topic-title",
  169. type: "json",
  170. content: {
  171. isCommon: true,
  172. number: question.number,
  173. body: question.body,
  174. },
  175. });
  176. }
  177. question.subQuestions.forEach((sq) => {
  178. const contents = this.parseSimpleQuestion(sq, false);
  179. renderStructList.push(...contents);
  180. });
  181. } else {
  182. const contents = this.parseSimpleQuestion(question, true);
  183. renderStructList.push(...contents);
  184. }
  185. });
  186. });
  187. this.renderStructList = renderStructList.map((item) => {
  188. item.id = randomCode();
  189. return item;
  190. });
  191. // console.log(renderStructList);
  192. },
  193. parseSimpleQuestion(question, isCommon) {
  194. let contents = [];
  195. contents.push({
  196. cls: "topic-title",
  197. type: "json",
  198. content: {
  199. isSub: !isCommon,
  200. number: question.number,
  201. body: question.body,
  202. },
  203. });
  204. if (question.options && question.options.length) {
  205. question.options.forEach((op) => {
  206. contents.push({
  207. cls: "topic-option",
  208. type: "json",
  209. content: {
  210. isSub: false,
  211. number: numberToUpperCase(op.number),
  212. body: op.body,
  213. },
  214. });
  215. });
  216. }
  217. if (question.answer) {
  218. contents.push({
  219. cls: "topic-answer",
  220. type: "json",
  221. content: {
  222. answerType: "standard",
  223. structType: question.structType,
  224. body: question.answer,
  225. },
  226. });
  227. }
  228. if (question.studentAnswer) {
  229. // 简答题的特殊处理,解决上传的大图无法分页问题。文字内容不受影响。
  230. if (question.structType === STRUCT_TYPES.TEXT) {
  231. // console.log(question.studentAnswer);
  232. let aindex = 0;
  233. question.studentAnswer.forEach((answer) => {
  234. answer.sections.forEach((section) => {
  235. contents.push({
  236. cls: "topic-answer std-answer",
  237. type: "json",
  238. content: {
  239. answerType: "student",
  240. structType: question.structType,
  241. hideTitle: aindex !== 0,
  242. body: [
  243. {
  244. sections: [section],
  245. },
  246. ],
  247. },
  248. });
  249. aindex++;
  250. });
  251. });
  252. } else {
  253. contents.push({
  254. cls: "topic-answer std-answer",
  255. type: "json",
  256. content: {
  257. answerType: "student",
  258. structType: question.structType,
  259. body: question.studentAnswer,
  260. },
  261. });
  262. }
  263. }
  264. contents.push({
  265. cls: "topic-score",
  266. type: "text",
  267. content: "得分:" + (question.studentScore || 0),
  268. });
  269. return contents;
  270. },
  271. loadImg(url) {
  272. return new Promise((resolve, reject) => {
  273. const img = new Image();
  274. img.onload = function () {
  275. resolve(true);
  276. };
  277. img.onerror = function () {
  278. reject();
  279. };
  280. img.src = url;
  281. });
  282. },
  283. getRichJsonImgUrls(richJson) {
  284. let urls = [];
  285. richJson.sections.forEach((section) => {
  286. section.blocks.forEach((elem) => {
  287. if (elem.type === "image") {
  288. urls.push(elem.value);
  289. }
  290. });
  291. });
  292. return urls;
  293. },
  294. async waitAllImgLoaded() {
  295. let imgUrls = [];
  296. this.renderStructList.forEach((item) => {
  297. if (item.type === "text") return;
  298. if (item.cls === "topic-title" || item.cls === "topic-option") {
  299. imgUrls.push(...this.getRichJsonImgUrls(item.content.body));
  300. return;
  301. }
  302. // 简答题才可能会有图片
  303. if (item.cls.indexOf("answer") && item.content.structType === 5) {
  304. item.content.body.forEach((body) => {
  305. imgUrls.push(...this.getRichJsonImgUrls(body));
  306. });
  307. }
  308. });
  309. // console.log(imgUrls);
  310. if (!imgUrls.length) return Promise.resolve(true);
  311. const imgLoads = imgUrls.map((item) => this.loadImg(item));
  312. const imgLoadResult = await Promise.all(imgLoads).catch(() => {});
  313. if (imgLoadResult && imgLoadResult.length) {
  314. return Promise.resolve(true);
  315. } else {
  316. return Promise.reject();
  317. }
  318. },
  319. buildRenderGroupStructList() {
  320. const pageMaxHeight = 1042;
  321. let curGroup = [],
  322. curGroupHeight = 0;
  323. let renderGroupStructList = [];
  324. this.renderStructList.forEach((item) => {
  325. const itemHeight = document.getElementById(item.id).clientHeight;
  326. if (curGroupHeight + itemHeight > pageMaxHeight) {
  327. if (curGroup.length) renderGroupStructList.push(curGroup);
  328. curGroup = [];
  329. curGroupHeight = 0;
  330. }
  331. curGroup.push(item);
  332. curGroupHeight += itemHeight;
  333. });
  334. if (curGroup.length) renderGroupStructList.push(curGroup);
  335. this.renderGroupStructList = renderGroupStructList;
  336. this.renderStructList = [];
  337. },
  338. getPreviewHtml() {
  339. const html = previewTem(this.$refs.PaperBody.innerHTML);
  340. console.log(html);
  341. },
  342. },
  343. };
  344. </script>
  345. <style src="@/styles/paper-preview.css"></style>