card.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. import {
  2. getExplainElements,
  3. getFillQuesitonElements,
  4. getFillLineElements,
  5. getCompositionElements,
  6. getNewPage,
  7. getTopicHeadModel
  8. } from "../elementModel";
  9. import { objAssign, deepCopy, calcSum, getElementId } from "../plugins/utils";
  10. const state = {
  11. cardConfig: {},
  12. paperParams: {},
  13. curElement: {},
  14. curDragElement: {},
  15. curPage: {},
  16. curPageNo: 0,
  17. pages: [],
  18. topics: [],
  19. topicSeries: [], // 大题顺序号
  20. insetTarget: {}, // 需要在其后面插入大题的大题
  21. openElementEditDialog: false
  22. };
  23. const mutations = {
  24. setCardConfig(state, cardConfig) {
  25. state.cardConfig = Object.assign({}, state.cardConfig, cardConfig);
  26. },
  27. setPaperParams(state, paperParams) {
  28. state.paperParams = paperParams;
  29. },
  30. setCurElement(state, curElement) {
  31. state.curElement = curElement;
  32. },
  33. setCurDragElement(state, curDragElement) {
  34. state.curDragElement = curDragElement;
  35. },
  36. setCurPage(state, curPageNo) {
  37. const pageNo = state.pages[curPageNo] ? curPageNo : 0;
  38. state.curPage = state.pages[pageNo];
  39. state.curPageNo = pageNo;
  40. },
  41. setCurPageNo(state, curPageNo) {
  42. state.curPageNo = curPageNo;
  43. },
  44. setPages(state, pages) {
  45. state.pages = pages;
  46. },
  47. setTopics(state, topics) {
  48. state.topics = topics;
  49. },
  50. setTopicSeries(state, topicSeries) {
  51. state.topicSeries = topicSeries;
  52. },
  53. setInsetTarget(state, insetTarget) {
  54. state.insetTarget = insetTarget;
  55. },
  56. addPage(state, page) {
  57. state.pages.push(page);
  58. },
  59. modifyPage(state, page) {
  60. state.pages.splice(page._pageNo, 1, page);
  61. },
  62. setOpenElementEditDialog(state, openElementEditDialog) {
  63. state.openElementEditDialog = openElementEditDialog;
  64. },
  65. initState(state) {
  66. state.curElement = {};
  67. state.curDragElement = {};
  68. state.curPage = {};
  69. state.curPageNo = 0;
  70. state.topics = [];
  71. state.pages = [];
  72. state.cardConfig = {};
  73. state.paperParams = {};
  74. state.openElementEditDialog = false;
  75. state.topicSeries = [];
  76. }
  77. };
  78. const fetchElementPositionInfos = (element, topics) => {
  79. return topics.findIndex(item => item.id === element.id);
  80. };
  81. const fetchAllRelateParentElementPositionInfos = (parentElement, topics) => {
  82. // 当为解答题时,parentElement传入的值是EXPLAIN
  83. let postionInfos = [];
  84. topics.forEach((item, eindex) => {
  85. if (item["parent"] && item.parent.id === parentElement.id) {
  86. let pos = { _elementNo: eindex };
  87. if (parentElement.type === "EXPLAIN") {
  88. pos.serialNumber = item.serialNumber;
  89. }
  90. postionInfos.push(pos);
  91. }
  92. });
  93. return postionInfos;
  94. };
  95. const fetchFirstSubjectiveTopicPositionInfo = topics => {
  96. return topics.findIndex(item => item.sign === "subjective");
  97. };
  98. const fetchSameSerialNumberChildrenPositionInfo = (
  99. elementChildernElement,
  100. topics
  101. ) => {
  102. let postionInfos = [];
  103. const elementId = elementChildernElement.parent.id;
  104. const serialNumber = elementChildernElement.serialNumber;
  105. topics.forEach((item, eindex) => {
  106. if (
  107. item.parent &&
  108. item.parent.id === elementId &&
  109. item.serialNumber === serialNumber
  110. ) {
  111. postionInfos.push({
  112. _elementNo: eindex,
  113. _elementId: item.id
  114. });
  115. }
  116. });
  117. return postionInfos;
  118. };
  119. const groupByParams = (datas, paramKey) => {
  120. let elementGroupInfos = [];
  121. for (let i = 0, len = datas.length; i < len; i++) {
  122. if (i === 0 || datas[i][paramKey] !== datas[i - 1][paramKey]) {
  123. elementGroupInfos.push([datas[i]]);
  124. } else {
  125. elementGroupInfos[elementGroupInfos.length - 1].push(datas[i]);
  126. }
  127. }
  128. return elementGroupInfos;
  129. };
  130. const findElementById = (id, topics) => {
  131. let curElement = null;
  132. topics.forEach(element => {
  133. if (curElement) return;
  134. if (element.id === id) {
  135. curElement = element;
  136. return;
  137. }
  138. if (element["elements"]) {
  139. element["elements"].forEach(elem => {
  140. if (elem.id === id) curElement = elem;
  141. });
  142. }
  143. });
  144. return curElement;
  145. };
  146. const checkElementisCovered = (id, type) => {
  147. const elementDom = document.getElementById(id);
  148. if (type === "EXPLAIN") {
  149. const elemTitleDome = elementDom.querySelector(".elem-title");
  150. const limitHeight = elemTitleDome
  151. ? elementDom.offsetHeight - elemTitleDome.offsetHeight
  152. : elementDom.offsetHeight;
  153. let elementHeights = [];
  154. elementDom
  155. .querySelector(".elem-explain-elements")
  156. .childNodes.forEach(node => {
  157. if (!node.className.includes("elem-explain-element")) return;
  158. elementHeights.push(
  159. node.firstChild.offsetHeight + node.firstChild.offsetTop
  160. );
  161. });
  162. return elementHeights.some(item => item > limitHeight);
  163. }
  164. if (type === "COMPOSITION") {
  165. const elemTitleDome = elementDom.querySelector(".elem-title");
  166. const limitHeight = elemTitleDome
  167. ? elementDom.offsetHeight - elemTitleDome.offsetHeight
  168. : elementDom.offsetHeight;
  169. let elementHeights = [];
  170. elementDom
  171. .querySelector(".elem-composition-elements")
  172. .childNodes.forEach(node => {
  173. if (!node.className.includes("elem-composition-element")) return;
  174. elementHeights.push(
  175. node.firstChild.offsetHeight + node.firstChild.offsetTop
  176. );
  177. });
  178. return elementHeights.some(item => item > limitHeight);
  179. }
  180. return elementDom.offsetHeight < elementDom.firstChild.offsetHeight;
  181. };
  182. const createFunc = {
  183. EXPLAIN(element) {
  184. return getExplainElements(element);
  185. },
  186. FILL_QUESTION(element) {
  187. return getFillQuesitonElements(element, state.cardConfig);
  188. },
  189. FILL_LINE(element) {
  190. return getFillLineElements(element);
  191. },
  192. COMPOSITION(element) {
  193. return [getCompositionElements(element)];
  194. }
  195. };
  196. const actions = {
  197. initTopicsFromPages({ state, commit }) {
  198. let topics = [];
  199. state.pages.forEach(page => {
  200. page.columns.forEach(column => {
  201. column.elements.forEach(element => {
  202. if (
  203. element.type === "TOPIC_HEAD" ||
  204. (element.type === "CARD_HEAD" && element.isSimple)
  205. )
  206. return;
  207. topics.push(element);
  208. });
  209. });
  210. });
  211. commit("setTopics", topics);
  212. },
  213. actElementById({ state, commit }, id) {
  214. const curElement = findElementById(id, state.topics);
  215. if (!curElement) return;
  216. commit("setCurElement", curElement);
  217. },
  218. resetTopicSeries({ state, commit }) {
  219. let curTopicId = "",
  220. curTopicNo = 0,
  221. topicSeries = [];
  222. state.topics.forEach(topic => {
  223. if (!topic.parent) return;
  224. if (curTopicId !== topic.parent.id) {
  225. curTopicId = topic.parent.id;
  226. curTopicNo++;
  227. topicSeries.push({
  228. id: curTopicId,
  229. topicNo: curTopicNo,
  230. type: topic.type,
  231. sign: topic.sign
  232. });
  233. }
  234. topic.topicNo = curTopicNo;
  235. });
  236. commit("setTopicSeries", topicSeries);
  237. },
  238. // 新增试题 --------------->
  239. addElement({ state, commit, dispatch }, element) {
  240. let pos = null;
  241. // 客观题和主观题分别对待
  242. if (state.insetTarget.id) {
  243. // 存在插入目标元素
  244. if (
  245. state.insetTarget.type === "FILL_QUESTION" &&
  246. element.type !== "FILL_QUESTION"
  247. ) {
  248. pos = fetchFirstSubjectiveTopicPositionInfo(state.topics);
  249. } else {
  250. let relateTopicPos = [];
  251. state.topics.forEach((item, index) => {
  252. if (item.parent && item.parent.id === state.insetTarget.id)
  253. relateTopicPos.push(index);
  254. });
  255. pos = relateTopicPos.slice(-1)[0] + 1;
  256. }
  257. } else {
  258. // 不存在插入目标元素
  259. if (element.sign === "objective") {
  260. pos = fetchFirstSubjectiveTopicPositionInfo(state.topics);
  261. }
  262. }
  263. let preElements = createFunc[element.type](element);
  264. preElements.forEach((preElement, index) => {
  265. if (pos && pos !== -1) {
  266. state.topics.splice(pos + index, 0, preElement);
  267. } else {
  268. state.topics.push(preElement);
  269. }
  270. });
  271. dispatch("resetTopicSeries");
  272. commit("setInsetTarget", {});
  273. commit("setCurElement", element);
  274. },
  275. // 修改试题 --------------->
  276. modifyTopic({ state }, element) {
  277. // 单独编辑某个细分题
  278. const pos = fetchElementPositionInfos(element, state.topics);
  279. state.topics.splice(pos, 1, element);
  280. },
  281. modifyComposition({ state }, element) {
  282. const positionInfos = fetchAllRelateParentElementPositionInfos(
  283. element,
  284. state.topics
  285. );
  286. positionInfos.forEach(({ _elementNo }) => {
  287. state.topics[_elementNo].topicNo = element.topicNo;
  288. state.topics[_elementNo].parent = objAssign(
  289. state.topics[_elementNo].parent,
  290. element
  291. );
  292. });
  293. },
  294. modifyExplain({ state }, element) {
  295. // 解答题既是拆分题,又是可复制题
  296. const positionInfos = fetchAllRelateParentElementPositionInfos(
  297. element,
  298. state.topics
  299. );
  300. const elementGroupPosInfos = groupByParams(positionInfos, "serialNumber");
  301. const orgElementCount = elementGroupPosInfos.length;
  302. if (orgElementCount > element.questionsCount) {
  303. // 原小题数多于新小题数,要删除原多于的小题;
  304. let needDeleteInfos = elementGroupPosInfos.splice(
  305. element.questionsCount,
  306. orgElementCount - element.questionsCount
  307. );
  308. needDeleteInfos.reverse().forEach(item => {
  309. item.reverse().forEach(pos => {
  310. state.topics.splice(pos._elementNo, 1);
  311. });
  312. });
  313. }
  314. const newElements = getExplainElements(element);
  315. const lastPos = elementGroupPosInfos.slice(-1)[0].slice(-1)[0];
  316. let lastNewElementPos = lastPos._elementNo;
  317. for (let i = 0; i < element.questionsCount; i++) {
  318. if (elementGroupPosInfos[i]) {
  319. elementGroupPosInfos[i].forEach(pos => {
  320. let child = state.topics[pos._elementNo];
  321. child.serialNumber = i + element.startNumber;
  322. child.parent = { ...element };
  323. child.topicNo = element.topicNo;
  324. });
  325. } else {
  326. state.topics.splice(++lastNewElementPos, 0, newElements[i]);
  327. }
  328. }
  329. },
  330. modifySplitTopic({ state }, element) {
  331. // 非作文题都是拆分题,即同一个题拆分成多个小题展示
  332. const positionInfos = fetchAllRelateParentElementPositionInfos(
  333. element,
  334. state.topics
  335. );
  336. // 缓存已编辑的小题高度信息。
  337. // const elementHeights = positionInfos.map(
  338. // pos => state.topics[pos._elementNo].h
  339. // );
  340. // 删除所有小题
  341. positionInfos.reverse().forEach(pos => {
  342. state.topics.splice(pos._elementNo, 1);
  343. });
  344. // 创建新的小题元素
  345. const newElements = createFunc[element.type](element);
  346. const pos = positionInfos.pop();
  347. newElements.forEach((newElement, index) => {
  348. // 不再复用缓存高度,修改于2022-03-10 09:23
  349. // newElement.h = Math.max(elementHeights[index] || 0, newElement.h);
  350. state.topics.splice(pos._elementNo + index, 0, newElement);
  351. });
  352. },
  353. modifyElement({ commit, dispatch }, element) {
  354. if (element.type === "COMPOSITION") {
  355. dispatch("modifyComposition", element);
  356. } else if (element.type === "EXPLAIN") {
  357. dispatch("modifyExplain", element);
  358. } else {
  359. dispatch("modifySplitTopic", element);
  360. }
  361. dispatch("resetTopicSeries");
  362. commit("setCurElement", element);
  363. },
  364. modifyCardHead({ state }, element) {
  365. state.topics.splice(0, 1, element);
  366. },
  367. // 修改试题包含元素
  368. modifyElementChild({ state, commit }, element) {
  369. // 修改解答题小题和作文题的子元素
  370. const pos = fetchElementPositionInfos(element.container, state.topics);
  371. const columnElements = state.topics[pos].elements;
  372. const childIndex = columnElements.findIndex(item => item.id === element.id);
  373. element.id = getElementId();
  374. // 作文题中的多线条和网格元素重新计算高度。
  375. if (element.container.type === "COMPOSITION") {
  376. if (element.type === "LINES") {
  377. element.h = element.lineCount * (element.lineSpacing + 1);
  378. }
  379. if (element.type === "GRIDS") {
  380. if (element.halving) {
  381. const columnWs = {
  382. 2: 703,
  383. 3: 461,
  384. 4: 349
  385. };
  386. const columnSize =
  387. columnWs[state.cardConfig.columnNumber] / element.columnCount;
  388. element.h = element.rowCount * (columnSize + element.rowSpace) + 1;
  389. } else {
  390. element.h =
  391. element.rowCount * (element.columnSize + element.rowSpace) + 1;
  392. }
  393. }
  394. }
  395. if (childIndex === -1) {
  396. columnElements.push(element);
  397. } else {
  398. columnElements.splice(childIndex, 1, element);
  399. }
  400. commit("setCurElement", element);
  401. },
  402. // 粘贴试题内的元素
  403. pasteExplainElementChild({ state }, { curElement, pasteElement }) {
  404. let element = {
  405. id: curElement.container ? curElement.container.id : curElement.id
  406. };
  407. const pos = fetchElementPositionInfos(element, state.topics);
  408. if (pos === -1) return;
  409. element = state.topics[pos];
  410. const newElement = Object.assign({}, pasteElement, {
  411. id: getElementId(),
  412. container: {
  413. id: element.id,
  414. type: element.type
  415. }
  416. });
  417. element.elements.push(newElement);
  418. },
  419. // 删除试题 --------------->
  420. removeElement({ state, commit, dispatch }, element) {
  421. const positionInfos = fetchAllRelateParentElementPositionInfos(
  422. element.parent,
  423. state.topics
  424. );
  425. positionInfos.reverse().forEach(pos => {
  426. state.topics.splice(pos._elementNo, 1);
  427. });
  428. dispatch("resetTopicSeries");
  429. commit("setCurElement", {});
  430. },
  431. // 删除试题包含元素 --------------->
  432. removeElementChild({ state, commit }, element) {
  433. // 删除解答题小题和作文题的子元素
  434. const pos = fetchElementPositionInfos(element.container, state.topics);
  435. const columnElements = state.topics[pos].elements;
  436. const childIndex = columnElements.findIndex(item => item.id === element.id);
  437. columnElements.splice(childIndex, 1);
  438. commit("setCurElement", {});
  439. },
  440. // 扩展答题区操作 --------------->
  441. copyExplainChildren({ state }, element) {
  442. let curElement = {
  443. id: element.container ? element.container.id : element.id
  444. };
  445. const pos = fetchElementPositionInfos(curElement, state.topics);
  446. curElement = state.topics[pos];
  447. let newElement = Object.assign({}, curElement, {
  448. id: getElementId(),
  449. elements: [],
  450. h: 200,
  451. isExtend: true,
  452. showTitle: false
  453. });
  454. state.topics.splice(pos + 1, 0, newElement);
  455. // 更新小题答题区isLast
  456. let positionInfos = fetchSameSerialNumberChildrenPositionInfo(
  457. curElement,
  458. state.topics
  459. );
  460. positionInfos.forEach((pos, pindex) => {
  461. state.topics[pos._elementNo].isLast = pindex + 1 === positionInfos.length;
  462. });
  463. },
  464. deleteExplainChildren({ state }, element) {
  465. let curElement = {
  466. id: element.container ? element.container.id : element.id
  467. };
  468. const curPos = fetchElementPositionInfos(curElement, state.topics);
  469. curElement = state.topics[curPos];
  470. let positionInfos = fetchSameSerialNumberChildrenPositionInfo(
  471. curElement,
  472. state.topics
  473. );
  474. if (positionInfos.length < 2) return;
  475. const pindex = positionInfos.findIndex(
  476. item => item._elementId === curElement.id
  477. );
  478. const pos = positionInfos[pindex]._elementNo;
  479. positionInfos.splice(pindex, 1);
  480. const nextPos = positionInfos[0]._elementNo;
  481. // 当删除的是非扩展区域时,则下一个答题区要被设置成非扩展区
  482. if (!curElement.isExtend) {
  483. state.topics[nextPos].isExtend = false;
  484. }
  485. // 当删除的是含有标题答题区时,则需要将下一个答题区开启显示标题。
  486. if (curElement.showTitle) {
  487. state.topics[nextPos].showTitle = true;
  488. }
  489. state.topics.splice(pos, 1);
  490. // 更新小题答题区isLast
  491. positionInfos = fetchSameSerialNumberChildrenPositionInfo(
  492. curElement,
  493. state.topics
  494. );
  495. positionInfos.forEach((pos, pindex) => {
  496. state.topics[pos._elementNo].isLast = pindex + 1 === positionInfos.length;
  497. });
  498. },
  499. // 大题顺序操作 --------------->
  500. topicMoveUp({ state, dispatch }, topicId) {
  501. const curTopicPos = state.topicSeries.findIndex(
  502. item => item.id === topicId
  503. );
  504. const prevTopicId = state.topicSeries[curTopicPos - 1].id;
  505. let relateTopicPos = [];
  506. state.topics.forEach((item, index) => {
  507. if (item.parent && item.parent.id === topicId) relateTopicPos.push(index);
  508. });
  509. const prevTopicFirstIndex = state.topics.findIndex(
  510. item => item.parent && item.parent.id === prevTopicId
  511. );
  512. const relateTopics = state.topics.splice(
  513. relateTopicPos[0],
  514. relateTopicPos.length
  515. );
  516. relateTopics.reverse().forEach(topic => {
  517. state.topics.splice(prevTopicFirstIndex, 0, topic);
  518. });
  519. dispatch("resetTopicSeries");
  520. },
  521. // 重构页面
  522. resetElementProp({ state }, isResetId = false) {
  523. state.topics.forEach(element => {
  524. const elementDom = document.getElementById(element.id);
  525. if (elementDom) {
  526. element.h = elementDom.offsetHeight;
  527. element.w = elementDom.offsetWidth;
  528. }
  529. if (isResetId) {
  530. element.id = getElementId();
  531. }
  532. });
  533. },
  534. rebuildPages({ state, commit }) {
  535. const columnNumber = state.cardConfig.columnNumber;
  536. const pageSize = state.cardConfig.pageSize;
  537. // 更新元件最新的高度信息
  538. // 整理所有元件
  539. const cardHeadElement = state.topics[0];
  540. state.topics.forEach(element => {
  541. const elementDom = document.getElementById(`preview-${element.id}`);
  542. if (elementDom) {
  543. element.h = elementDom.offsetHeight;
  544. element.w = elementDom.offsetWidth;
  545. // 解答题小题与其他题有些区别。
  546. // 其他题都是通过内部子元素自动撑高元件,而解答题则需要手动设置高度。
  547. const ESCAPE_ELEMENTS = ["CARD_HEAD"];
  548. element.isCovered =
  549. !ESCAPE_ELEMENTS.includes(element.type) &&
  550. checkElementisCovered(`preview-${element.id}`, element.type);
  551. }
  552. });
  553. // 动态计算每列可以分配的元件
  554. const columnHeight = document.getElementById("topic-column").offsetHeight;
  555. const simpleCardHeadHeight = document.getElementById("simple-card-head")
  556. .offsetHeight;
  557. let pages = [];
  558. let page = {};
  559. let columns = [];
  560. let curColumnElements = [];
  561. let curColumnHeight = 0;
  562. const initCurColumnElements = () => {
  563. curColumnElements = [];
  564. curColumnHeight = 0;
  565. const groupColumnNumber = columnNumber * 2;
  566. // 奇数页第一栏;
  567. if (!(columns.length % groupColumnNumber)) {
  568. // 非第一页奇数页第一栏
  569. if (columns.length) {
  570. const cardHeadSimpleElement = Object.assign({}, cardHeadElement, {
  571. id: getElementId(),
  572. isSimple: true,
  573. h: simpleCardHeadHeight
  574. });
  575. curColumnElements.push(cardHeadSimpleElement);
  576. curColumnHeight += simpleCardHeadHeight;
  577. } else {
  578. curColumnElements.push(cardHeadElement);
  579. curColumnHeight += cardHeadElement.h;
  580. }
  581. }
  582. };
  583. const checkElementIsCurColumnFirstType = element => {
  584. const topicHeadIndex = curColumnElements.findIndex(
  585. elem => elem.type === "TOPIC_HEAD" && elem.sign === element.sign
  586. );
  587. return topicHeadIndex === -1;
  588. };
  589. // 放入元素通用流程
  590. const pushElement = element => {
  591. // 当前栏中第一个题型之前新增题型头元素(topic-head)。
  592. // 题型头和当前题要组合加入栏中,不可拆分。
  593. let elementList = [element];
  594. if (checkElementIsCurColumnFirstType(element)) {
  595. elementList.unshift(
  596. getTopicHeadModel(
  597. state.cardConfig[`${element.sign}Attention`],
  598. element.sign,
  599. !curColumnElements.length
  600. )
  601. );
  602. }
  603. const elementHeight = calcSum(elementList.map(elem => elem.h));
  604. if (curColumnHeight + elementHeight > columnHeight) {
  605. // 当前栏第一个元素高度超过一栏时,不拆分,直接放在当前栏。
  606. // 解决可能空栏的情况
  607. const curElementIsFirst = !curColumnElements.length;
  608. if (curElementIsFirst) {
  609. curColumnElements = [...curColumnElements, ...elementList];
  610. curColumnHeight += elementHeight;
  611. } else {
  612. columns.push([...curColumnElements]);
  613. initCurColumnElements();
  614. pushElement(element);
  615. }
  616. } else {
  617. curColumnElements = [...curColumnElements, ...elementList];
  618. curColumnHeight += elementHeight;
  619. }
  620. };
  621. // 批量添加所有元素。
  622. initCurColumnElements();
  623. state.topics.slice(1).forEach((element, eindex) => {
  624. element.elementSerialNo = eindex;
  625. pushElement(element);
  626. });
  627. // 最后一栏的处理。
  628. columns.push([...curColumnElements]);
  629. // 构建pages
  630. columns.forEach((column, cindex) => {
  631. const columnNo = cindex % columnNumber;
  632. if (!columnNo) {
  633. page = getNewPage(pages.length, { pageSize, columnNumber });
  634. }
  635. page.columns[columnNo].elements = column;
  636. if (columnNo + 1 === columnNumber || cindex === columns.length - 1) {
  637. pages.push(deepCopy(page));
  638. }
  639. });
  640. // 保证页面总是偶数页
  641. if (pages.length % 2) {
  642. pages.push(getNewPage(pages.length, { pageSize, columnNumber }));
  643. }
  644. commit("setPages", pages);
  645. commit("setCurPage", state.curPageNo);
  646. }
  647. };
  648. export { fetchSameSerialNumberChildrenPositionInfo, checkElementisCovered };
  649. export default {
  650. namespaced: true,
  651. state,
  652. mutations,
  653. actions
  654. };