card.js 23 KB

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