|
@@ -1,687 +1,698 @@
|
|
|
-import {
|
|
|
- getExplainElements,
|
|
|
- getFillQuesitonElements,
|
|
|
- getFillLineElements,
|
|
|
- getCompositionElements,
|
|
|
- getNewPage,
|
|
|
- getTopicHeadModel
|
|
|
-} from "../elementModel";
|
|
|
-import { objAssign, deepCopy, calcSum, getElementId } from "../plugins/utils";
|
|
|
-
|
|
|
-const state = {
|
|
|
- cardConfig: {},
|
|
|
- paperParams: {},
|
|
|
- curElement: {},
|
|
|
- curDragElement: {},
|
|
|
- curPage: {},
|
|
|
- curPageNo: 0,
|
|
|
- pages: [],
|
|
|
- topics: [],
|
|
|
- topicSeries: [], // 大题顺序号
|
|
|
- insetTarget: {}, // 需要在其后面插入大题的大题
|
|
|
- openElementEditDialog: false
|
|
|
-};
|
|
|
-
|
|
|
-const mutations = {
|
|
|
- setCardConfig(state, cardConfig) {
|
|
|
- state.cardConfig = Object.assign({}, state.cardConfig, cardConfig);
|
|
|
- },
|
|
|
- setPaperParams(state, paperParams) {
|
|
|
- state.paperParams = paperParams;
|
|
|
- },
|
|
|
- setCurElement(state, curElement) {
|
|
|
- state.curElement = curElement;
|
|
|
- },
|
|
|
- setCurDragElement(state, curDragElement) {
|
|
|
- state.curDragElement = curDragElement;
|
|
|
- },
|
|
|
- setCurPage(state, curPageNo) {
|
|
|
- const pageNo = state.pages[curPageNo] ? curPageNo : 0;
|
|
|
- state.curPage = state.pages[pageNo];
|
|
|
- state.curPageNo = pageNo;
|
|
|
- },
|
|
|
- setCurPageNo(state, curPageNo) {
|
|
|
- state.curPageNo = curPageNo;
|
|
|
- },
|
|
|
- setPages(state, pages) {
|
|
|
- state.pages = pages;
|
|
|
- },
|
|
|
- setTopics(state, topics) {
|
|
|
- state.topics = topics;
|
|
|
- },
|
|
|
- setTopicSeries(state, topicSeries) {
|
|
|
- state.topicSeries = topicSeries;
|
|
|
- },
|
|
|
- setInsetTarget(state, insetTarget) {
|
|
|
- state.insetTarget = insetTarget;
|
|
|
- },
|
|
|
- addPage(state, page) {
|
|
|
- state.pages.push(page);
|
|
|
- },
|
|
|
- modifyPage(state, page) {
|
|
|
- state.pages.splice(page._pageNo, 1, page);
|
|
|
- },
|
|
|
- setOpenElementEditDialog(state, openElementEditDialog) {
|
|
|
- state.openElementEditDialog = openElementEditDialog;
|
|
|
- },
|
|
|
- initState(state) {
|
|
|
- state.curElement = {};
|
|
|
- state.curDragElement = {};
|
|
|
- state.curPage = {};
|
|
|
- state.curPageNo = 0;
|
|
|
- state.topics = [];
|
|
|
- state.pages = [];
|
|
|
- state.cardConfig = {};
|
|
|
- state.paperParams = {};
|
|
|
- state.openElementEditDialog = false;
|
|
|
- state.topicSeries = [];
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const fetchElementPositionInfos = (element, topics) => {
|
|
|
- return topics.findIndex(item => item.id === element.id);
|
|
|
-};
|
|
|
-
|
|
|
-const fetchAllRelateParentElementPositionInfos = (parentElement, topics) => {
|
|
|
- // 当为解答题时,parentElement传入的值是EXPLAIN
|
|
|
- let postionInfos = [];
|
|
|
- topics.forEach((item, eindex) => {
|
|
|
- if (item["parent"] && item.parent.id === parentElement.id) {
|
|
|
- let pos = { _elementNo: eindex };
|
|
|
- if (parentElement.type === "EXPLAIN") {
|
|
|
- pos.serialNumber = item.serialNumber;
|
|
|
- }
|
|
|
- postionInfos.push(pos);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return postionInfos;
|
|
|
-};
|
|
|
-
|
|
|
-const fetchFirstSubjectiveTopicPositionInfo = topics => {
|
|
|
- return topics.findIndex(item => item.sign === "subjective");
|
|
|
-};
|
|
|
-
|
|
|
-const fetchSameSerialNumberChildrenPositionInfo = (
|
|
|
- elementChildernElement,
|
|
|
- topics
|
|
|
-) => {
|
|
|
- let postionInfos = [];
|
|
|
- const elementId = elementChildernElement.parent.id;
|
|
|
- const serialNumber = elementChildernElement.serialNumber;
|
|
|
-
|
|
|
- topics.forEach((item, eindex) => {
|
|
|
- if (
|
|
|
- item.parent &&
|
|
|
- item.parent.id === elementId &&
|
|
|
- item.serialNumber === serialNumber
|
|
|
- ) {
|
|
|
- postionInfos.push({
|
|
|
- _elementNo: eindex,
|
|
|
- _elementId: item.id
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- return postionInfos;
|
|
|
-};
|
|
|
-
|
|
|
-const groupByParams = (datas, paramKey) => {
|
|
|
- let elementGroupInfos = [];
|
|
|
- for (let i = 0, len = datas.length; i < len; i++) {
|
|
|
- if (i === 0 || datas[i][paramKey] !== datas[i - 1][paramKey]) {
|
|
|
- elementGroupInfos.push([datas[i]]);
|
|
|
- } else {
|
|
|
- elementGroupInfos[elementGroupInfos.length - 1].push(datas[i]);
|
|
|
- }
|
|
|
- }
|
|
|
- return elementGroupInfos;
|
|
|
-};
|
|
|
-
|
|
|
-const findElementById = (id, topics) => {
|
|
|
- let curElement = null;
|
|
|
- topics.forEach(element => {
|
|
|
- if (curElement) return;
|
|
|
- if (element.id === id) {
|
|
|
- curElement = element;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (element["elements"]) {
|
|
|
- element["elements"].forEach(elem => {
|
|
|
- if (elem.id === id) curElement = elem;
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- return curElement;
|
|
|
-};
|
|
|
-
|
|
|
-const checkElementisCovered = (id, type) => {
|
|
|
- const elementDom = document.getElementById(id);
|
|
|
-
|
|
|
- if (type === "EXPLAIN") {
|
|
|
- const elemTitleDome = elementDom.querySelector(".elem-title");
|
|
|
- const limitHeight = elemTitleDome
|
|
|
- ? elementDom.offsetHeight - elemTitleDome.offsetHeight
|
|
|
- : elementDom.offsetHeight;
|
|
|
-
|
|
|
- let elementHeights = [];
|
|
|
- elementDom
|
|
|
- .querySelector(".elem-explain-elements")
|
|
|
- .childNodes.forEach(node => {
|
|
|
- if (!node.className.includes("elem-explain-element")) return;
|
|
|
- elementHeights.push(
|
|
|
- node.firstChild.offsetHeight + node.firstChild.offsetTop
|
|
|
- );
|
|
|
- });
|
|
|
- return elementHeights.some(item => item > limitHeight);
|
|
|
- }
|
|
|
-
|
|
|
- if (type === "COMPOSITION") {
|
|
|
- const elemTitleDome = elementDom.querySelector(".elem-title");
|
|
|
- const limitHeight = elemTitleDome
|
|
|
- ? elementDom.offsetHeight - elemTitleDome.offsetHeight
|
|
|
- : elementDom.offsetHeight;
|
|
|
-
|
|
|
- let elementHeights = [];
|
|
|
- elementDom
|
|
|
- .querySelector(".elem-composition-elements")
|
|
|
- .childNodes.forEach(node => {
|
|
|
- if (!node.className.includes("elem-composition-element")) return;
|
|
|
- elementHeights.push(
|
|
|
- node.firstChild.offsetHeight + node.firstChild.offsetTop
|
|
|
- );
|
|
|
- });
|
|
|
- return elementHeights.some(item => item > limitHeight);
|
|
|
- }
|
|
|
-
|
|
|
- return elementDom.offsetHeight < elementDom.firstChild.offsetHeight;
|
|
|
-};
|
|
|
-
|
|
|
-const createFunc = {
|
|
|
- EXPLAIN(element) {
|
|
|
- return getExplainElements(element);
|
|
|
- },
|
|
|
- FILL_QUESTION(element) {
|
|
|
- return getFillQuesitonElements(element, state.cardConfig);
|
|
|
- },
|
|
|
- FILL_LINE(element) {
|
|
|
- return getFillLineElements(element);
|
|
|
- },
|
|
|
- COMPOSITION(element) {
|
|
|
- return [getCompositionElements(element)];
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const actions = {
|
|
|
- initTopicsFromPages({ state, commit }) {
|
|
|
- let topics = [];
|
|
|
- state.pages.forEach(page => {
|
|
|
- page.columns.forEach(column => {
|
|
|
- column.elements.forEach(element => {
|
|
|
- if (
|
|
|
- element.type === "TOPIC_HEAD" ||
|
|
|
- (element.type === "CARD_HEAD" && element.isSimple)
|
|
|
- )
|
|
|
- return;
|
|
|
- topics.push(element);
|
|
|
- });
|
|
|
- });
|
|
|
- });
|
|
|
- commit("setTopics", topics);
|
|
|
- },
|
|
|
- actElementById({ state, commit }, id) {
|
|
|
- const curElement = findElementById(id, state.topics);
|
|
|
- if (!curElement) return;
|
|
|
-
|
|
|
- commit("setCurElement", curElement);
|
|
|
- },
|
|
|
- resetTopicSeries({ state, commit }) {
|
|
|
- let curTopicId = "",
|
|
|
- curTopicNo = 0,
|
|
|
- topicSeries = [];
|
|
|
- state.topics.forEach(topic => {
|
|
|
- if (!topic.parent) return;
|
|
|
- if (curTopicId !== topic.parent.id) {
|
|
|
- curTopicId = topic.parent.id;
|
|
|
- curTopicNo++;
|
|
|
- topicSeries.push({
|
|
|
- id: curTopicId,
|
|
|
- topicNo: curTopicNo,
|
|
|
- type: topic.type,
|
|
|
- sign: topic.sign
|
|
|
- });
|
|
|
- }
|
|
|
- topic.topicNo = curTopicNo;
|
|
|
- });
|
|
|
- commit("setTopicSeries", topicSeries);
|
|
|
- },
|
|
|
- // 新增试题 --------------->
|
|
|
- addElement({ state, commit, dispatch }, element) {
|
|
|
- let pos = null;
|
|
|
- // 客观题和主观题分别对待
|
|
|
- if (state.insetTarget.id) {
|
|
|
- // 存在插入目标元素
|
|
|
- if (
|
|
|
- state.insetTarget.type === "FILL_QUESTION" &&
|
|
|
- element.type !== "FILL_QUESTION"
|
|
|
- ) {
|
|
|
- pos = fetchFirstSubjectiveTopicPositionInfo(state.topics);
|
|
|
- } else {
|
|
|
- let relateTopicPos = [];
|
|
|
- state.topics.forEach((item, index) => {
|
|
|
- if (item.parent && item.parent.id === state.insetTarget.id)
|
|
|
- relateTopicPos.push(index);
|
|
|
- });
|
|
|
- pos = relateTopicPos.slice(-1)[0] + 1;
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 不存在插入目标元素
|
|
|
- if (element.sign === "objective") {
|
|
|
- pos = fetchFirstSubjectiveTopicPositionInfo(state.topics);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let preElements = createFunc[element.type](element);
|
|
|
- preElements.forEach((preElement, index) => {
|
|
|
- if (pos && pos !== -1) {
|
|
|
- state.topics.splice(pos + index, 0, preElement);
|
|
|
- } else {
|
|
|
- state.topics.push(preElement);
|
|
|
- }
|
|
|
- });
|
|
|
- dispatch("resetTopicSeries");
|
|
|
- commit("setInsetTarget", {});
|
|
|
-
|
|
|
- commit("setCurElement", element);
|
|
|
- },
|
|
|
- // 修改试题 --------------->
|
|
|
- modifyTopic({ state }, element) {
|
|
|
- // 单独编辑某个细分题
|
|
|
- const pos = fetchElementPositionInfos(element, state.topics);
|
|
|
- state.topics.splice(pos, 1, element);
|
|
|
- },
|
|
|
- modifyComposition({ state }, element) {
|
|
|
- const positionInfos = fetchAllRelateParentElementPositionInfos(
|
|
|
- element,
|
|
|
- state.topics
|
|
|
- );
|
|
|
- positionInfos.forEach(({ _elementNo }) => {
|
|
|
- state.topics[_elementNo].topicNo = element.topicNo;
|
|
|
- state.topics[_elementNo].parent = objAssign(
|
|
|
- state.topics[_elementNo].parent,
|
|
|
- element
|
|
|
- );
|
|
|
- });
|
|
|
- },
|
|
|
- modifyExplain({ state }, element) {
|
|
|
- // 解答题既是拆分题,又是可复制题
|
|
|
- const positionInfos = fetchAllRelateParentElementPositionInfos(
|
|
|
- element,
|
|
|
- state.topics
|
|
|
- );
|
|
|
- const elementGroupPosInfos = groupByParams(positionInfos, "serialNumber");
|
|
|
- const orgElementCount = elementGroupPosInfos.length;
|
|
|
- if (orgElementCount > element.questionsCount) {
|
|
|
- // 原小题数多于新小题数,要删除原多于的小题;
|
|
|
- let needDeleteInfos = elementGroupPosInfos.splice(
|
|
|
- element.questionsCount,
|
|
|
- orgElementCount - element.questionsCount
|
|
|
- );
|
|
|
- needDeleteInfos.reverse().forEach(item => {
|
|
|
- item.reverse().forEach(pos => {
|
|
|
- state.topics.splice(pos._elementNo, 1);
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- const newElements = getExplainElements(element);
|
|
|
- const lastPos = elementGroupPosInfos.slice(-1)[0].slice(-1)[0];
|
|
|
- let lastNewElementPos = lastPos._elementNo;
|
|
|
- for (let i = 0; i < element.questionsCount; i++) {
|
|
|
- if (elementGroupPosInfos[i]) {
|
|
|
- elementGroupPosInfos[i].forEach(pos => {
|
|
|
- let child = state.topics[pos._elementNo];
|
|
|
- child.serialNumber = i + element.startNumber;
|
|
|
- child.parent = { ...element };
|
|
|
- child.topicNo = element.topicNo;
|
|
|
- });
|
|
|
- } else {
|
|
|
- state.topics.splice(++lastNewElementPos, 0, newElements[i]);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- modifySplitTopic({ state }, element) {
|
|
|
- // 非作文题都是拆分题,即同一个题拆分成多个小题展示
|
|
|
- const positionInfos = fetchAllRelateParentElementPositionInfos(
|
|
|
- element,
|
|
|
- state.topics
|
|
|
- );
|
|
|
- // 缓存已编辑的小题高度信息。
|
|
|
- // const elementHeights = positionInfos.map(
|
|
|
- // pos => state.topics[pos._elementNo].h
|
|
|
- // );
|
|
|
- // 删除所有小题
|
|
|
- positionInfos.reverse().forEach(pos => {
|
|
|
- state.topics.splice(pos._elementNo, 1);
|
|
|
- });
|
|
|
- // 创建新的小题元素
|
|
|
- const newElements = createFunc[element.type](element);
|
|
|
- const pos = positionInfos.pop();
|
|
|
- newElements.forEach((newElement, index) => {
|
|
|
- // 不再复用缓存高度,修改于2022-03-10 09:23
|
|
|
- // newElement.h = Math.max(elementHeights[index] || 0, newElement.h);
|
|
|
- state.topics.splice(pos._elementNo + index, 0, newElement);
|
|
|
- });
|
|
|
- },
|
|
|
- modifyElement({ commit, dispatch }, element) {
|
|
|
- if (element.type === "COMPOSITION") {
|
|
|
- dispatch("modifyComposition", element);
|
|
|
- } else if (element.type === "EXPLAIN") {
|
|
|
- dispatch("modifyExplain", element);
|
|
|
- } else {
|
|
|
- dispatch("modifySplitTopic", element);
|
|
|
- }
|
|
|
- dispatch("resetTopicSeries");
|
|
|
- commit("setCurElement", element);
|
|
|
- },
|
|
|
- modifyCardHead({ state }, element) {
|
|
|
- state.topics.splice(0, 1, element);
|
|
|
- },
|
|
|
- // 修改试题包含元素
|
|
|
- modifyElementChild({ state, commit }, element) {
|
|
|
- // 修改解答题小题和作文题的子元素
|
|
|
- const pos = fetchElementPositionInfos(element.container, state.topics);
|
|
|
- const columnElements = state.topics[pos].elements;
|
|
|
- const childIndex = columnElements.findIndex(item => item.id === element.id);
|
|
|
- element.id = getElementId();
|
|
|
- // 作文题中的多线条和网格元素重新计算高度。
|
|
|
- if (element.container.type === "COMPOSITION") {
|
|
|
- if (element.type === "LINES") {
|
|
|
- element.h = element.lineCount * (element.lineSpacing + 1);
|
|
|
- }
|
|
|
- if (element.type === "GRIDS") {
|
|
|
- element.h =
|
|
|
- element.rowCount * (element.columnSize + 1 + element.rowSpace) + 1;
|
|
|
- }
|
|
|
- }
|
|
|
- if (childIndex === -1) {
|
|
|
- columnElements.push(element);
|
|
|
- } else {
|
|
|
- columnElements.splice(childIndex, 1, element);
|
|
|
- }
|
|
|
-
|
|
|
- commit("setCurElement", element);
|
|
|
- },
|
|
|
- // 粘贴试题内的元素
|
|
|
- pasteExplainElementChild({ state }, { curElement, pasteElement }) {
|
|
|
- let element = {
|
|
|
- id: curElement.container ? curElement.container.id : curElement.id
|
|
|
- };
|
|
|
- const pos = fetchElementPositionInfos(element, state.topics);
|
|
|
- if (pos === -1) return;
|
|
|
-
|
|
|
- element = state.topics[pos];
|
|
|
- const newElement = Object.assign({}, pasteElement, {
|
|
|
- id: getElementId(),
|
|
|
- container: {
|
|
|
- id: element.id,
|
|
|
- type: element.type
|
|
|
- }
|
|
|
- });
|
|
|
- element.elements.push(newElement);
|
|
|
- },
|
|
|
- // 删除试题 --------------->
|
|
|
- removeElement({ state, commit, dispatch }, element) {
|
|
|
- const positionInfos = fetchAllRelateParentElementPositionInfos(
|
|
|
- element.parent,
|
|
|
- state.topics
|
|
|
- );
|
|
|
- positionInfos.reverse().forEach(pos => {
|
|
|
- state.topics.splice(pos._elementNo, 1);
|
|
|
- });
|
|
|
-
|
|
|
- dispatch("resetTopicSeries");
|
|
|
-
|
|
|
- commit("setCurElement", {});
|
|
|
- },
|
|
|
- // 删除试题包含元素 --------------->
|
|
|
- removeElementChild({ state, commit }, element) {
|
|
|
- // 删除解答题小题和作文题的子元素
|
|
|
- const pos = fetchElementPositionInfos(element.container, state.topics);
|
|
|
- const columnElements = state.topics[pos].elements;
|
|
|
- const childIndex = columnElements.findIndex(item => item.id === element.id);
|
|
|
- columnElements.splice(childIndex, 1);
|
|
|
-
|
|
|
- commit("setCurElement", {});
|
|
|
- },
|
|
|
- // 扩展答题区操作 --------------->
|
|
|
- copyExplainChildren({ state }, element) {
|
|
|
- let curElement = {
|
|
|
- id: element.container ? element.container.id : element.id
|
|
|
- };
|
|
|
- const pos = fetchElementPositionInfos(curElement, state.topics);
|
|
|
- curElement = state.topics[pos];
|
|
|
-
|
|
|
- let newElement = Object.assign({}, curElement, {
|
|
|
- id: getElementId(),
|
|
|
- elements: [],
|
|
|
- h: 200,
|
|
|
- isExtend: true,
|
|
|
- showTitle: false
|
|
|
- });
|
|
|
-
|
|
|
- state.topics.splice(pos + 1, 0, newElement);
|
|
|
- // 更新小题答题区isLast
|
|
|
- let positionInfos = fetchSameSerialNumberChildrenPositionInfo(
|
|
|
- curElement,
|
|
|
- state.topics
|
|
|
- );
|
|
|
- positionInfos.forEach((pos, pindex) => {
|
|
|
- state.topics[pos._elementNo].isLast = pindex + 1 === positionInfos.length;
|
|
|
- });
|
|
|
- },
|
|
|
- deleteExplainChildren({ state }, element) {
|
|
|
- let curElement = {
|
|
|
- id: element.container ? element.container.id : element.id
|
|
|
- };
|
|
|
- const curPos = fetchElementPositionInfos(curElement, state.topics);
|
|
|
- curElement = state.topics[curPos];
|
|
|
-
|
|
|
- let positionInfos = fetchSameSerialNumberChildrenPositionInfo(
|
|
|
- curElement,
|
|
|
- state.topics
|
|
|
- );
|
|
|
- if (positionInfos.length < 2) return;
|
|
|
- const pindex = positionInfos.findIndex(
|
|
|
- item => item._elementId === curElement.id
|
|
|
- );
|
|
|
- const pos = positionInfos[pindex]._elementNo;
|
|
|
- positionInfos.splice(pindex, 1);
|
|
|
- const nextPos = positionInfos[0]._elementNo;
|
|
|
- // 当删除的是非扩展区域时,则下一个答题区要被设置成非扩展区
|
|
|
- if (!curElement.isExtend) {
|
|
|
- state.topics[nextPos].isExtend = false;
|
|
|
- }
|
|
|
- // 当删除的是含有标题答题区时,则需要将下一个答题区开启显示标题。
|
|
|
- if (curElement.showTitle) {
|
|
|
- state.topics[nextPos].showTitle = true;
|
|
|
- }
|
|
|
- state.topics.splice(pos, 1);
|
|
|
- // 更新小题答题区isLast
|
|
|
- positionInfos = fetchSameSerialNumberChildrenPositionInfo(
|
|
|
- curElement,
|
|
|
- state.topics
|
|
|
- );
|
|
|
- positionInfos.forEach((pos, pindex) => {
|
|
|
- state.topics[pos._elementNo].isLast = pindex + 1 === positionInfos.length;
|
|
|
- });
|
|
|
- },
|
|
|
- // 大题顺序操作 --------------->
|
|
|
- topicMoveUp({ state, dispatch }, topicId) {
|
|
|
- const curTopicPos = state.topicSeries.findIndex(
|
|
|
- item => item.id === topicId
|
|
|
- );
|
|
|
- const prevTopicId = state.topicSeries[curTopicPos - 1].id;
|
|
|
- let relateTopicPos = [];
|
|
|
- state.topics.forEach((item, index) => {
|
|
|
- if (item.parent && item.parent.id === topicId) relateTopicPos.push(index);
|
|
|
- });
|
|
|
- const prevTopicFirstIndex = state.topics.findIndex(
|
|
|
- item => item.parent && item.parent.id === prevTopicId
|
|
|
- );
|
|
|
- const relateTopics = state.topics.splice(
|
|
|
- relateTopicPos[0],
|
|
|
- relateTopicPos.length
|
|
|
- );
|
|
|
- relateTopics.reverse().forEach(topic => {
|
|
|
- state.topics.splice(prevTopicFirstIndex, 0, topic);
|
|
|
- });
|
|
|
-
|
|
|
- dispatch("resetTopicSeries");
|
|
|
- },
|
|
|
- // 重构页面
|
|
|
- resetElementProp({ state }, isResetId = false) {
|
|
|
- state.topics.forEach(element => {
|
|
|
- const elementDom = document.getElementById(element.id);
|
|
|
- if (elementDom) {
|
|
|
- element.h = elementDom.offsetHeight;
|
|
|
- element.w = elementDom.offsetWidth;
|
|
|
- }
|
|
|
- if (isResetId) {
|
|
|
- element.id = getElementId();
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
- rebuildPages({ state, commit }) {
|
|
|
- const columnNumber = state.cardConfig.columnNumber;
|
|
|
- const pageSize = state.cardConfig.pageSize;
|
|
|
- // 更新元件最新的高度信息
|
|
|
- // 整理所有元件
|
|
|
- const cardHeadElement = state.topics[0];
|
|
|
- state.topics.forEach(element => {
|
|
|
- const elementDom = document.getElementById(`preview-${element.id}`);
|
|
|
-
|
|
|
- if (elementDom) {
|
|
|
- element.h = elementDom.offsetHeight;
|
|
|
- element.w = elementDom.offsetWidth;
|
|
|
- // 解答题小题与其他题有些区别。
|
|
|
- // 其他题都是通过内部子元素自动撑高元件,而解答题则需要手动设置高度。
|
|
|
- const ESCAPE_ELEMENTS = ["CARD_HEAD"];
|
|
|
- element.isCovered =
|
|
|
- !ESCAPE_ELEMENTS.includes(element.type) &&
|
|
|
- checkElementisCovered(`preview-${element.id}`, element.type);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 动态计算每列可以分配的元件
|
|
|
- const columnHeight = document.getElementById("topic-column").offsetHeight;
|
|
|
- const simpleCardHeadHeight = document.getElementById("simple-card-head")
|
|
|
- .offsetHeight;
|
|
|
- let pages = [];
|
|
|
- let page = {};
|
|
|
- let columns = [];
|
|
|
- let curColumnElements = [];
|
|
|
- let curColumnHeight = 0;
|
|
|
-
|
|
|
- const initCurColumnElements = () => {
|
|
|
- curColumnElements = [];
|
|
|
- curColumnHeight = 0;
|
|
|
- const groupColumnNumber = columnNumber * 2;
|
|
|
- // 奇数页第一栏;
|
|
|
- if (!(columns.length % groupColumnNumber)) {
|
|
|
- // 非第一页奇数页第一栏
|
|
|
- if (columns.length) {
|
|
|
- const cardHeadSimpleElement = Object.assign({}, cardHeadElement, {
|
|
|
- id: getElementId(),
|
|
|
- isSimple: true,
|
|
|
- h: simpleCardHeadHeight
|
|
|
- });
|
|
|
- curColumnElements.push(cardHeadSimpleElement);
|
|
|
- curColumnHeight += simpleCardHeadHeight;
|
|
|
- } else {
|
|
|
- curColumnElements.push(cardHeadElement);
|
|
|
- curColumnHeight += cardHeadElement.h;
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const checkElementIsCurColumnFirstType = element => {
|
|
|
- const topicHeadIndex = curColumnElements.findIndex(
|
|
|
- elem => elem.type === "TOPIC_HEAD" && elem.sign === element.sign
|
|
|
- );
|
|
|
- return topicHeadIndex === -1;
|
|
|
- };
|
|
|
-
|
|
|
- // 放入元素通用流程
|
|
|
- const pushElement = element => {
|
|
|
- // 当前栏中第一个题型之前新增题型头元素(topic-head)。
|
|
|
- // 题型头和当前题要组合加入栏中,不可拆分。
|
|
|
- let elementList = [element];
|
|
|
- if (checkElementIsCurColumnFirstType(element)) {
|
|
|
- elementList.unshift(
|
|
|
- getTopicHeadModel(
|
|
|
- state.cardConfig[`${element.sign}Attention`],
|
|
|
- element.sign,
|
|
|
- !curColumnElements.length
|
|
|
- )
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- const elementHeight = calcSum(elementList.map(elem => elem.h));
|
|
|
- if (curColumnHeight + elementHeight > columnHeight) {
|
|
|
- // 当前栏第一个元素高度超过一栏时,不拆分,直接放在当前栏。
|
|
|
- // 解决可能空栏的情况
|
|
|
- const curElementIsFirst = !curColumnElements.length;
|
|
|
- if (curElementIsFirst) {
|
|
|
- curColumnElements = [...curColumnElements, ...elementList];
|
|
|
- curColumnHeight += elementHeight;
|
|
|
- } else {
|
|
|
- columns.push([...curColumnElements]);
|
|
|
- initCurColumnElements();
|
|
|
- pushElement(element);
|
|
|
- }
|
|
|
- } else {
|
|
|
- curColumnElements = [...curColumnElements, ...elementList];
|
|
|
- curColumnHeight += elementHeight;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- // 批量添加所有元素。
|
|
|
- initCurColumnElements();
|
|
|
- state.topics.slice(1).forEach((element, eindex) => {
|
|
|
- element.elementSerialNo = eindex;
|
|
|
- pushElement(element);
|
|
|
- });
|
|
|
-
|
|
|
- // 最后一栏的处理。
|
|
|
- columns.push([...curColumnElements]);
|
|
|
- // 构建pages
|
|
|
- columns.forEach((column, cindex) => {
|
|
|
- const columnNo = cindex % columnNumber;
|
|
|
- if (!columnNo) {
|
|
|
- page = getNewPage(pages.length, { pageSize, columnNumber });
|
|
|
- }
|
|
|
- page.columns[columnNo].elements = column;
|
|
|
-
|
|
|
- if (columnNo + 1 === columnNumber || cindex === columns.length - 1) {
|
|
|
- pages.push(deepCopy(page));
|
|
|
- }
|
|
|
- });
|
|
|
- // 保证页面总是偶数页
|
|
|
- if (pages.length % 2) {
|
|
|
- pages.push(getNewPage(pages.length, { pageSize, columnNumber }));
|
|
|
- }
|
|
|
-
|
|
|
- commit("setPages", pages);
|
|
|
- commit("setCurPage", state.curPageNo);
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-export { fetchSameSerialNumberChildrenPositionInfo, checkElementisCovered };
|
|
|
-
|
|
|
-export default {
|
|
|
- namespaced: true,
|
|
|
- state,
|
|
|
- mutations,
|
|
|
- actions
|
|
|
-};
|
|
|
+import {
|
|
|
+ getExplainElements,
|
|
|
+ getFillQuesitonElements,
|
|
|
+ getFillLineElements,
|
|
|
+ getCompositionElements,
|
|
|
+ getNewPage,
|
|
|
+ getTopicHeadModel
|
|
|
+} from "../elementModel";
|
|
|
+import { objAssign, deepCopy, calcSum, getElementId } from "../plugins/utils";
|
|
|
+
|
|
|
+const state = {
|
|
|
+ cardConfig: {},
|
|
|
+ paperParams: {},
|
|
|
+ curElement: {},
|
|
|
+ curDragElement: {},
|
|
|
+ curPage: {},
|
|
|
+ curPageNo: 0,
|
|
|
+ pages: [],
|
|
|
+ topics: [],
|
|
|
+ topicSeries: [], // 大题顺序号
|
|
|
+ insetTarget: {}, // 需要在其后面插入大题的大题
|
|
|
+ openElementEditDialog: false
|
|
|
+};
|
|
|
+
|
|
|
+const mutations = {
|
|
|
+ setCardConfig(state, cardConfig) {
|
|
|
+ state.cardConfig = Object.assign({}, state.cardConfig, cardConfig);
|
|
|
+ },
|
|
|
+ setPaperParams(state, paperParams) {
|
|
|
+ state.paperParams = paperParams;
|
|
|
+ },
|
|
|
+ setCurElement(state, curElement) {
|
|
|
+ state.curElement = curElement;
|
|
|
+ },
|
|
|
+ setCurDragElement(state, curDragElement) {
|
|
|
+ state.curDragElement = curDragElement;
|
|
|
+ },
|
|
|
+ setCurPage(state, curPageNo) {
|
|
|
+ const pageNo = state.pages[curPageNo] ? curPageNo : 0;
|
|
|
+ state.curPage = state.pages[pageNo];
|
|
|
+ state.curPageNo = pageNo;
|
|
|
+ },
|
|
|
+ setCurPageNo(state, curPageNo) {
|
|
|
+ state.curPageNo = curPageNo;
|
|
|
+ },
|
|
|
+ setPages(state, pages) {
|
|
|
+ state.pages = pages;
|
|
|
+ },
|
|
|
+ setTopics(state, topics) {
|
|
|
+ state.topics = topics;
|
|
|
+ },
|
|
|
+ setTopicSeries(state, topicSeries) {
|
|
|
+ state.topicSeries = topicSeries;
|
|
|
+ },
|
|
|
+ setInsetTarget(state, insetTarget) {
|
|
|
+ state.insetTarget = insetTarget;
|
|
|
+ },
|
|
|
+ addPage(state, page) {
|
|
|
+ state.pages.push(page);
|
|
|
+ },
|
|
|
+ modifyPage(state, page) {
|
|
|
+ state.pages.splice(page._pageNo, 1, page);
|
|
|
+ },
|
|
|
+ setOpenElementEditDialog(state, openElementEditDialog) {
|
|
|
+ state.openElementEditDialog = openElementEditDialog;
|
|
|
+ },
|
|
|
+ initState(state) {
|
|
|
+ state.curElement = {};
|
|
|
+ state.curDragElement = {};
|
|
|
+ state.curPage = {};
|
|
|
+ state.curPageNo = 0;
|
|
|
+ state.topics = [];
|
|
|
+ state.pages = [];
|
|
|
+ state.cardConfig = {};
|
|
|
+ state.paperParams = {};
|
|
|
+ state.openElementEditDialog = false;
|
|
|
+ state.topicSeries = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const fetchElementPositionInfos = (element, topics) => {
|
|
|
+ return topics.findIndex(item => item.id === element.id);
|
|
|
+};
|
|
|
+
|
|
|
+const fetchAllRelateParentElementPositionInfos = (parentElement, topics) => {
|
|
|
+ // 当为解答题时,parentElement传入的值是EXPLAIN
|
|
|
+ let postionInfos = [];
|
|
|
+ topics.forEach((item, eindex) => {
|
|
|
+ if (item["parent"] && item.parent.id === parentElement.id) {
|
|
|
+ let pos = { _elementNo: eindex };
|
|
|
+ if (parentElement.type === "EXPLAIN") {
|
|
|
+ pos.serialNumber = item.serialNumber;
|
|
|
+ }
|
|
|
+ postionInfos.push(pos);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return postionInfos;
|
|
|
+};
|
|
|
+
|
|
|
+const fetchFirstSubjectiveTopicPositionInfo = topics => {
|
|
|
+ return topics.findIndex(item => item.sign === "subjective");
|
|
|
+};
|
|
|
+
|
|
|
+const fetchSameSerialNumberChildrenPositionInfo = (
|
|
|
+ elementChildernElement,
|
|
|
+ topics
|
|
|
+) => {
|
|
|
+ let postionInfos = [];
|
|
|
+ const elementId = elementChildernElement.parent.id;
|
|
|
+ const serialNumber = elementChildernElement.serialNumber;
|
|
|
+
|
|
|
+ topics.forEach((item, eindex) => {
|
|
|
+ if (
|
|
|
+ item.parent &&
|
|
|
+ item.parent.id === elementId &&
|
|
|
+ item.serialNumber === serialNumber
|
|
|
+ ) {
|
|
|
+ postionInfos.push({
|
|
|
+ _elementNo: eindex,
|
|
|
+ _elementId: item.id
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return postionInfos;
|
|
|
+};
|
|
|
+
|
|
|
+const groupByParams = (datas, paramKey) => {
|
|
|
+ let elementGroupInfos = [];
|
|
|
+ for (let i = 0, len = datas.length; i < len; i++) {
|
|
|
+ if (i === 0 || datas[i][paramKey] !== datas[i - 1][paramKey]) {
|
|
|
+ elementGroupInfos.push([datas[i]]);
|
|
|
+ } else {
|
|
|
+ elementGroupInfos[elementGroupInfos.length - 1].push(datas[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return elementGroupInfos;
|
|
|
+};
|
|
|
+
|
|
|
+const findElementById = (id, topics) => {
|
|
|
+ let curElement = null;
|
|
|
+ topics.forEach(element => {
|
|
|
+ if (curElement) return;
|
|
|
+ if (element.id === id) {
|
|
|
+ curElement = element;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (element["elements"]) {
|
|
|
+ element["elements"].forEach(elem => {
|
|
|
+ if (elem.id === id) curElement = elem;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return curElement;
|
|
|
+};
|
|
|
+
|
|
|
+const checkElementisCovered = (id, type) => {
|
|
|
+ const elementDom = document.getElementById(id);
|
|
|
+
|
|
|
+ if (type === "EXPLAIN") {
|
|
|
+ const elemTitleDome = elementDom.querySelector(".elem-title");
|
|
|
+ const limitHeight = elemTitleDome
|
|
|
+ ? elementDom.offsetHeight - elemTitleDome.offsetHeight
|
|
|
+ : elementDom.offsetHeight;
|
|
|
+
|
|
|
+ let elementHeights = [];
|
|
|
+ elementDom
|
|
|
+ .querySelector(".elem-explain-elements")
|
|
|
+ .childNodes.forEach(node => {
|
|
|
+ if (!node.className.includes("elem-explain-element")) return;
|
|
|
+ elementHeights.push(
|
|
|
+ node.firstChild.offsetHeight + node.firstChild.offsetTop
|
|
|
+ );
|
|
|
+ });
|
|
|
+ return elementHeights.some(item => item > limitHeight);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (type === "COMPOSITION") {
|
|
|
+ const elemTitleDome = elementDom.querySelector(".elem-title");
|
|
|
+ const limitHeight = elemTitleDome
|
|
|
+ ? elementDom.offsetHeight - elemTitleDome.offsetHeight
|
|
|
+ : elementDom.offsetHeight;
|
|
|
+
|
|
|
+ let elementHeights = [];
|
|
|
+ elementDom
|
|
|
+ .querySelector(".elem-composition-elements")
|
|
|
+ .childNodes.forEach(node => {
|
|
|
+ if (!node.className.includes("elem-composition-element")) return;
|
|
|
+ elementHeights.push(
|
|
|
+ node.firstChild.offsetHeight + node.firstChild.offsetTop
|
|
|
+ );
|
|
|
+ });
|
|
|
+ return elementHeights.some(item => item > limitHeight);
|
|
|
+ }
|
|
|
+
|
|
|
+ return elementDom.offsetHeight < elementDom.firstChild.offsetHeight;
|
|
|
+};
|
|
|
+
|
|
|
+const createFunc = {
|
|
|
+ EXPLAIN(element) {
|
|
|
+ return getExplainElements(element);
|
|
|
+ },
|
|
|
+ FILL_QUESTION(element) {
|
|
|
+ return getFillQuesitonElements(element, state.cardConfig);
|
|
|
+ },
|
|
|
+ FILL_LINE(element) {
|
|
|
+ return getFillLineElements(element);
|
|
|
+ },
|
|
|
+ COMPOSITION(element) {
|
|
|
+ return [getCompositionElements(element)];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const actions = {
|
|
|
+ initTopicsFromPages({ state, commit }) {
|
|
|
+ let topics = [];
|
|
|
+ state.pages.forEach(page => {
|
|
|
+ page.columns.forEach(column => {
|
|
|
+ column.elements.forEach(element => {
|
|
|
+ if (
|
|
|
+ element.type === "TOPIC_HEAD" ||
|
|
|
+ (element.type === "CARD_HEAD" && element.isSimple)
|
|
|
+ )
|
|
|
+ return;
|
|
|
+ topics.push(element);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ commit("setTopics", topics);
|
|
|
+ },
|
|
|
+ actElementById({ state, commit }, id) {
|
|
|
+ const curElement = findElementById(id, state.topics);
|
|
|
+ if (!curElement) return;
|
|
|
+
|
|
|
+ commit("setCurElement", curElement);
|
|
|
+ },
|
|
|
+ resetTopicSeries({ state, commit }) {
|
|
|
+ let curTopicId = "",
|
|
|
+ curTopicNo = 0,
|
|
|
+ topicSeries = [];
|
|
|
+ state.topics.forEach(topic => {
|
|
|
+ if (!topic.parent) return;
|
|
|
+ if (curTopicId !== topic.parent.id) {
|
|
|
+ curTopicId = topic.parent.id;
|
|
|
+ curTopicNo++;
|
|
|
+ topicSeries.push({
|
|
|
+ id: curTopicId,
|
|
|
+ topicNo: curTopicNo,
|
|
|
+ type: topic.type,
|
|
|
+ sign: topic.sign
|
|
|
+ });
|
|
|
+ }
|
|
|
+ topic.topicNo = curTopicNo;
|
|
|
+ });
|
|
|
+ commit("setTopicSeries", topicSeries);
|
|
|
+ },
|
|
|
+ // 新增试题 --------------->
|
|
|
+ addElement({ state, commit, dispatch }, element) {
|
|
|
+ let pos = null;
|
|
|
+ // 客观题和主观题分别对待
|
|
|
+ if (state.insetTarget.id) {
|
|
|
+ // 存在插入目标元素
|
|
|
+ if (
|
|
|
+ state.insetTarget.type === "FILL_QUESTION" &&
|
|
|
+ element.type !== "FILL_QUESTION"
|
|
|
+ ) {
|
|
|
+ pos = fetchFirstSubjectiveTopicPositionInfo(state.topics);
|
|
|
+ } else {
|
|
|
+ let relateTopicPos = [];
|
|
|
+ state.topics.forEach((item, index) => {
|
|
|
+ if (item.parent && item.parent.id === state.insetTarget.id)
|
|
|
+ relateTopicPos.push(index);
|
|
|
+ });
|
|
|
+ pos = relateTopicPos.slice(-1)[0] + 1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 不存在插入目标元素
|
|
|
+ if (element.sign === "objective") {
|
|
|
+ pos = fetchFirstSubjectiveTopicPositionInfo(state.topics);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let preElements = createFunc[element.type](element);
|
|
|
+ preElements.forEach((preElement, index) => {
|
|
|
+ if (pos && pos !== -1) {
|
|
|
+ state.topics.splice(pos + index, 0, preElement);
|
|
|
+ } else {
|
|
|
+ state.topics.push(preElement);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ dispatch("resetTopicSeries");
|
|
|
+ commit("setInsetTarget", {});
|
|
|
+
|
|
|
+ commit("setCurElement", element);
|
|
|
+ },
|
|
|
+ // 修改试题 --------------->
|
|
|
+ modifyTopic({ state }, element) {
|
|
|
+ // 单独编辑某个细分题
|
|
|
+ const pos = fetchElementPositionInfos(element, state.topics);
|
|
|
+ state.topics.splice(pos, 1, element);
|
|
|
+ },
|
|
|
+ modifyComposition({ state }, element) {
|
|
|
+ const positionInfos = fetchAllRelateParentElementPositionInfos(
|
|
|
+ element,
|
|
|
+ state.topics
|
|
|
+ );
|
|
|
+ positionInfos.forEach(({ _elementNo }) => {
|
|
|
+ state.topics[_elementNo].topicNo = element.topicNo;
|
|
|
+ state.topics[_elementNo].parent = objAssign(
|
|
|
+ state.topics[_elementNo].parent,
|
|
|
+ element
|
|
|
+ );
|
|
|
+ });
|
|
|
+ },
|
|
|
+ modifyExplain({ state }, element) {
|
|
|
+ // 解答题既是拆分题,又是可复制题
|
|
|
+ const positionInfos = fetchAllRelateParentElementPositionInfos(
|
|
|
+ element,
|
|
|
+ state.topics
|
|
|
+ );
|
|
|
+ const elementGroupPosInfos = groupByParams(positionInfos, "serialNumber");
|
|
|
+ const orgElementCount = elementGroupPosInfos.length;
|
|
|
+ if (orgElementCount > element.questionsCount) {
|
|
|
+ // 原小题数多于新小题数,要删除原多于的小题;
|
|
|
+ let needDeleteInfos = elementGroupPosInfos.splice(
|
|
|
+ element.questionsCount,
|
|
|
+ orgElementCount - element.questionsCount
|
|
|
+ );
|
|
|
+ needDeleteInfos.reverse().forEach(item => {
|
|
|
+ item.reverse().forEach(pos => {
|
|
|
+ state.topics.splice(pos._elementNo, 1);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ const newElements = getExplainElements(element);
|
|
|
+ const lastPos = elementGroupPosInfos.slice(-1)[0].slice(-1)[0];
|
|
|
+ let lastNewElementPos = lastPos._elementNo;
|
|
|
+ for (let i = 0; i < element.questionsCount; i++) {
|
|
|
+ if (elementGroupPosInfos[i]) {
|
|
|
+ elementGroupPosInfos[i].forEach(pos => {
|
|
|
+ let child = state.topics[pos._elementNo];
|
|
|
+ child.serialNumber = i + element.startNumber;
|
|
|
+ child.parent = { ...element };
|
|
|
+ child.topicNo = element.topicNo;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ state.topics.splice(++lastNewElementPos, 0, newElements[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ modifySplitTopic({ state }, element) {
|
|
|
+ // 非作文题都是拆分题,即同一个题拆分成多个小题展示
|
|
|
+ const positionInfos = fetchAllRelateParentElementPositionInfos(
|
|
|
+ element,
|
|
|
+ state.topics
|
|
|
+ );
|
|
|
+ // 缓存已编辑的小题高度信息。
|
|
|
+ // const elementHeights = positionInfos.map(
|
|
|
+ // pos => state.topics[pos._elementNo].h
|
|
|
+ // );
|
|
|
+ // 删除所有小题
|
|
|
+ positionInfos.reverse().forEach(pos => {
|
|
|
+ state.topics.splice(pos._elementNo, 1);
|
|
|
+ });
|
|
|
+ // 创建新的小题元素
|
|
|
+ const newElements = createFunc[element.type](element);
|
|
|
+ const pos = positionInfos.pop();
|
|
|
+ newElements.forEach((newElement, index) => {
|
|
|
+ // 不再复用缓存高度,修改于2022-03-10 09:23
|
|
|
+ // newElement.h = Math.max(elementHeights[index] || 0, newElement.h);
|
|
|
+ state.topics.splice(pos._elementNo + index, 0, newElement);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ modifyElement({ commit, dispatch }, element) {
|
|
|
+ if (element.type === "COMPOSITION") {
|
|
|
+ dispatch("modifyComposition", element);
|
|
|
+ } else if (element.type === "EXPLAIN") {
|
|
|
+ dispatch("modifyExplain", element);
|
|
|
+ } else {
|
|
|
+ dispatch("modifySplitTopic", element);
|
|
|
+ }
|
|
|
+ dispatch("resetTopicSeries");
|
|
|
+ commit("setCurElement", element);
|
|
|
+ },
|
|
|
+ modifyCardHead({ state }, element) {
|
|
|
+ state.topics.splice(0, 1, element);
|
|
|
+ },
|
|
|
+ // 修改试题包含元素
|
|
|
+ modifyElementChild({ state, commit }, element) {
|
|
|
+ // 修改解答题小题和作文题的子元素
|
|
|
+ const pos = fetchElementPositionInfos(element.container, state.topics);
|
|
|
+ const columnElements = state.topics[pos].elements;
|
|
|
+ const childIndex = columnElements.findIndex(item => item.id === element.id);
|
|
|
+ element.id = getElementId();
|
|
|
+ // 作文题中的多线条和网格元素重新计算高度。
|
|
|
+ if (element.container.type === "COMPOSITION") {
|
|
|
+ if (element.type === "LINES") {
|
|
|
+ element.h = element.lineCount * (element.lineSpacing + 1);
|
|
|
+ }
|
|
|
+ if (element.type === "GRIDS") {
|
|
|
+ if (element.halving) {
|
|
|
+ const columnWs = {
|
|
|
+ 2: 703,
|
|
|
+ 3: 461,
|
|
|
+ 4: 349
|
|
|
+ };
|
|
|
+ const columnSize =
|
|
|
+ columnWs[state.cardConfig.columnNumber] / element.columnCount;
|
|
|
+ element.h = element.rowCount * (columnSize + element.rowSpace) + 1;
|
|
|
+ } else {
|
|
|
+ element.h =
|
|
|
+ element.rowCount * (element.columnSize + element.rowSpace) + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (childIndex === -1) {
|
|
|
+ columnElements.push(element);
|
|
|
+ } else {
|
|
|
+ columnElements.splice(childIndex, 1, element);
|
|
|
+ }
|
|
|
+
|
|
|
+ commit("setCurElement", element);
|
|
|
+ },
|
|
|
+ // 粘贴试题内的元素
|
|
|
+ pasteExplainElementChild({ state }, { curElement, pasteElement }) {
|
|
|
+ let element = {
|
|
|
+ id: curElement.container ? curElement.container.id : curElement.id
|
|
|
+ };
|
|
|
+ const pos = fetchElementPositionInfos(element, state.topics);
|
|
|
+ if (pos === -1) return;
|
|
|
+
|
|
|
+ element = state.topics[pos];
|
|
|
+ const newElement = Object.assign({}, pasteElement, {
|
|
|
+ id: getElementId(),
|
|
|
+ container: {
|
|
|
+ id: element.id,
|
|
|
+ type: element.type
|
|
|
+ }
|
|
|
+ });
|
|
|
+ element.elements.push(newElement);
|
|
|
+ },
|
|
|
+ // 删除试题 --------------->
|
|
|
+ removeElement({ state, commit, dispatch }, element) {
|
|
|
+ const positionInfos = fetchAllRelateParentElementPositionInfos(
|
|
|
+ element.parent,
|
|
|
+ state.topics
|
|
|
+ );
|
|
|
+ positionInfos.reverse().forEach(pos => {
|
|
|
+ state.topics.splice(pos._elementNo, 1);
|
|
|
+ });
|
|
|
+
|
|
|
+ dispatch("resetTopicSeries");
|
|
|
+
|
|
|
+ commit("setCurElement", {});
|
|
|
+ },
|
|
|
+ // 删除试题包含元素 --------------->
|
|
|
+ removeElementChild({ state, commit }, element) {
|
|
|
+ // 删除解答题小题和作文题的子元素
|
|
|
+ const pos = fetchElementPositionInfos(element.container, state.topics);
|
|
|
+ const columnElements = state.topics[pos].elements;
|
|
|
+ const childIndex = columnElements.findIndex(item => item.id === element.id);
|
|
|
+ columnElements.splice(childIndex, 1);
|
|
|
+
|
|
|
+ commit("setCurElement", {});
|
|
|
+ },
|
|
|
+ // 扩展答题区操作 --------------->
|
|
|
+ copyExplainChildren({ state }, element) {
|
|
|
+ let curElement = {
|
|
|
+ id: element.container ? element.container.id : element.id
|
|
|
+ };
|
|
|
+ const pos = fetchElementPositionInfos(curElement, state.topics);
|
|
|
+ curElement = state.topics[pos];
|
|
|
+
|
|
|
+ let newElement = Object.assign({}, curElement, {
|
|
|
+ id: getElementId(),
|
|
|
+ elements: [],
|
|
|
+ h: 200,
|
|
|
+ isExtend: true,
|
|
|
+ showTitle: false
|
|
|
+ });
|
|
|
+
|
|
|
+ state.topics.splice(pos + 1, 0, newElement);
|
|
|
+ // 更新小题答题区isLast
|
|
|
+ let positionInfos = fetchSameSerialNumberChildrenPositionInfo(
|
|
|
+ curElement,
|
|
|
+ state.topics
|
|
|
+ );
|
|
|
+ positionInfos.forEach((pos, pindex) => {
|
|
|
+ state.topics[pos._elementNo].isLast = pindex + 1 === positionInfos.length;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ deleteExplainChildren({ state }, element) {
|
|
|
+ let curElement = {
|
|
|
+ id: element.container ? element.container.id : element.id
|
|
|
+ };
|
|
|
+ const curPos = fetchElementPositionInfos(curElement, state.topics);
|
|
|
+ curElement = state.topics[curPos];
|
|
|
+
|
|
|
+ let positionInfos = fetchSameSerialNumberChildrenPositionInfo(
|
|
|
+ curElement,
|
|
|
+ state.topics
|
|
|
+ );
|
|
|
+ if (positionInfos.length < 2) return;
|
|
|
+ const pindex = positionInfos.findIndex(
|
|
|
+ item => item._elementId === curElement.id
|
|
|
+ );
|
|
|
+ const pos = positionInfos[pindex]._elementNo;
|
|
|
+ positionInfos.splice(pindex, 1);
|
|
|
+ const nextPos = positionInfos[0]._elementNo;
|
|
|
+ // 当删除的是非扩展区域时,则下一个答题区要被设置成非扩展区
|
|
|
+ if (!curElement.isExtend) {
|
|
|
+ state.topics[nextPos].isExtend = false;
|
|
|
+ }
|
|
|
+ // 当删除的是含有标题答题区时,则需要将下一个答题区开启显示标题。
|
|
|
+ if (curElement.showTitle) {
|
|
|
+ state.topics[nextPos].showTitle = true;
|
|
|
+ }
|
|
|
+ state.topics.splice(pos, 1);
|
|
|
+ // 更新小题答题区isLast
|
|
|
+ positionInfos = fetchSameSerialNumberChildrenPositionInfo(
|
|
|
+ curElement,
|
|
|
+ state.topics
|
|
|
+ );
|
|
|
+ positionInfos.forEach((pos, pindex) => {
|
|
|
+ state.topics[pos._elementNo].isLast = pindex + 1 === positionInfos.length;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 大题顺序操作 --------------->
|
|
|
+ topicMoveUp({ state, dispatch }, topicId) {
|
|
|
+ const curTopicPos = state.topicSeries.findIndex(
|
|
|
+ item => item.id === topicId
|
|
|
+ );
|
|
|
+ const prevTopicId = state.topicSeries[curTopicPos - 1].id;
|
|
|
+ let relateTopicPos = [];
|
|
|
+ state.topics.forEach((item, index) => {
|
|
|
+ if (item.parent && item.parent.id === topicId) relateTopicPos.push(index);
|
|
|
+ });
|
|
|
+ const prevTopicFirstIndex = state.topics.findIndex(
|
|
|
+ item => item.parent && item.parent.id === prevTopicId
|
|
|
+ );
|
|
|
+ const relateTopics = state.topics.splice(
|
|
|
+ relateTopicPos[0],
|
|
|
+ relateTopicPos.length
|
|
|
+ );
|
|
|
+ relateTopics.reverse().forEach(topic => {
|
|
|
+ state.topics.splice(prevTopicFirstIndex, 0, topic);
|
|
|
+ });
|
|
|
+
|
|
|
+ dispatch("resetTopicSeries");
|
|
|
+ },
|
|
|
+ // 重构页面
|
|
|
+ resetElementProp({ state }, isResetId = false) {
|
|
|
+ state.topics.forEach(element => {
|
|
|
+ const elementDom = document.getElementById(element.id);
|
|
|
+ if (elementDom) {
|
|
|
+ element.h = elementDom.offsetHeight;
|
|
|
+ element.w = elementDom.offsetWidth;
|
|
|
+ }
|
|
|
+ if (isResetId) {
|
|
|
+ element.id = getElementId();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ rebuildPages({ state, commit }) {
|
|
|
+ const columnNumber = state.cardConfig.columnNumber;
|
|
|
+ const pageSize = state.cardConfig.pageSize;
|
|
|
+ // 更新元件最新的高度信息
|
|
|
+ // 整理所有元件
|
|
|
+ const cardHeadElement = state.topics[0];
|
|
|
+ state.topics.forEach(element => {
|
|
|
+ const elementDom = document.getElementById(`preview-${element.id}`);
|
|
|
+
|
|
|
+ if (elementDom) {
|
|
|
+ element.h = elementDom.offsetHeight;
|
|
|
+ element.w = elementDom.offsetWidth;
|
|
|
+ // 解答题小题与其他题有些区别。
|
|
|
+ // 其他题都是通过内部子元素自动撑高元件,而解答题则需要手动设置高度。
|
|
|
+ const ESCAPE_ELEMENTS = ["CARD_HEAD"];
|
|
|
+ element.isCovered =
|
|
|
+ !ESCAPE_ELEMENTS.includes(element.type) &&
|
|
|
+ checkElementisCovered(`preview-${element.id}`, element.type);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 动态计算每列可以分配的元件
|
|
|
+ const columnHeight = document.getElementById("topic-column").offsetHeight;
|
|
|
+ const simpleCardHeadHeight = document.getElementById("simple-card-head")
|
|
|
+ .offsetHeight;
|
|
|
+ let pages = [];
|
|
|
+ let page = {};
|
|
|
+ let columns = [];
|
|
|
+ let curColumnElements = [];
|
|
|
+ let curColumnHeight = 0;
|
|
|
+
|
|
|
+ const initCurColumnElements = () => {
|
|
|
+ curColumnElements = [];
|
|
|
+ curColumnHeight = 0;
|
|
|
+ const groupColumnNumber = columnNumber * 2;
|
|
|
+ // 奇数页第一栏;
|
|
|
+ if (!(columns.length % groupColumnNumber)) {
|
|
|
+ // 非第一页奇数页第一栏
|
|
|
+ if (columns.length) {
|
|
|
+ const cardHeadSimpleElement = Object.assign({}, cardHeadElement, {
|
|
|
+ id: getElementId(),
|
|
|
+ isSimple: true,
|
|
|
+ h: simpleCardHeadHeight
|
|
|
+ });
|
|
|
+ curColumnElements.push(cardHeadSimpleElement);
|
|
|
+ curColumnHeight += simpleCardHeadHeight;
|
|
|
+ } else {
|
|
|
+ curColumnElements.push(cardHeadElement);
|
|
|
+ curColumnHeight += cardHeadElement.h;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const checkElementIsCurColumnFirstType = element => {
|
|
|
+ const topicHeadIndex = curColumnElements.findIndex(
|
|
|
+ elem => elem.type === "TOPIC_HEAD" && elem.sign === element.sign
|
|
|
+ );
|
|
|
+ return topicHeadIndex === -1;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 放入元素通用流程
|
|
|
+ const pushElement = element => {
|
|
|
+ // 当前栏中第一个题型之前新增题型头元素(topic-head)。
|
|
|
+ // 题型头和当前题要组合加入栏中,不可拆分。
|
|
|
+ let elementList = [element];
|
|
|
+ if (checkElementIsCurColumnFirstType(element)) {
|
|
|
+ elementList.unshift(
|
|
|
+ getTopicHeadModel(
|
|
|
+ state.cardConfig[`${element.sign}Attention`],
|
|
|
+ element.sign,
|
|
|
+ !curColumnElements.length
|
|
|
+ )
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ const elementHeight = calcSum(elementList.map(elem => elem.h));
|
|
|
+ if (curColumnHeight + elementHeight > columnHeight) {
|
|
|
+ // 当前栏第一个元素高度超过一栏时,不拆分,直接放在当前栏。
|
|
|
+ // 解决可能空栏的情况
|
|
|
+ const curElementIsFirst = !curColumnElements.length;
|
|
|
+ if (curElementIsFirst) {
|
|
|
+ curColumnElements = [...curColumnElements, ...elementList];
|
|
|
+ curColumnHeight += elementHeight;
|
|
|
+ } else {
|
|
|
+ columns.push([...curColumnElements]);
|
|
|
+ initCurColumnElements();
|
|
|
+ pushElement(element);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ curColumnElements = [...curColumnElements, ...elementList];
|
|
|
+ curColumnHeight += elementHeight;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 批量添加所有元素。
|
|
|
+ initCurColumnElements();
|
|
|
+ state.topics.slice(1).forEach((element, eindex) => {
|
|
|
+ element.elementSerialNo = eindex;
|
|
|
+ pushElement(element);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 最后一栏的处理。
|
|
|
+ columns.push([...curColumnElements]);
|
|
|
+ // 构建pages
|
|
|
+ columns.forEach((column, cindex) => {
|
|
|
+ const columnNo = cindex % columnNumber;
|
|
|
+ if (!columnNo) {
|
|
|
+ page = getNewPage(pages.length, { pageSize, columnNumber });
|
|
|
+ }
|
|
|
+ page.columns[columnNo].elements = column;
|
|
|
+
|
|
|
+ if (columnNo + 1 === columnNumber || cindex === columns.length - 1) {
|
|
|
+ pages.push(deepCopy(page));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 保证页面总是偶数页
|
|
|
+ if (pages.length % 2) {
|
|
|
+ pages.push(getNewPage(pages.length, { pageSize, columnNumber }));
|
|
|
+ }
|
|
|
+
|
|
|
+ commit("setPages", pages);
|
|
|
+ commit("setCurPage", state.curPageNo);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+export { fetchSameSerialNumberChildrenPositionInfo, checkElementisCovered };
|
|
|
+
|
|
|
+export default {
|
|
|
+ namespaced: true,
|
|
|
+ state,
|
|
|
+ mutations,
|
|
|
+ actions
|
|
|
+};
|