123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698 |
- 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
- };
|