PaperTemplateBuild.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. <template>
  2. <div class="paper-template-build">
  3. <div class="paper-template-build-body">
  4. <div class="margin_top_10">
  5. <el-select
  6. v-model="seqMode"
  7. class="margin-right-10"
  8. @change="seqModeChange"
  9. >
  10. <el-option value="MODE1" label="单题型连续"></el-option>
  11. <el-option value="MODE2" label="客观题整体连续"></el-option>
  12. <el-option value="MODE3" label="按大题独立"></el-option>
  13. <el-option value="MODE5" label="整卷连续"></el-option>
  14. </el-select>
  15. <el-select
  16. v-model="curPaperTemp"
  17. class="margin-right-10"
  18. placeholder="请选择"
  19. value-key="id"
  20. @change="paperTempChange"
  21. >
  22. <el-option
  23. v-for="item in paperTempList"
  24. :key="item.id"
  25. :label="item.name"
  26. :value="item"
  27. >
  28. </el-option>
  29. </el-select>
  30. <el-button type="primary" @click="toDownload">下载试卷</el-button>
  31. </div>
  32. <paper-template-view
  33. ref="PaperTemplateView"
  34. class="preview-body"
  35. :pages="pages"
  36. :page-config="paperTempJson.pageConfig"
  37. ></paper-template-view>
  38. </div>
  39. </div>
  40. </template>
  41. <script>
  42. import PaperTemplateView from "../components/PaperTemplateView.vue";
  43. import { getModel as getRichTextModel } from "../elements/rich-text/model";
  44. import { getModel as getPageModel } from "../elements/page/model";
  45. import { getElementId, randomCode, deepCopy } from "../../card/plugins/utils";
  46. import { calcSum, maxNum } from "@/plugins/utils";
  47. import previewTemp from "../previewTemp";
  48. import { paperDetailInfoApi } from "../../paper/api";
  49. import { paperTemplateListApi, paperPdfDownloadApi } from "../api";
  50. import { downloadByApi } from "@/plugins/download";
  51. // import paperJson from "./data/paper.json";
  52. // import paperTempJson from "./data/paper-temp.json";
  53. const numberToUpperCase = function (val) {
  54. if (val < 1 || val > 26) return;
  55. return String.fromCharCode(64 + val);
  56. };
  57. const checkRichTextHasCont = function (data) {
  58. if (!data) return false;
  59. if (!data.sections || !data.sections.length) return false;
  60. if (!data.sections[0].blocks || !data.sections[0].blocks.length) return false;
  61. return true;
  62. };
  63. export default {
  64. name: "PaperTemplateBuild",
  65. components: { PaperTemplateView },
  66. data() {
  67. return {
  68. paperId: this.$route.params.paperId,
  69. viewType: this.$route.params.viewType,
  70. seqMode: "MODE1",
  71. renderStructList: [],
  72. pages: [],
  73. paperJson: {},
  74. paperTempJson: {
  75. pages: [],
  76. pageConfig: {},
  77. },
  78. maxColumnWidth: 200,
  79. maxColumnHeight: 200,
  80. paperTempList: [],
  81. curPaperTemp: {},
  82. downloading: false,
  83. fieldData: {},
  84. paperStructs: [],
  85. };
  86. },
  87. mounted() {
  88. if (this.viewType === "frame") {
  89. this.initFrame();
  90. return;
  91. }
  92. this.initData();
  93. },
  94. methods: {
  95. async initFrame() {
  96. try {
  97. const paperSet = window.parent.paperSet;
  98. if (!paperSet) {
  99. this.emitFrameResult(false, "数据缺失");
  100. return;
  101. }
  102. this.seqMode = paperSet.seqMode;
  103. this.curPaperTemp = paperSet.paperTemp;
  104. await this.getPaperJson();
  105. let paperTempJson = this.curPaperTemp.content
  106. ? JSON.parse(this.curPaperTemp.content)
  107. : { pages: [], pageConfig: {} };
  108. this.paperTempJson = paperTempJson;
  109. this.pages = paperTempJson.pages;
  110. this.updaterFieldInfo();
  111. } catch (error) {
  112. this.emitFrameResult(false, "数据错误");
  113. }
  114. this.$nextTick(async () => {
  115. try {
  116. this.maxColumnWidth =
  117. document.getElementById("column-0-0").offsetWidth;
  118. this.maxColumnHeight =
  119. document.getElementById("column-0-0").offsetHeight;
  120. this.parseRenderStructList();
  121. this.buildPrePages();
  122. } catch (error) {
  123. this.emitFrameResult(false, "构建错误");
  124. }
  125. const loadRes = await this.waitAllImgLoaded().catch(() => {});
  126. if (!loadRes) {
  127. this.emitFrameResult(false, "数据缓存错误");
  128. return;
  129. }
  130. this.$nextTick(() => {
  131. try {
  132. this.buildReleasePages();
  133. } catch (error) {
  134. this.emitFrameResult(false, "构建pdf错误");
  135. }
  136. this.$nextTick(() => {
  137. this.emitFrameResult(true, "", this.getPreviewTemp());
  138. });
  139. });
  140. });
  141. },
  142. emitFrameResult(success = true, errorMsg = "", htmlCont = "") {
  143. window.parent &&
  144. window.parent.submitPaperTemp &&
  145. window.parent.submitPaperTemp({
  146. success,
  147. errorMsg,
  148. htmlCont,
  149. templateId: this.curPaperTemp.id,
  150. });
  151. },
  152. async initData() {
  153. await this.getPaperJson();
  154. await this.getPaperTempList();
  155. if (!this.paperTempList.length) {
  156. this.$message.error("导出模板缺失!");
  157. return;
  158. }
  159. this.paperTempChange(this.paperTempList[0]);
  160. // test--->
  161. // this.paperJson = paperJson;
  162. // this.paperTempJson = paperTempJson;
  163. // this.pages = paperTempJson.pages;
  164. // this.$nextTick(() => {
  165. // this.buildData();
  166. // });
  167. },
  168. async getPaperJson() {
  169. const res = await paperDetailInfoApi({
  170. paperId: this.paperId,
  171. seqMode: this.seqMode,
  172. });
  173. this.paperJson = res.data;
  174. this.resetClozeSerialNo(this.paperJson);
  175. this.fieldData = {
  176. paperName: res.data.name,
  177. courseName: `${res.data.course.name}(${res.data.course.code})`,
  178. totalScore: res.data.totalScore,
  179. rootOrgName: res.data.rootOrgName,
  180. };
  181. this.paperStructs = this.paperJson.paperDetails.map((detail) => {
  182. return {
  183. detailName: detail.name,
  184. questionCount: detail.unitCount,
  185. totalScore: detail.score,
  186. };
  187. });
  188. },
  189. resetClozeSerialNo(paperData) {
  190. const clozeQuestionTypes = ["CLOZE", "BANKED_CLOZE"];
  191. paperData.paperDetails.forEach((detail) => {
  192. detail.paperDetailUnits.forEach((question) => {
  193. if (!clozeQuestionTypes.includes(question.questionType)) return;
  194. question.question.quesBody.sections.forEach((section) => {
  195. section.blocks.forEach((block) => {
  196. if (block.type !== "cloze") return;
  197. block.value =
  198. question.question.subQuestions[block.value - 1].questionSeq;
  199. });
  200. });
  201. });
  202. });
  203. },
  204. async seqModeChange() {
  205. await this.getPaperJson();
  206. this.$nextTick(() => {
  207. this.buildData();
  208. });
  209. },
  210. async getPaperTempList() {
  211. const res = await paperTemplateListApi("PAPER_EXPORT");
  212. this.paperTempList = res.data;
  213. },
  214. paperTempChange(paperTemp) {
  215. // console.log(paperTemp);
  216. this.curPaperTemp = paperTemp;
  217. let paperTempJson = paperTemp.content
  218. ? JSON.parse(paperTemp.content)
  219. : { pages: [], pageConfig: {} };
  220. this.paperTempJson = paperTempJson;
  221. this.pages = paperTempJson.pages;
  222. this.updaterFieldInfo();
  223. this.$nextTick(() => {
  224. this.buildData();
  225. });
  226. },
  227. updaterFieldInfo() {
  228. const VALID_ELEMENTS_FOR_EXTERNAL = ["FIELD_TEXT"];
  229. this.paperTempJson.pages.forEach((page) => {
  230. page.columns.forEach((column) => {
  231. column.elements.forEach((elem) => {
  232. if (elem.type === "PAPER_STRUCT") {
  233. elem.structs = this.paperStructs;
  234. }
  235. if (!elem.elements || !elem.elements.length) return;
  236. elem.elements.forEach((element) => {
  237. if (!VALID_ELEMENTS_FOR_EXTERNAL.includes(element.type)) return;
  238. if (element.type === "FIELD_TEXT" && element.field) {
  239. element.content = this.fieldData[element.field];
  240. }
  241. });
  242. });
  243. });
  244. });
  245. },
  246. async buildData() {
  247. this.maxColumnWidth = document.getElementById("column-0-0").offsetWidth;
  248. this.maxColumnHeight = document.getElementById("column-0-0").offsetHeight;
  249. this.parseRenderStructList();
  250. this.buildPrePages();
  251. const loadRes = await this.waitAllImgLoaded().catch(() => {});
  252. if (!loadRes) {
  253. this.$message.error("图片加载有误!");
  254. return;
  255. }
  256. this.$nextTick(() => {
  257. this.buildReleasePages();
  258. });
  259. },
  260. parseRenderStructList() {
  261. let renderStructList = [];
  262. this.paperJson.paperDetails.forEach((detail) => {
  263. renderStructList.push(this.parseDetailTitle(detail));
  264. if (checkRichTextHasCont(detail.description)) {
  265. const descData = this.parseTitleOption(detail.description, "");
  266. renderStructList.push(...descData);
  267. }
  268. detail.paperDetailUnits.forEach((question) => {
  269. let questionInfo = question.question;
  270. if (questionInfo.subQuestions && questionInfo.subQuestions.length) {
  271. const bodys = this.parseTitleOption(questionInfo.quesBody, "");
  272. renderStructList.push(...bodys);
  273. const isMatches = this.checkIsMatches(questionInfo.questionType);
  274. if (
  275. isMatches &&
  276. questionInfo.quesOptions &&
  277. questionInfo.quesOptions.length
  278. ) {
  279. questionInfo.quesOptions.forEach((op) => {
  280. const obodys = this.parseTitleOption(
  281. op.optionBody,
  282. `${numberToUpperCase(op.number)}、`,
  283. "option"
  284. );
  285. renderStructList.push(...obodys);
  286. });
  287. }
  288. // 选词填空不展示小题
  289. if (questionInfo.questionType === "BANKED_CLOZE") {
  290. renderStructList.push(this.parseLineGap());
  291. return;
  292. }
  293. questionInfo.subQuestions.forEach((sq, sqindex) => {
  294. sq.subNumber = sqindex + 1;
  295. if (isMatches) sq.quesOptions = []; // 选词填空、段落匹配小题中不展示选项
  296. const contents = this.parseSimpleQuestion(sq, false);
  297. renderStructList.push(...contents);
  298. renderStructList.push(this.parseLineGap());
  299. });
  300. } else {
  301. questionInfo.number = question.number;
  302. const datas = this.parseSimpleQuestion(questionInfo, true);
  303. renderStructList.push(...datas);
  304. renderStructList.push(this.parseLineGap());
  305. }
  306. });
  307. });
  308. // 去掉最后一题的间隔行
  309. // console.log(renderStructList);
  310. this.renderStructList = renderStructList.slice(0, -1);
  311. },
  312. getRichStruct(blocks) {
  313. return {
  314. sections: [
  315. {
  316. blocks: [...blocks],
  317. },
  318. ],
  319. };
  320. },
  321. transformRichJson(richJson) {
  322. if (!richJson || !richJson.sections) return [];
  323. let contents = [];
  324. let curBlock = [];
  325. const checkNeedSplitSection = (block) => {
  326. if (block.type !== "image") return false;
  327. if (block.param) {
  328. if (block.param.width)
  329. return block.param.width > this.maxColumnWidth / 2;
  330. if (block.param.height) return block.param.height > 150;
  331. }
  332. return true;
  333. };
  334. richJson.sections.forEach((section) => {
  335. section.blocks.forEach((block) => {
  336. if (checkNeedSplitSection(block) && curBlock.length) {
  337. contents.push(this.getRichStruct(curBlock));
  338. curBlock = [];
  339. }
  340. curBlock.push(block);
  341. });
  342. if (curBlock.length) {
  343. contents.push(this.getRichStruct(curBlock));
  344. curBlock = [];
  345. }
  346. });
  347. return contents;
  348. },
  349. changeRichTextCloze(richText) {
  350. return {
  351. sections: richText.sections.map((section) => {
  352. return {
  353. blocks: section.blocks.map((item) => {
  354. if (item.type === "cloze") {
  355. return {
  356. type: "text",
  357. value: "_____",
  358. };
  359. } else {
  360. return { ...item };
  361. }
  362. }),
  363. };
  364. }),
  365. };
  366. },
  367. parseSimpleQuestion(question) {
  368. let contents = [];
  369. let quesBody = question.quesBody;
  370. if (question.questionType === "FILL_BLANK_QUESTION") {
  371. quesBody = this.changeRichTextCloze(quesBody);
  372. }
  373. const tbodys = this.parseTitleOption(
  374. quesBody,
  375. `${question.questionSeq}、`
  376. );
  377. contents.push(...tbodys);
  378. const hasNobody = !tbodys.length;
  379. if (question.quesOptions && question.quesOptions.length) {
  380. question.quesOptions.forEach((op, oIndex) => {
  381. let noVal = `${numberToUpperCase(op.number)}、`;
  382. if (hasNobody && !oIndex) {
  383. // 针对如完形填空的小题做的特殊处理
  384. noVal = `${question.questionSeq}、${noVal}`;
  385. }
  386. const obodys = this.parseTitleOption(op.optionBody, noVal, "option");
  387. contents.push(...obodys);
  388. });
  389. }
  390. return contents;
  391. },
  392. parseDetailTitle(data) {
  393. let content = this.getRichStruct([
  394. {
  395. type: "text",
  396. value: `${data.cnNum}、${data.name}(共${data.unitCount}小题,满分${data.score}分)`,
  397. },
  398. ]);
  399. return getRichTextModel({
  400. styles: { width: "100%", fontWeight: 600 },
  401. content,
  402. });
  403. },
  404. parseTitleOption(richJson, noVal, contType = "content") {
  405. if (!richJson) return [];
  406. const bodys = this.transformRichJson(richJson);
  407. return bodys.map((body, index) => {
  408. if (index === 0 && noVal) {
  409. body.sections[0].blocks.unshift({
  410. type: "text",
  411. value: noVal,
  412. });
  413. }
  414. return getRichTextModel({
  415. contType,
  416. styles: { width: contType !== "option" ? "100%" : "auto" },
  417. content: body,
  418. });
  419. });
  420. },
  421. parseLineGap() {
  422. return getRichTextModel({
  423. contType: "gap",
  424. styles: { width: "100%", height: "10px" },
  425. content: this.getRichStruct([{ type: "text", value: "" }]),
  426. });
  427. },
  428. checkIsMatches(structType) {
  429. const matchesTypes = ["BANKED_CLOZE", "PARAGRAPH_MATCHING"];
  430. return matchesTypes.includes(structType);
  431. },
  432. buildPrePages() {
  433. let pages = deepCopy(this.paperTempJson.pages);
  434. const firstPageNo = pages.findIndex((p) => p.pageType !== "cover");
  435. pages[firstPageNo].columns[0].texts = [];
  436. pages[firstPageNo].columns[0].texts.push(...this.renderStructList);
  437. this.pages = pages;
  438. },
  439. buildReleasePages() {
  440. this.resetRenderStructSize();
  441. // console.log(this.renderStructList);
  442. this.buildPageAutoPage();
  443. },
  444. resetRenderStructSize() {
  445. let curOptions = [];
  446. this.renderStructList.forEach((elem, eindex) => {
  447. const elemDom = document.getElementById(`rich-text-${elem.id}`);
  448. elem.w = elemDom.offsetWidth;
  449. elem.h = elemDom.offsetHeight;
  450. if (elem.contType !== "option") return;
  451. curOptions.push(elem);
  452. // 全选选项逻辑
  453. const nextElem = this.renderStructList[eindex + 1];
  454. if (nextElem && nextElem.contType === "option") return;
  455. curOptions.forEach((optionElem) => {
  456. optionElem._percent = this.getSizePercent(
  457. optionElem.w,
  458. this.maxColumnWidth
  459. );
  460. });
  461. const optionCount = curOptions.length;
  462. // 奇数选项,全部一行
  463. if (optionCount % 2 > 0) {
  464. curOptions.forEach((optionElem) => {
  465. optionElem._percent = 1;
  466. optionElem.styles.width = "100%";
  467. });
  468. curOptions = [];
  469. return;
  470. }
  471. const percents = curOptions.map((item) => item._percent);
  472. const maxPercent = maxNum(percents);
  473. let aveOptionPercent = 1;
  474. if (optionCount % 4 === 0) {
  475. aveOptionPercent = this.calcAveOptionPercent(maxPercent);
  476. } else {
  477. aveOptionPercent = maxPercent > 0.5 ? 1 : 0.5;
  478. }
  479. curOptions.forEach((optionElem) => {
  480. optionElem._percent = aveOptionPercent;
  481. optionElem.styles.width = aveOptionPercent * 100 + "%";
  482. });
  483. curOptions = [];
  484. });
  485. this.renderStructList.forEach((elem) => {
  486. if (elem.styles.width === "100%") {
  487. this.$set(elem.styles, "display", "block");
  488. }
  489. });
  490. },
  491. buildPageAutoPage() {
  492. let pages = [];
  493. let curPage = null,
  494. curElem = null;
  495. let curColumn = null,
  496. curColumnNo = 0,
  497. curColumnHeight = 0;
  498. let curLinePercent = 0;
  499. let groups = [],
  500. curGroup = [];
  501. // 分组自动分页 选项分组
  502. const getNextElem = () => {
  503. return this.renderStructList.shift();
  504. };
  505. curElem = getNextElem();
  506. while (curElem) {
  507. if (
  508. curElem.contType !== "option" ||
  509. (curElem.contType === "option" && curElem._percent === 1)
  510. ) {
  511. if (curGroup.length) {
  512. groups.push(curGroup);
  513. curGroup = [];
  514. curLinePercent = 0;
  515. }
  516. groups.push([curElem]);
  517. curElem = getNextElem();
  518. } else {
  519. if (curLinePercent + curElem._percent > 1) {
  520. groups.push(curGroup);
  521. curGroup = [];
  522. curLinePercent = 0;
  523. } else {
  524. curGroup.push(curElem);
  525. curLinePercent += curElem._percent;
  526. curElem = getNextElem();
  527. }
  528. }
  529. }
  530. if (curGroup.length) {
  531. groups.push(curGroup);
  532. curGroup = [];
  533. }
  534. const getNextGroup = () => {
  535. return groups.shift();
  536. };
  537. curGroup = getNextGroup();
  538. while (curGroup) {
  539. if (!curPage) {
  540. curPage = this.getNewPageModel(pages.length);
  541. }
  542. if (!curColumn) {
  543. curColumn = curPage.columns[curColumnNo++];
  544. curColumnHeight = this.calcInitColumnHeight(curColumn);
  545. }
  546. let curGroupHeigth =
  547. groups.length === 1
  548. ? curGroup[0].h
  549. : Math.max.apply(
  550. null,
  551. curGroup.map((item) => item.h)
  552. );
  553. if (curGroupHeigth + curColumnHeight > this.maxColumnHeight) {
  554. // 当前栏满了
  555. if (curColumnNo >= curPage.columnNumber) {
  556. // 当前页满了
  557. pages.push(curPage);
  558. curPage = null;
  559. curColumnNo = null;
  560. }
  561. curColumn = null;
  562. curColumnHeight = 0;
  563. } else {
  564. // 当前栏未满
  565. curColumnHeight += curGroupHeigth;
  566. curColumn.texts.push(...curGroup);
  567. curGroup = getNextGroup();
  568. }
  569. }
  570. if (curPage) {
  571. pages.push(curPage);
  572. curPage = null;
  573. }
  574. // 正文部分保证偶数页
  575. if (pages.length % 2) {
  576. pages.push(this.getNewPageModel(pages.length));
  577. }
  578. if (this.paperTempJson.pageConfig.showCover) {
  579. // 封面自动插入反面空白页
  580. let coverPages = deepCopy(
  581. this.paperTempJson.pages.filter((p) => p.pageType === "cover")
  582. );
  583. let coverBackPage = deepCopy(coverPages[0]);
  584. coverBackPage.columns.forEach((column) => {
  585. column.elements = [];
  586. });
  587. let nCoverPages = [];
  588. coverPages.forEach((cpage) => {
  589. nCoverPages.push(cpage);
  590. nCoverPages.push(coverBackPage);
  591. });
  592. pages = [...nCoverPages, ...pages];
  593. }
  594. this.pages = pages;
  595. },
  596. getNewPageModel(pageNo) {
  597. let contentPages = this.paperTempJson.pages.slice(-2);
  598. let pNo = pageNo % 2;
  599. const pageTemp = contentPages[pNo];
  600. let newPage = getPageModel({
  601. ...this.paperTempJson.pageConfig,
  602. pageType: pageTemp.pageType,
  603. });
  604. newPage.sides = pageTemp.sides.map((elem) => {
  605. let nelem = deepCopy(elem);
  606. nelem.id = getElementId();
  607. nelem.key = randomCode();
  608. if (pNo === 1 && nelem.type === "GUTTER") {
  609. nelem.direction = "right";
  610. }
  611. return nelem;
  612. });
  613. newPage.columns.forEach((column) => {
  614. column.texts = [];
  615. });
  616. if (pageNo > 1) return newPage;
  617. newPage.columns.forEach((column, cindex) => {
  618. column.elements = pageTemp.columns[cindex].elements.map((elem) => {
  619. let nelem = deepCopy(elem);
  620. nelem.id = getElementId();
  621. nelem.key = randomCode();
  622. nelem.h = this.getElementHeight(`preview-${elem.id}`);
  623. if (nelem.elements && nelem.elements.length) {
  624. nelem.elements.forEach((celem) => {
  625. celem.id = getElementId();
  626. celem.key = randomCode();
  627. });
  628. }
  629. return nelem;
  630. });
  631. column.texts = [];
  632. });
  633. return newPage;
  634. },
  635. getElementHeight(elementId) {
  636. const dom = document.getElementById(elementId);
  637. return dom ? dom.offsetHeight : 0;
  638. },
  639. calcAveOptionPercent(maxPercent) {
  640. if (maxPercent > 0.5) return 1;
  641. if (maxPercent > 0.25) return 0.5;
  642. return 0.25;
  643. },
  644. calcInitColumnHeight(column) {
  645. return calcSum(column.elements.map((item) => item.h));
  646. },
  647. getSizePercent(size, fullSize) {
  648. const rate = size / fullSize;
  649. if (rate <= 0.25) return 0.25;
  650. if (rate <= 0.5) return 0.5;
  651. return 1;
  652. },
  653. // img
  654. loadImg(url) {
  655. return new Promise((resolve, reject) => {
  656. const img = new Image();
  657. img.onload = function () {
  658. resolve(true);
  659. };
  660. img.onerror = function () {
  661. reject();
  662. };
  663. img.src = url;
  664. });
  665. },
  666. getRichJsonImgUrls(richJson) {
  667. let urls = [];
  668. if (!richJson) return urls;
  669. richJson.sections.forEach((section) => {
  670. section.blocks.forEach((elem) => {
  671. if (elem.type === "image" && elem.value.startsWith("http")) {
  672. urls.push(elem.value);
  673. }
  674. });
  675. });
  676. return urls;
  677. },
  678. async waitAllImgLoaded() {
  679. let imgUrls = [];
  680. this.renderStructList.forEach((item) => {
  681. if (item.contType === "gap") return;
  682. imgUrls.push(...this.getRichJsonImgUrls(item.content));
  683. });
  684. // console.log(imgUrls);
  685. if (!imgUrls.length) return Promise.resolve(true);
  686. const imgLoads = imgUrls.map((item) => this.loadImg(item));
  687. const imgLoadResult = await Promise.all(imgLoads).catch(() => {});
  688. if (imgLoadResult && imgLoadResult.length) {
  689. return Promise.resolve(true);
  690. } else {
  691. return Promise.reject();
  692. }
  693. },
  694. getPreviewTemp() {
  695. const elementDoms =
  696. this.$refs.PaperTemplateView.$el.querySelectorAll(".elem-rich-text");
  697. elementDoms.forEach((eDom) => {
  698. const width = eDom.offsetWidth;
  699. if (eDom.firstChild && eDom.firstChild.nodeName === "DIV") {
  700. eDom.firstChild.style.width = width + "px";
  701. if (
  702. eDom.firstChild.firstChild &&
  703. eDom.firstChild.firstChild.nodeName === "DIV"
  704. )
  705. eDom.firstChild.firstChild.style.width = width + "px";
  706. }
  707. });
  708. return previewTemp(this.$refs.PaperTemplateView.$el.outerHTML);
  709. },
  710. async toDownload() {
  711. const htmlCont = this.getPreviewTemp();
  712. if (this.downloading) return;
  713. this.downloading = true;
  714. const res = await downloadByApi(() => {
  715. return paperPdfDownloadApi({
  716. content: htmlCont,
  717. templateId: this.curPaperTemp.id,
  718. paperId: this.paperId,
  719. });
  720. }).catch((e) => {
  721. this.$message.error(e || "下载失败,请重新尝试!");
  722. });
  723. this.downloading = false;
  724. if (!res) return;
  725. this.$message.success("下载成功!");
  726. },
  727. },
  728. };
  729. </script>
  730. <style>
  731. .paper-template-build {
  732. text-align: center;
  733. }
  734. .paper-template-build-body {
  735. display: inline-block;
  736. text-align: initial;
  737. }
  738. .paper-template-build .page-box {
  739. margin-top: 10px;
  740. margin-bottom: 10px;
  741. }
  742. </style>