card.js 23 KB

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