<template> <div class="card-design"> <div class="design-header"> <div class="design-steps"> <div class="step-item" v-for="(step, index) in steps" :key="index"> <i>{{ index + 1 }}</i> <span>{{ step }}</span> </div> </div> </div> <!-- actions --> <div class="design-action"> <div class="design-logo"> <h1> <i class="el-icon-d-arrow-left" @click="toExit" title="退出"></i> 答题卡制作 </h1> </div> <div class="action-part"> <div class="action-part-title"><h2>基本设置</h2></div> <div class="action-part-body"> <page-prop-edit @init-page="initPageData"></page-prop-edit> </div> </div> <div class="action-part"> <div class="action-part-title"><h2>试题配置</h2></div> <div class="action-part-body"> <div class="type-list"> <div class="type-item" v-for="item in TOPIC_LIST" :key="item.type"> <el-button @click="addNewTopic(item)" ><i class="el-icon-plus"></i>{{ item.name }}</el-button > </div> <div class="type-item" v-for="item in NOT_TOPIC_LIST" :key="item.type" > <el-button @click="addNewTopic(item)" ><i class="el-icon-plus"></i>{{ item.name }}</el-button > </div> </div> <p class="tips-info">提示:点击创建试题</p> </div> </div> <div class="action-part"> <div class="action-part-title"><h2>插入元素</h2></div> <div class="action-part-body"> <div class="type-list"> <div class="type-item" v-for="(item, index) in ELEMENT_LIST" :key="index" draggable="true" @dragstart="dragstart(item)" > <el-button><i class="el-icon-plus"></i>{{ item.name }}</el-button> </div> <p class="tips-info">提示:拖动插入元素</p> </div> <!-- Develop btns --> <!-- <card-config-prop-edit></card-config-prop-edit> --> </div> <!-- <br /><br /> --> <!-- <el-button @click="initCard">新建页面</el-button> --> </div> <!-- <div class="action-part"> <div class="action-part-title"><h2>阅卷参数</h2></div> <div class="action-part-body"> <el-button type="primary" @click="modifyParams" >上传阅卷参数<span class="color-danger" >({{ paperParams["pageSumScore"] || 0 }}分)</span ></el-button > </div> </div> --> </div> <div id="design-main" class="design-main"> <!-- menus --> <div class="design-control"> <div class="control-left tab-btns"> <el-button v-for="(page, pageNo) in pages" :key="pageNo" :type="curPageNo === pageNo ? 'primary' : 'default'" @click="swithPage(pageNo)" >第{{ pageNo + 1 }}页</el-button > </div> <div class="control-right"> <el-button type="success" :loading="isSubmit" :disabled="!pages.length" @click="toPreview" >预览</el-button > <!-- <el-button v-if="showSaveBtn" type="primary" :loading="isSubmit" :disabled="canSave || !pages.length" @click="toSave" >暂存</el-button > --> <el-button type="primary" :loading="isSubmit" @click="toSubmit" >提交</el-button > </div> </div> <!-- edit body --> <div class="design-body"> <!-- 注意:后台要替换内容,改类名时,要注意 --> <div v-for="(page, pageNo) in pages" :key="pageNo" :id="`edit-page-box-${pageNo}`" :class="[ 'page-box', `page-box-${cardConfig.pageSize}`, `page-box-${pageNo % 2}`, { 'page-box-less': pages.length <= 2 }, ]" > <!-- locator --> <div class="page-locator page-locator-top"> <div v-for="elem in page.locators.top" :key="elem.id" :id="elem.id" class="page-locator-item" ></div> </div> <div class="page-locator page-locator-bottom"> <div v-for="elem in page.locators.bottom" :key="elem.id" :id="elem.id" class="page-locator-item" ></div> </div> <!-- inner edit area --> <div class="page-main-inner"> <div :class="['page-main', `page-main-${page.columns.length}`]" :style="{ margin: `0 -${page.columnGap / 2}px` }" > <div class="page-column" v-for="(column, columnNo) in page.columns" :key="columnNo" :style="{ padding: `0 ${page.columnGap / 2}px` }" > <div class="page-column-main" :id="[`column-${pageNo}-${columnNo}`]" > <div class="page-column-body" v-if="column.elements.length"> <topic-element-edit class="page-column-element" :data-h="element.h" v-for="element in column.elements" :key="element.id" :data="element" ></topic-element-edit> </div> <div class="page-column-body" v-else> <div class="page-column-forbid-area" v-if="cardConfig.showForbidArea" > <p>该区域严禁作答</p> </div> </div> </div> </div> </div> </div> <!-- outer edit area --> <div class="page-main-outer"> <page-number type="rect" :total="pages.length" :current="pageNo + 1" ></page-number> <page-number type="text" :total="pages.length" :current="pageNo + 1" ></page-number> </div> </div> </div> </div> <!-- all topics --> <div class="topic-list"> <div :class="['page-box', `page-box-${cardConfig.pageSize}`]"> <div class="page-main-inner"> <div :class="['page-main', `page-main-${cardConfig.columnNumber}`]" :style="{ margin: `0 -${cardConfig.columnGap / 2}px` }" > <div class="page-column" :style="{ padding: `0 ${cardConfig.columnGap / 2}px` }" > <div class="page-column-main" id="topic-column"> <div class="page-column-body"> <!-- card-head-sample --> <card-head-sample :data="cardHeadSampleData" id="simple-card-head" v-if="topics.length && cardHeadSampleData" ></card-head-sample> <!-- topic element --> <topic-element-preview class="page-column-element" v-for="element in topics" :key="element.id" :data="element" ></topic-element-preview> </div> </div> </div> </div> </div> </div> </div> <!-- element-prop-edit --> <element-prop-edit ref="ElementPropEdit"></element-prop-edit> <!-- right-click-menu --> <right-click-menu @inset-topic="insetNewTopic"></right-click-menu> <!-- paper-params --> <paper-params :pages="pages" :paper-params="paperParams" @confirm="paperParamsModified" ref="PaperParams" ></paper-params> <!-- topic select dialog --> <topic-select-dialog ref="TopicSelectDialog" :topics="topicList" @confirm="addNewTopic" ></topic-select-dialog> </div> </template> <script> import { mapState, mapMutations, mapActions } from "vuex"; import { getElementModel, getCardHeadModel, ELEMENT_LIST, TOPIC_LIST, NOT_TOPIC_LIST, OTHER_ELEMENT, } from "../elementModel"; import { CARD_VERSION } from "../enumerate"; // import CardConfigPropEdit from "../components/CardConfigPropEdit"; import TopicElementEdit from "../components/TopicElementEdit"; import TopicElementPreview from "../components/TopicElementPreview"; import PagePropEdit from "../components/PagePropEdit"; import ElementPropEdit from "../components/ElementPropEdit"; import RightClickMenu from "../components/RightClickMenu"; import PageNumber from "../components/PageNumber"; import PaperParams from "../components/PaperParams"; import CardHeadSample from "../elements/card-head/CardHead"; import TopicSelectDialog from "../components/TopicSelectDialog"; export default { name: "card-design", props: { content: { type: Object, default() { return { pages: [], cardConfig: {}, paperParams: {}, }; }, }, showSaveBtn: { type: Boolean, default: true, }, }, components: { // CardConfigPropEdit, TopicElementEdit, TopicElementPreview, PagePropEdit, ElementPropEdit, RightClickMenu, CardHeadSample, PageNumber, PaperParams, TopicSelectDialog, }, data() { return { ELEMENT_LIST, TOPIC_LIST, NOT_TOPIC_LIST, topicList: [], steps: ["添加标题", "基本设置", "试题配置", "预览生成"], columnWidth: 0, isSubmit: false, canSave: false, }; }, computed: { ...mapState("card", [ "cardConfig", "topics", "pages", "paperParams", "curElement", "curPage", "curPageNo", ]), cardHeadSampleData() { if (!this.cardConfig["pageSize"]) return; const data = getCardHeadModel(this.cardConfig); data.isSimple = true; return data; }, }, mounted() { this.initCard(); }, methods: { ...mapMutations("card", [ "addPage", "setCurPage", "setCurElement", "setCardConfig", "setOpenElementEditDialog", "setCurDragElement", "setPages", "setPaperParams", "setInsetTarget", "initState", ]), ...mapActions("card", [ "resetTopicSeries", "removePage", "addElement", "modifyCardHead", "modifyElement", "rebuildPages", "initTopicsFromPages", "scrollToElementPage", ]), async initCard() { const { cardConfig, pages, paperParams } = this.content; this.setCardConfig(cardConfig); this.setPaperParams(paperParams); if (pages && pages.length) { this.setPages(pages); this.initTopicsFromPages(); this.resetTopicSeries(); this.setCurPage(0); } else { this.initPageData(); } this.addWatch(); this.$nextTick(() => { this.registSrollEvent(); }); }, initPageData() { this.modifyCardHead({ ...getCardHeadModel(this.cardConfig), }); this.$nextTick(() => { this.rebuildPages(); this.setCurPage(0); }); }, addNewTopic(item) { let element = getElementModel(item.type); element.w = document.getElementById("topic-column").offsetWidth; if (item.type === "FORBID_AREA") { const lastTopicElement = this.topics.findLast( (item) => !OTHER_ELEMENT.includes(item.type) ); element.sign = lastTopicElement ? lastTopicElement.sign : "objective"; this.addElement(element); this.setCurElement(element); this.$nextTick(() => { this.rebuildPages(); this.$nextTick(() => { this.scrollToElementPage(element); }); }); } else { this.setCurElement(element); this.$refs.ElementPropEdit.open(); // to elementPropEdit/ElementPropEdit open topic edit dialog } }, insetNewTopic({ id, type }) { console.log(id, type); this.setInsetTarget({ id, type }); if (type === "FILL_QUESTION") { this.topicList = this.TOPIC_LIST; } else { this.topicList = this.TOPIC_LIST.filter( (item) => item.type !== "FILL_QUESTION" ); } this.$refs.TopicSelectDialog.open(); }, // 元件编辑 dragstart(element) { this.setCurDragElement(getElementModel(element.type)); }, addWatch() { this.$watch("cardConfig", (val) => { const element = getCardHeadModel(val); this.modifyCardHead(element); this.$nextTick(() => { this.rebuildPages(); }); }); }, // 页面切换 swithPage(pindex) { if (this.curPageNo === pindex) return; let pageTops = this.pages.map((page, pageNo) => { return document.getElementById(`edit-page-box-${pageNo}`).offsetTop; }); pageTops = pageTops.map((item) => item - pageTops[0]); document.getElementById("design-main").scrollTop = pageTops[pindex]; this.setCurPage(pindex); this.setCurElement({}); }, registSrollEvent() { document.getElementById("design-main").addEventListener("scroll", (e) => { e.preventDefault(); e.stopPropagation(); let pageTops = this.pages.map((page, pageNo) => { return document.getElementById(`edit-page-box-${pageNo}`).offsetTop; }); const pageRangeHeight = document.getElementById(`edit-page-box-0`).offsetHeight * 0.2; pageTops = pageTops.map((item) => item - pageTops[0] - pageRangeHeight); const scrollTop = e.target.scrollTop; // console.log(pageTops, scrollTop); const targePageTop = pageTops.find((item) => scrollTop < item); const pageNo = targePageTop ? pageTops.indexOf(targePageTop) - 1 : pageTops.length - 1; if (this.curPageNo === pageNo) return; this.setCurPage(pageNo); }); }, // paper-params modifyParams() { this.$refs.PaperParams.open(); }, paperParamsModified(paperParams) { this.setPaperParams(paperParams); }, // save getCardData(htmlContent = "", model = "") { const data = { title: this.cardConfig.cardTitle, content: model, htmlContent, }; return data; }, checkElementCovered() { let elements = []; this.pages.forEach((page) => { page.columns.forEach((column) => { column.elements.forEach((element) => { if (element.isCovered) { elements.push(element.id); } }); }); }); return elements.length; }, checkCardValid() { if (!this.cardConfig.cardTitle) { this.$message.error("题卡标题不能为空!"); this.setCurPageNo(0); setTimeout(() => { document.getElementById("cardTitleInput").focus(); }); return false; } // if (!this.cardConfig.cardDesc) { // this.$message.error("题卡描述信息不能为空!"); // this.setCurPage(0); // setTimeout(() => { // document.getElementById("cardDescInput").focus(); // }); // return false; // } if (this.checkElementCovered()) { this.$message.error("题卡中存在被遮挡的元件,请注意调整!"); return false; } return true; }, getCardJson() { // 防止页面未渲染完成,各试题高度未及时更新,保存数据有误的问题 return new Promise((resolve) => { setTimeout(() => { const data = JSON.stringify( { version: CARD_VERSION, cardType: "STANDARD", cardConfig: this.cardConfig, paperParams: this.paperParams, pages: this.pages, }, (k, v) => (k.startsWith("_") ? undefined : v) ); resolve(data); }, 100); }); }, async toPreview() { if (this.isSubmit) return; this.isSubmit = true; const model = await this.getCardJson(); const datas = this.getCardData("", model); this.$emit("on-preview", datas); }, async toSave() { if (!this.checkCardValid()) return; if (this.isSubmit) return; this.isSubmit = true; const model = await this.getCardJson(); const datas = this.getCardData("", model); this.$emit("on-save", datas); }, toSubmit() { if (this.isSubmit) return; if (!this.checkCardValid()) return; this.$emit("on-submit", { cardConfig: this.cardConfig, pages: this.pages, paperParams: this.paperParams, }); }, toExit() { this.$emit("on-exit"); }, loading() { this.isSubmit = true; }, unloading() { this.isSubmit = false; }, }, beforeDestroy() { this.initState(); }, }; </script>