123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- <template>
- <div class="paper-template-build">
- <div class="paper-template-build-body">
- <div class="margin_top_10">
- <el-select
- v-model="seqMode"
- class="margin-right-10"
- size="small"
- @change="seqModeChange"
- >
- <!-- <el-option value="MODE1" label="单题型连续"></el-option>
- <el-option value="MODE2" label="客观题整体连续"></el-option> -->
- <el-option value="MODE3" label="按大题独立"></el-option>
- <el-option value="MODE5" label="整卷连续"></el-option>
- </el-select>
- <el-select
- v-model="curPaperTemp"
- class="margin-right-10"
- placeholder="请选择"
- value-key="id"
- size="small"
- @change="paperTempChange"
- >
- <el-option
- v-for="item in paperTempList"
- :key="item.id"
- :label="item.name"
- :value="item"
- >
- </el-option>
- </el-select>
- <el-button
- type="primary"
- size="small"
- :loading="downloading"
- @click="toAction('downloadPdf')"
- >下载试卷</el-button
- >
- <el-button
- type="primary"
- size="small"
- :loading="downloading"
- @click="toAction('buildPackge')"
- >生成数据包</el-button
- >
- </div>
- <paper-build-config
- ref="PaperBuildConfig"
- :config-sources="configSources"
- :showConfirmBtn="false"
- ></paper-build-config>
- <paper-template-view
- ref="PaperTemplateView"
- id="paper-template-view"
- class="preview-body"
- :pages="pages"
- :page-config="paperTempJson.pageConfig"
- >
- <template #texts="{ texts }">
- <div
- v-for="groupItem in texts"
- :key="groupItem.id"
- :id="groupItem.id"
- :class="[
- 'group-item',
- { 'is-detail-title': groupItem.type === 'detail-title' },
- {
- 'is-detail-description':
- groupItem.type === 'detail-description',
- },
- ]"
- :style="groupItem.styles"
- >
- <table
- v-if="
- configModalForm.showDetailScoreTable &&
- groupItem.type === 'detail-title'
- "
- class="detail-score-table"
- >
- <tr>
- <th>得分</th>
- <th>评分人</th>
- </tr>
- <tr>
- <td></td>
- <td></td>
- </tr>
- </table>
- <!-- 内容 -->
- <template v-if="groupItem.elements && groupItem.elements.length">
- <elem-rich-text
- v-for="elem in groupItem.elements"
- :id="`rich-text-${elem.id}`"
- :key="elem.id"
- :data="elem"
- ></elem-rich-text>
- </template>
- </div>
- </template>
- </paper-template-view>
- <!-- process dom -->
- <div v-if="elementList.length" class="element-list">
- <elem-rich-text
- v-for="elem in elementList"
- :id="elem.id"
- :key="elem.id"
- :data="elem"
- ></elem-rich-text>
- </div>
- <!-- answer dom -->
- <answer-template-view
- ref="AnswerTemplateView"
- id="answer-template-view"
- class="preview-body"
- :answerData="paperJson"
- :pageCountMode="configModalForm.pageCountMode"
- ></answer-template-view>
- </div>
- </div>
- </template>
- <script>
- import ElemRichText from "../elements/rich-text/ElemRichText.vue";
- import PaperTemplateView from "../components/PaperTemplateView.vue";
- import PaperBuildConfig from "../components/PaperBuildConfig.vue";
- import AnswerTemplateView from "../components/AnswerTemplateView.vue";
- import { deepCopy } from "../../card/plugins/utils";
- import { paperDetailInfoApi } from "../../paper/api";
- import { paperTemplateListApi, paperAndAnswerPdfDownloadApi } from "../api";
- import paperTemplateBuildMixins from "./paperTemplateBuildMixins";
- import { buildPdf } from "@/plugins/htmlToPdf";
- export default {
- name: "PaperTemplateBuild",
- components: {
- PaperTemplateView,
- PaperBuildConfig,
- ElemRichText,
- AnswerTemplateView,
- },
- mixins: [paperTemplateBuildMixins],
- data() {
- return {
- paperId: this.$route.params.paperId,
- viewType: this.$route.params.viewType,
- seqMode: "MODE3",
- paperJson: {},
- paperTempJson: {
- pages: [],
- pageConfig: {},
- },
- maxColumnWidth: 200,
- maxColumnHeight: 200,
- paperTempList: [],
- curPaperTemp: {},
- downloading: false,
- fieldData: {},
- paperStructs: [],
- configModalForm: {
- showDetailNo: true,
- showDetailScoreInfo: true,
- showDetailScoreTable: false,
- pageCountMode: "SIMPLE",
- },
- configSources: [],
- prepareDownloadPdf: false,
- actionType: "",
- };
- },
- mounted() {
- if (this.viewType === "frame") {
- this.initFrame();
- return;
- }
- this.initData();
- },
- methods: {
- async initFrame() {
- try {
- const paperSet = window.parent.paperSet;
- if (!paperSet) {
- this.emitFrameResult(false, "数据缺失");
- return;
- }
- this.seqMode = paperSet.seqMode;
- this.curPaperTemp = paperSet.paperTemp;
- this.configModalForm = paperSet.configModalForm;
- await this.getPaperJson();
- let paperTempJson = this.curPaperTemp.content
- ? JSON.parse(this.curPaperTemp.content)
- : { pages: [], pageConfig: {} };
- this.paperTempJson = paperTempJson;
- console.log("paperTempJson", paperTempJson);
- this.pages = paperTempJson.pages;
- this.updaterFieldInfo();
- } catch (error) {
- this.emitFrameResult(false, "数据错误");
- }
- this.$nextTick(async () => {
- try {
- this.maxColumnWidth =
- document.getElementById("column-0-0").offsetWidth;
- this.maxColumnHeight =
- document.getElementById("column-0-0").offsetHeight - 10;
- this.buildElementsFromStruct();
- } catch (error) {
- this.emitFrameResult(false, "构建元素错误");
- }
- const loadRes = await this.waitAllImgLoaded().catch(() => {});
- if (!loadRes) {
- this.emitFrameResult(false, "数据缓存错误");
- return;
- }
- this.$nextTick(() => {
- try {
- this.updateElementWidthInfo();
- this.buildGroupsFromStruct();
- this.buildPrePages();
- } catch (error) {
- this.emitFrameResult(false, "预构建页面错误");
- }
- this.$nextTick(() => {
- try {
- this.updateGroupHeightInfo();
- this.buildPagesByAutoPage();
- } catch (error) {
- this.emitFrameResult(false, "构建pdf错误");
- }
- this.$nextTick(async () => {
- const paperBlob = await buildPdf(
- {
- element: document.getElementById("paper-template-view"),
- pageSize: this.paperTempJson.pageConfig.pageSize,
- },
- true
- ).catch((error) => {
- console.error(error);
- });
- if (!paperBlob) {
- this.emitFrameResult(false, "生成试卷pdf错误");
- return;
- }
- this.emitFrameResult(true, "", paperBlob);
- });
- });
- });
- });
- },
- emitFrameResult(success = true, errorMsg = "", blobCont = null) {
- window.parent &&
- window.parent.submitPaperTemp &&
- window.parent.submitPaperTemp({
- success,
- errorMsg,
- blobCont,
- contType: "paper",
- });
- },
- async initData() {
- await this.getPaperJson();
- await this.getPaperTempList();
- if (!this.paperTempList.length) {
- this.$message.error("导出模板缺失!");
- return;
- }
- this.paperTempChange(this.paperTempList[0]);
- },
- async getPaperJson() {
- const res = await paperDetailInfoApi({
- paperId: this.paperId,
- seqMode: this.seqMode,
- });
- this.paperJson = res.data;
- this.resetClozeSerialNo(this.paperJson);
- this.fieldData = {
- paperName: res.data.name,
- courseName: res.data.course.name,
- courseCode: res.data.course.code,
- totalScore: res.data.totalScore,
- rootOrgName: res.data.rootOrgName,
- };
- this.paperStructs = this.paperJson.paperDetails.map((detail) => {
- return {
- detailName: detail.name,
- questionCount: detail.unitCount,
- totalScore: detail.score,
- };
- });
- },
- resetClozeSerialNo(paperData) {
- const clozeQuestionTypes = ["CLOZE", "BANKED_CLOZE"];
- paperData.paperDetails.forEach((detail) => {
- detail.paperDetailUnits.forEach((question) => {
- if (!clozeQuestionTypes.includes(question.questionType)) return;
- question.question.quesBody.sections.forEach((section) => {
- section.blocks.forEach((block) => {
- if (block.type !== "cloze") return;
- block.value =
- question.question.subQuestions[block.value - 1].questionSeq;
- });
- });
- });
- });
- },
- async getPaperTempList() {
- const res = await paperTemplateListApi("PAPER_EXPORT");
- this.paperTempList = res.data;
- },
- getConfigSources() {
- const { pages } = this.paperTempJson;
- let sources = [],
- fieldAble = {};
- pages.forEach((page) => {
- page.columns.forEach((column) => {
- column.elements.forEach((element) => {
- if (element.type !== "PAPER_PROPS") return;
- if (!sources.length) {
- sources = deepCopy(element.props);
- }
- element.props.forEach((prop) => {
- fieldAble[prop.field] = fieldAble[prop.field] || prop.enable;
- });
- });
- });
- });
- sources.forEach((item) => {
- item.enable = fieldAble[item.field];
- });
- this.configSources = sources;
- },
- updaterFieldInfo() {
- const VALID_ELEMENTS_FOR_EXTERNAL = ["FIELD_TEXT"];
- this.paperTempJson.pages.forEach((page) => {
- if (page.pageNumberRule) {
- Object.keys(this.fieldData).forEach((field) => {
- page.pageNumberRule = page.pageNumberRule.replace(
- "${" + field + "}",
- this.fieldData[field]
- );
- });
- }
- page.columns.forEach((column) => {
- column.elements.forEach((elem) => {
- if (elem.type === "PAPER_STRUCT") {
- elem.structs = this.paperStructs;
- } else if (elem.type === "SCORE_TABLE") {
- elem.detailCount = this.paperStructs.length;
- } else if (elem.type === "PAPER_PROPS") {
- elem.props.forEach((prop) => {
- prop.value = this.configModalForm[prop.field];
- });
- }
- if (!elem.elements || !elem.elements.length) return;
- elem.elements.forEach((element) => {
- if (!VALID_ELEMENTS_FOR_EXTERNAL.includes(element.type)) return;
- if (element.type === "FIELD_TEXT" && element.field) {
- element.content = this.fieldData[element.field];
- }
- });
- });
- });
- });
- },
- async seqModeChange() {
- await this.getPaperJson();
- this.$nextTick(() => {
- this.buildData();
- });
- },
- buildConfigChange(val) {
- this.configModalForm = val;
- this.updaterFieldInfo();
- this.$nextTick(() => {
- this.buildData();
- });
- },
- paperTempChange(paperTemp) {
- // console.log(paperTemp);
- this.curPaperTemp = paperTemp;
- let paperTempJson = paperTemp.content
- ? JSON.parse(paperTemp.content)
- : { pages: [], pageConfig: {} };
- this.paperTempJson = paperTempJson;
- this.pages = paperTempJson.pages;
- this.getConfigSources();
- this.updaterFieldInfo();
- this.$nextTick(() => {
- this.buildData();
- });
- },
- async buildData() {
- this.maxColumnWidth = document.getElementById("column-0-0").offsetWidth;
- this.maxColumnHeight =
- document.getElementById("column-0-0").offsetHeight - 10;
- this.buildElementsFromStruct();
- const loadRes = await this.waitAllImgLoaded().catch(() => {});
- if (!loadRes) {
- this.$message.error("图片加载有误!");
- return;
- }
- this.$nextTick(() => {
- this.updateElementWidthInfo();
- this.buildGroupsFromStruct();
- this.buildPrePages();
- this.$nextTick(() => {
- this.updateGroupHeightInfo();
- this.buildPagesByAutoPage();
- if (this.prepareDownloadPdf) {
- this.$nextTick(async () => {
- await this.runAction().catch(() => {});
- this.prepareDownloadPdf = false;
- });
- }
- });
- });
- },
- // img ------ start >
- loadImg(url) {
- return new Promise((resolve, reject) => {
- const img = new Image();
- img.onload = function () {
- resolve(true);
- };
- img.onerror = function () {
- reject();
- };
- img.src = url;
- });
- },
- getRichJsonImgUrls(richJson) {
- let urls = [];
- if (!richJson) return urls;
- richJson.sections.forEach((section) => {
- section.blocks.forEach((elem) => {
- if (elem.type === "image" && elem.value.startsWith("http")) {
- urls.push(elem.value);
- }
- });
- });
- return urls;
- },
- async waitAllImgLoaded() {
- let imgUrls = [];
- this.elementList.forEach((item) => {
- imgUrls.push(...this.getRichJsonImgUrls(item.content));
- });
- // console.log(imgUrls);
- if (!imgUrls.length) return Promise.resolve(true);
- const imgLoads = imgUrls.map((item) => this.loadImg(item));
- const imgLoadResult = await Promise.all(imgLoads).catch(() => {});
- if (imgLoadResult && imgLoadResult.length) {
- return Promise.resolve(true);
- } else {
- return Promise.reject();
- }
- },
- // img ------ end >
- // download ------ start >
- async toAction(actionType) {
- const valid = await this.$refs.PaperBuildConfig.checkData().catch(
- () => {}
- );
- if (!valid) return;
- this.actionType = actionType;
- const configData = this.$refs.PaperBuildConfig.getData();
- if (JSON.stringify(configData) === JSON.stringify(this.configModalForm)) {
- this.runAction();
- } else {
- this.prepareDownloadPdf = true;
- this.buildConfigChange(configData);
- }
- },
- async runAction() {
- if (this.actionType === "downloadPdf") {
- await this.downloadPaperPdf();
- } else {
- await this.buildPackage();
- }
- },
- async downloadPaperPdf() {
- if (this.downloading) return;
- this.downloading = true;
- let result = true;
- await buildPdf({
- element: document.getElementById("paper-template-view"),
- filename: `${this.fieldData.courseName}(${this.fieldData.courseCode})_${this.fieldData.paperName}.pdf`,
- pageSize: this.paperTempJson.pageConfig.pageSize,
- }).catch((error) => {
- result = false;
- console.error(error);
- this.$message.error(error.message || "下载失败,请重新尝试!");
- });
- this.downloading = false;
- if (!result) return;
- this.$message.success("下载成功!");
- },
- async buildPackage() {
- if (this.downloading) return;
- this.downloading = true;
- // 试卷PDF
- const paperPdfName = `${this.fieldData.courseName}(${this.fieldData.courseCode})_${this.fieldData.paperName}.pdf`;
- const paperBlob = await buildPdf(
- {
- element: document.getElementById("paper-template-view"),
- pageSize: this.paperTempJson.pageConfig.pageSize,
- },
- true
- ).catch((error) => {
- this.downloading = false;
- console.error(error);
- this.$message.error("生成试卷pdf失败,请重新尝试!");
- });
- if (!paperBlob) return;
- // 答案pdf
- const answerPdfName = `${this.fieldData.courseName}(${this.fieldData.courseCode})_${this.fieldData.paperName}_答案.pdf`;
- const answerBlob = await buildPdf(
- {
- element: document.getElementById("answer-template-view"),
- pageSize: "A4",
- },
- true
- ).catch((error) => {
- this.downloading = false;
- console.error(error);
- this.$message.error("生成答案pdf失败,请重新尝试!");
- });
- if (!answerBlob) return;
- const data = new FormData();
- data.append(
- "paperPdf",
- new File([paperBlob], paperPdfName, { type: "application/pdf" })
- );
- data.append(
- "answerPdf",
- new File([answerBlob], answerPdfName, { type: "application/pdf" })
- );
- data.append("paperId", this.paperId);
- let result = true;
- await paperAndAnswerPdfDownloadApi(data).catch((error) => {
- result = false;
- console.error(error);
- });
- this.downloading = false;
- if (!result) return;
- this.$message.success("生成数据包成功!");
- },
- },
- };
- </script>
- <style>
- .paper-template-build {
- text-align: center;
- }
- .paper-template-build-body {
- display: inline-block;
- text-align: initial;
- }
- .paper-template-build .page-box {
- margin-top: 10px;
- margin-bottom: 10px;
- }
- .paper-template-build .paper-build-config {
- padding: 10px 15px 2px;
- margin-top: 5px;
- background: #fff;
- border-radius: 10px;
- }
- .paper-build-config .el-form-item {
- margin-bottom: 16px;
- margin-right: 30px;
- }
- .paper-template-build .element-list {
- visibility: hidden;
- position: absolute;
- width: 1200px;
- height: 600px;
- overflow: hidden;
- left: -9999px;
- top: 0;
- z-index: 1;
- }
- .paper-template-build .answer-template-view {
- position: absolute;
- left: -9999px;
- z-index: 999;
- visibility: hidden;
- }
- </style>
|