PreviewPaperDialog.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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.subIndex !== null) {
  115. studentAnsMap[`${k}-${question.subIndex}`] = {
  116. answer: JSON.parse(question.answer),
  117. score: question.score,
  118. };
  119. } else {
  120. studentAnsMap[k] = {
  121. answer: JSON.parse(question.answer),
  122. score: question.score,
  123. };
  124. }
  125. });
  126. // detail
  127. paperStructJson.details.forEach((detail) => {
  128. detail.questions.forEach((question) => {
  129. let k = `${detail.number}-${question.number}`;
  130. if (question.subQuestions) {
  131. question.subQuestions.forEach((sq) => {
  132. const sqk = `${k}-${sq.number}`;
  133. sq.answer = answerMap[sqk];
  134. const stdAns = studentAnsMap[sqk] || {
  135. answer: null,
  136. score: null,
  137. };
  138. sq.studentAnswer = stdAns.answer;
  139. sq.studentScore = stdAns.score;
  140. });
  141. } else {
  142. question.answer = answerMap[k];
  143. const stdAns = studentAnsMap[k] || { answer: null, score: null };
  144. question.studentAnswer = stdAns.answer;
  145. question.studentScore = stdAns.score;
  146. }
  147. });
  148. });
  149. // console.log(JSON.stringify(paperStructJson));
  150. this.paperStruct = paperStructJson;
  151. },
  152. parseRenderStructList() {
  153. let renderStructList = [];
  154. renderStructList.push({
  155. cls: "paper-name",
  156. type: "text",
  157. content: this.paperStruct.name,
  158. });
  159. this.paperStruct.details.forEach((detail) => {
  160. renderStructList.push({
  161. cls: "detail-name",
  162. type: "text",
  163. content: `${numberToChinese(detail.number)}、${detail.name}`,
  164. });
  165. detail.questions.forEach((question) => {
  166. if (question.subQuestions) {
  167. if (question.body) {
  168. renderStructList.push({
  169. cls: "topic-title",
  170. type: "json",
  171. content: {
  172. isCommon: true,
  173. number: question.number,
  174. body: question.body,
  175. },
  176. });
  177. }
  178. question.subQuestions.forEach((sq) => {
  179. const contents = this.parseSimpleQuestion(sq, false);
  180. renderStructList.push(...contents);
  181. });
  182. } else {
  183. const contents = this.parseSimpleQuestion(question, true);
  184. renderStructList.push(...contents);
  185. }
  186. });
  187. });
  188. this.renderStructList = renderStructList.map((item) => {
  189. item.id = randomCode();
  190. return item;
  191. });
  192. // console.log(renderStructList);
  193. },
  194. parseSimpleQuestion(question, isCommon) {
  195. let contents = [];
  196. contents.push({
  197. cls: "topic-title",
  198. type: "json",
  199. content: {
  200. isSub: !isCommon,
  201. number: question.number,
  202. body: question.body,
  203. },
  204. });
  205. if (question.options && question.options.length) {
  206. question.options.forEach((op) => {
  207. contents.push({
  208. cls: "topic-option",
  209. type: "json",
  210. content: {
  211. isSub: false,
  212. number: numberToUpperCase(op.number),
  213. body: op.body,
  214. },
  215. });
  216. });
  217. }
  218. if (question.answer) {
  219. contents.push({
  220. cls: "topic-answer",
  221. type: "json",
  222. content: {
  223. answerType: "standard",
  224. structType: question.structType,
  225. body: question.answer,
  226. },
  227. });
  228. }
  229. if (question.studentAnswer) {
  230. // 简答题的特殊处理,解决上传的大图无法分页问题。文字内容不受影响。
  231. if (question.structType === STRUCT_TYPES.TEXT) {
  232. // console.log(question.studentAnswer);
  233. let aindex = 0;
  234. question.studentAnswer.forEach((answer) => {
  235. answer.sections.forEach((section) => {
  236. contents.push({
  237. cls: "topic-answer std-answer",
  238. type: "json",
  239. content: {
  240. answerType: "student",
  241. structType: question.structType,
  242. hideTitle: aindex !== 0,
  243. body: [
  244. {
  245. sections: [section],
  246. },
  247. ],
  248. },
  249. });
  250. aindex++;
  251. });
  252. });
  253. } else {
  254. // console.log(question.studentAnswer);
  255. contents.push({
  256. cls: "topic-answer std-answer",
  257. type: "json",
  258. content: {
  259. answerType: "student",
  260. structType: question.structType,
  261. body: question.studentAnswer,
  262. },
  263. });
  264. }
  265. }
  266. contents.push({
  267. cls: "topic-score",
  268. type: "text",
  269. content: "得分:" + (question.studentScore || 0),
  270. });
  271. return contents;
  272. },
  273. loadImg(url) {
  274. return new Promise((resolve, reject) => {
  275. const img = new Image();
  276. img.onload = function () {
  277. resolve(true);
  278. };
  279. img.onerror = function () {
  280. reject();
  281. };
  282. img.src = url;
  283. });
  284. },
  285. getRichJsonImgUrls(richJson) {
  286. let urls = [];
  287. richJson.sections.forEach((section) => {
  288. section.blocks.forEach((elem) => {
  289. if (elem.type === "image") {
  290. urls.push(elem.value);
  291. }
  292. });
  293. });
  294. return urls;
  295. },
  296. async waitAllImgLoaded() {
  297. let imgUrls = [];
  298. this.renderStructList.forEach((item) => {
  299. if (item.type === "text") return;
  300. if (item.cls === "topic-title" || item.cls === "topic-option") {
  301. imgUrls.push(...this.getRichJsonImgUrls(item.content.body));
  302. return;
  303. }
  304. // 简答题才可能会有图片
  305. if (item.cls.indexOf("answer") && item.content.structType === 5) {
  306. item.content.body.forEach((body) => {
  307. imgUrls.push(...this.getRichJsonImgUrls(body));
  308. });
  309. }
  310. });
  311. // console.log(imgUrls);
  312. if (!imgUrls.length) return Promise.resolve(true);
  313. const imgLoads = imgUrls.map((item) => this.loadImg(item));
  314. const imgLoadResult = await Promise.all(imgLoads).catch(() => {});
  315. if (imgLoadResult && imgLoadResult.length) {
  316. return Promise.resolve(true);
  317. } else {
  318. return Promise.reject();
  319. }
  320. },
  321. buildRenderGroupStructList() {
  322. const pageMaxHeight = 1042;
  323. let curGroup = [],
  324. curGroupHeight = 0;
  325. let renderGroupStructList = [];
  326. this.renderStructList.forEach((item) => {
  327. const itemHeight = document.getElementById(item.id).clientHeight;
  328. if (curGroupHeight + itemHeight > pageMaxHeight) {
  329. if (curGroup.length) renderGroupStructList.push(curGroup);
  330. curGroup = [];
  331. curGroupHeight = 0;
  332. }
  333. curGroup.push(item);
  334. curGroupHeight += itemHeight;
  335. });
  336. if (curGroup.length) renderGroupStructList.push(curGroup);
  337. this.renderGroupStructList = renderGroupStructList;
  338. this.renderStructList = [];
  339. },
  340. getPreviewHtml() {
  341. const html = previewTem(this.$refs.PaperBody.innerHTML);
  342. console.log(html);
  343. },
  344. },
  345. };
  346. </script>
  347. <style src="@/styles/paper-preview.css"></style>