card.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  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. scrollToElementPage({ state, commit }, element) {
  244. let elementPapeNo = null;
  245. state.pages.forEach((page, pageNo) => {
  246. if (elementPapeNo !== null) return;
  247. page.columns.forEach((column) => {
  248. if (elementPapeNo !== null) return;
  249. column.elements.forEach((elem) => {
  250. if (elem.id === element.id || elem.parent?.id === element.id) {
  251. elementPapeNo = pageNo;
  252. }
  253. });
  254. });
  255. });
  256. // console.log(elementPapeNo);
  257. if (elementPapeNo === null || elementPapeNo === state.curPageNo) return;
  258. let pageTops = state.pages.map((page, pageNo) => {
  259. return document.getElementById(`edit-page-box-${pageNo}`).offsetTop;
  260. });
  261. pageTops = pageTops.map((item) => item - pageTops[0]);
  262. document.getElementById("design-main").scrollTop = pageTops[elementPapeNo];
  263. commit("setCurPageNo", elementPapeNo);
  264. },
  265. // 新增试题 --------------->
  266. addElement({ state, commit, dispatch }, element) {
  267. let pos = null;
  268. // 客观题和主观题分别对待
  269. if (element.insertTopic && element.insertTopic.id) {
  270. // 存在插入目标元素
  271. if (
  272. element.insertTopic.type === "FILL_QUESTION" &&
  273. element.type !== "FILL_QUESTION"
  274. ) {
  275. pos = fetchFirstSubjectiveTopicPositionInfo(state.topics);
  276. } else {
  277. let relateTopicPos = [];
  278. state.topics.forEach((item, index) => {
  279. if (item.parent && item.parent.id === element.insertTopic.id)
  280. relateTopicPos.push(index);
  281. });
  282. pos = relateTopicPos.slice(-1)[0] + 1;
  283. }
  284. delete element.insertTopic;
  285. } else {
  286. // 不存在插入目标元素
  287. if (element.sign === "objective") {
  288. pos = fetchFirstSubjectiveTopicPositionInfo(state.topics);
  289. }
  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("resetTopicSeries");
  300. commit("setCurElement", element);
  301. },
  302. addForbidArea({ state, commit }, { element, beforeElementId }) {
  303. const beforeElementPos = fetchElementPositionInfos(
  304. { id: beforeElementId },
  305. state.topics
  306. );
  307. state.topics.splice(beforeElementPos + 1, 0, element);
  308. commit("setCurElement", element);
  309. },
  310. // 修改试题 --------------->
  311. modifyTopic({ state }, element) {
  312. // 单独编辑某个细分题
  313. const pos = fetchElementPositionInfos(element, state.topics);
  314. state.topics.splice(pos, 1, element);
  315. },
  316. modifyComposition({ state }, element) {
  317. const positionInfos = fetchAllRelateParentElementPositionInfos(
  318. element,
  319. state.topics
  320. );
  321. positionInfos.forEach(({ _elementNo }) => {
  322. state.topics[_elementNo].topicNo = element.topicNo;
  323. state.topics[_elementNo].parent = objAssign(
  324. state.topics[_elementNo].parent,
  325. element
  326. );
  327. });
  328. },
  329. modifyExplain({ state }, element) {
  330. // 解答题既是拆分题,又是可复制题
  331. const positionInfos = fetchAllRelateParentElementPositionInfos(
  332. element,
  333. state.topics
  334. );
  335. const elementGroupPosInfos = groupByParams(positionInfos, "serialNumber");
  336. const orgElementCount = elementGroupPosInfos.length;
  337. if (orgElementCount > element.questionsCount) {
  338. // 原小题数多于新小题数,要删除原多于的小题;
  339. let needDeleteInfos = elementGroupPosInfos.splice(
  340. element.questionsCount,
  341. orgElementCount - element.questionsCount
  342. );
  343. needDeleteInfos.reverse().forEach((item) => {
  344. item.reverse().forEach((pos) => {
  345. state.topics.splice(pos._elementNo, 1);
  346. });
  347. });
  348. }
  349. const newElements = getExplainElements(element);
  350. const lastPos = elementGroupPosInfos.slice(-1)[0].slice(-1)[0];
  351. let lastNewElementPos = lastPos._elementNo;
  352. for (let i = 0; i < element.questionsCount; i++) {
  353. if (elementGroupPosInfos[i]) {
  354. elementGroupPosInfos[i].forEach((pos) => {
  355. let child = state.topics[pos._elementNo];
  356. child.serialNumber = i + element.startNumber;
  357. child.parent = { ...element };
  358. child.topicNo = element.topicNo;
  359. });
  360. } else {
  361. state.topics.splice(++lastNewElementPos, 0, newElements[i]);
  362. }
  363. }
  364. },
  365. modifySplitTopic({ state }, element) {
  366. // 非作文题都是拆分题,即同一个题拆分成多个小题展示
  367. const positionInfos = fetchAllRelateParentElementPositionInfos(
  368. element,
  369. state.topics
  370. );
  371. // 缓存已编辑的小题高度信息。
  372. // const elementHeights = positionInfos.map(
  373. // pos => state.topics[pos._elementNo].h
  374. // );
  375. // 删除所有小题
  376. positionInfos.reverse().forEach((pos) => {
  377. state.topics.splice(pos._elementNo, 1);
  378. });
  379. // 创建新的小题元素
  380. const newElements = createFunc[element.type](element);
  381. const pos = positionInfos.pop();
  382. newElements.forEach((newElement, index) => {
  383. // 不再复用缓存高度,修改于2022-03-10 09:23
  384. // newElement.h = Math.max(elementHeights[index] || 0, newElement.h);
  385. state.topics.splice(pos._elementNo + index, 0, newElement);
  386. });
  387. },
  388. modifyElement({ commit, dispatch }, element) {
  389. if (element.type === "COMPOSITION") {
  390. dispatch("modifyComposition", element);
  391. } else if (element.type === "EXPLAIN") {
  392. dispatch("modifyExplain", element);
  393. } else {
  394. dispatch("modifySplitTopic", element);
  395. }
  396. dispatch("resetTopicSeries");
  397. commit("setCurElement", element);
  398. },
  399. modifyCardHead({ state }, element) {
  400. state.topics.splice(0, 1, element);
  401. },
  402. // 修改试题包含元素
  403. modifyElementChild({ state, commit }, element) {
  404. // 修改解答题小题和作文题的子元素
  405. const pos = fetchElementPositionInfos(element.container, state.topics);
  406. const columnElements = state.topics[pos].elements;
  407. const childIndex = columnElements.findIndex(
  408. (item) => item.id === element.id
  409. );
  410. element.id = getElementId();
  411. // 作文题中的多线条和网格元素重新计算高度。
  412. if (element.container.type === "COMPOSITION") {
  413. if (element.type === "LINES") {
  414. element.h = element.lineCount * (element.lineSpacing + 1);
  415. }
  416. if (element.type === "GRIDS") {
  417. if (element.halving) {
  418. const columnWs = {
  419. 2: 703,
  420. 3: 461,
  421. 4: 349,
  422. };
  423. const columnSize =
  424. columnWs[state.cardConfig.columnNumber] / element.columnCount;
  425. element.h = element.rowCount * (columnSize + element.rowSpace) + 1;
  426. } else {
  427. element.h =
  428. element.rowCount * (element.columnSize + element.rowSpace) + 1;
  429. }
  430. }
  431. }
  432. if (childIndex === -1) {
  433. columnElements.push(element);
  434. } else {
  435. columnElements.splice(childIndex, 1, element);
  436. }
  437. commit("setCurElement", element);
  438. },
  439. // 粘贴试题内的元素
  440. pasteExplainElementChild({ state }, { curElement, pasteElement }) {
  441. let element = {
  442. id: curElement.container ? curElement.container.id : curElement.id,
  443. };
  444. const pos = fetchElementPositionInfos(element, state.topics);
  445. if (pos === -1) return;
  446. element = state.topics[pos];
  447. const newElement = Object.assign({}, pasteElement, {
  448. id: getElementId(),
  449. container: {
  450. id: element.id,
  451. type: element.type,
  452. },
  453. });
  454. element.elements.push(newElement);
  455. },
  456. // 删除试题 --------------->
  457. removeElement({ state, commit, dispatch }, element) {
  458. if (!element.parent) {
  459. state.topics = state.topics.filter((item) => item.id !== element.id);
  460. commit("setCurElement", {});
  461. return;
  462. }
  463. const positionInfos = fetchAllRelateParentElementPositionInfos(
  464. element.parent,
  465. state.topics
  466. );
  467. positionInfos.reverse().forEach((pos) => {
  468. state.topics.splice(pos._elementNo, 1);
  469. });
  470. dispatch("resetTopicSeries");
  471. commit("setCurElement", {});
  472. },
  473. // 删除试题包含元素 --------------->
  474. removeElementChild({ state, commit }, element) {
  475. // 删除解答题小题和作文题的子元素
  476. const pos = fetchElementPositionInfos(element.container, state.topics);
  477. const columnElements = state.topics[pos].elements;
  478. const childIndex = columnElements.findIndex(
  479. (item) => item.id === element.id
  480. );
  481. columnElements.splice(childIndex, 1);
  482. commit("setCurElement", {});
  483. },
  484. // 扩展答题区操作 --------------->
  485. copyExplainChildren({ state }, element) {
  486. let curElement = {
  487. id: element.container ? element.container.id : element.id,
  488. };
  489. const pos = fetchElementPositionInfos(curElement, state.topics);
  490. curElement = state.topics[pos];
  491. let newElement = Object.assign({}, curElement, {
  492. id: getElementId(),
  493. elements: [],
  494. h: 200,
  495. isExtend: true,
  496. showTitle: false,
  497. });
  498. state.topics.splice(pos + 1, 0, newElement);
  499. // 更新小题答题区isLast
  500. let positionInfos = fetchSameSerialNumberChildrenPositionInfo(
  501. curElement,
  502. state.topics
  503. );
  504. positionInfos.forEach((pos, pindex) => {
  505. state.topics[pos._elementNo].isLast = pindex + 1 === positionInfos.length;
  506. });
  507. },
  508. deleteExplainChildren({ state }, element) {
  509. let curElement = {
  510. id: element.container ? element.container.id : element.id,
  511. };
  512. const curPos = fetchElementPositionInfos(curElement, state.topics);
  513. curElement = state.topics[curPos];
  514. let positionInfos = fetchSameSerialNumberChildrenPositionInfo(
  515. curElement,
  516. state.topics
  517. );
  518. if (positionInfos.length < 2) return;
  519. const pindex = positionInfos.findIndex(
  520. (item) => item._elementId === curElement.id
  521. );
  522. const pos = positionInfos[pindex]._elementNo;
  523. positionInfos.splice(pindex, 1);
  524. const nextPos = positionInfos[0]._elementNo;
  525. // 当删除的是非扩展区域时,则下一个答题区要被设置成非扩展区
  526. if (!curElement.isExtend) {
  527. state.topics[nextPos].isExtend = false;
  528. }
  529. // 当删除的是含有标题答题区时,则需要将下一个答题区开启显示标题。
  530. if (curElement.showTitle) {
  531. state.topics[nextPos].showTitle = true;
  532. }
  533. state.topics.splice(pos, 1);
  534. // 更新小题答题区isLast
  535. positionInfos = fetchSameSerialNumberChildrenPositionInfo(
  536. curElement,
  537. state.topics
  538. );
  539. positionInfos.forEach((pos, pindex) => {
  540. state.topics[pos._elementNo].isLast = pindex + 1 === positionInfos.length;
  541. });
  542. },
  543. // 大题顺序操作 --------------->
  544. topicMoveUp({ state, dispatch }, topicId) {
  545. const curTopicPos = state.topicSeries.findIndex(
  546. (item) => item.id === topicId
  547. );
  548. const prevTopicId = state.topicSeries[curTopicPos - 1].id;
  549. let relateTopicPos = [];
  550. state.topics.forEach((item, index) => {
  551. if (item.parent && item.parent.id === topicId) relateTopicPos.push(index);
  552. });
  553. const prevTopicFirstIndex = state.topics.findIndex(
  554. (item) => item.parent && item.parent.id === prevTopicId
  555. );
  556. const relateTopics = state.topics.splice(
  557. relateTopicPos[0],
  558. relateTopicPos.length
  559. );
  560. relateTopics.reverse().forEach((topic) => {
  561. state.topics.splice(prevTopicFirstIndex, 0, topic);
  562. });
  563. dispatch("resetTopicSeries");
  564. },
  565. // 重构页面
  566. resetElementProp({ state }, isResetId = false) {
  567. state.topics.forEach((element) => {
  568. const elementDom = document.getElementById(element.id);
  569. if (elementDom) {
  570. element.h = elementDom.offsetHeight;
  571. element.w = elementDom.offsetWidth;
  572. }
  573. if (isResetId) {
  574. element.id = getElementId();
  575. }
  576. });
  577. },
  578. rebuildPages({ state, commit }) {
  579. const columnNumber = state.cardConfig.columnNumber;
  580. const pageSize = state.cardConfig.pageSize;
  581. // 更新元件最新的高度信息
  582. // 整理所有元件
  583. const cardHeadElement = state.topics[0];
  584. state.topics.forEach((element) => {
  585. const elementDom = document.getElementById(`preview-${element.id}`);
  586. if (elementDom) {
  587. element.h = elementDom.offsetHeight;
  588. element.w = elementDom.offsetWidth;
  589. // 解答题小题与其他题有些区别。
  590. // 其他题都是通过内部子元素自动撑高元件,而解答题则需要手动设置高度。
  591. const ESCAPE_ELEMENTS = ["CARD_HEAD"];
  592. element.isCovered =
  593. !ESCAPE_ELEMENTS.includes(element.type) &&
  594. checkElementisCovered(`preview-${element.id}`, element.type);
  595. }
  596. });
  597. // 动态计算每列可以分配的元件
  598. const columnHeight = document.getElementById("topic-column").offsetHeight;
  599. const simpleCardHeadHeight =
  600. document.getElementById("simple-card-head").offsetHeight;
  601. let pages = [];
  602. let page = {};
  603. let columns = [];
  604. let curColumnElements = [];
  605. let curColumnHeight = 0;
  606. const initCurColumnElements = () => {
  607. curColumnElements = [];
  608. curColumnHeight = 0;
  609. const groupColumnNumber = columnNumber * 2;
  610. // 奇数页第一栏;
  611. if (!(columns.length % groupColumnNumber)) {
  612. // 非第一页奇数页第一栏
  613. if (columns.length) {
  614. const cardHeadSimpleElement = Object.assign({}, cardHeadElement, {
  615. id: getElementId(),
  616. isSimple: true,
  617. h: simpleCardHeadHeight,
  618. });
  619. curColumnElements.push(cardHeadSimpleElement);
  620. curColumnHeight += simpleCardHeadHeight;
  621. } else {
  622. curColumnElements.push(cardHeadElement);
  623. curColumnHeight += cardHeadElement.h;
  624. }
  625. }
  626. };
  627. const checkElementIsCurColumnFirstType = (element) => {
  628. const topicHeadIndex = curColumnElements.findIndex(
  629. (elem) => elem.type === "TOPIC_HEAD" && elem.sign === element.sign
  630. );
  631. return topicHeadIndex === -1;
  632. };
  633. // 放入元素通用流程
  634. const pushElement = (element) => {
  635. // 当前栏中第一个题型之前新增题型头元素(topic-head)。
  636. // 题型头和当前题要组合加入栏中,不可拆分。
  637. let elementList = [element];
  638. if (checkElementIsCurColumnFirstType(element)) {
  639. elementList.unshift(
  640. getTopicHeadModel(
  641. state.cardConfig[`${element.sign}Attention`],
  642. element.sign,
  643. !curColumnElements.length
  644. )
  645. );
  646. }
  647. const elementHeight = calcSum(elementList.map((elem) => elem.h));
  648. if (curColumnHeight + elementHeight > columnHeight) {
  649. // 当前栏第一个元素高度超过一栏时,不拆分,直接放在当前栏。
  650. // 解决可能空栏的情况
  651. const curElementIsFirst = !curColumnElements.length;
  652. if (curElementIsFirst) {
  653. curColumnElements = [...curColumnElements, ...elementList];
  654. curColumnHeight += elementHeight;
  655. } else {
  656. columns.push([...curColumnElements]);
  657. initCurColumnElements();
  658. pushElement(element);
  659. }
  660. } else {
  661. curColumnElements = [...curColumnElements, ...elementList];
  662. curColumnHeight += elementHeight;
  663. }
  664. };
  665. // 批量添加所有元素。
  666. initCurColumnElements();
  667. state.topics.slice(1).forEach((element, eindex) => {
  668. element.elementSerialNo = eindex;
  669. pushElement(element);
  670. });
  671. // 最后一栏的处理。
  672. columns.push([...curColumnElements]);
  673. // 构建pages
  674. columns.forEach((column, cindex) => {
  675. const columnNo = cindex % columnNumber;
  676. if (!columnNo) {
  677. page = getNewPage(pages.length, { pageSize, columnNumber });
  678. }
  679. page.columns[columnNo].elements = column;
  680. if (columnNo + 1 === columnNumber || cindex === columns.length - 1) {
  681. pages.push(deepCopy(page));
  682. }
  683. });
  684. // 保证页面总是偶数页
  685. if (pages.length % 2) {
  686. pages.push(getNewPage(pages.length, { pageSize, columnNumber }));
  687. }
  688. commit("setPages", pages);
  689. commit("setCurPage", state.curPageNo);
  690. },
  691. };
  692. export { fetchSameSerialNumberChildrenPositionInfo, checkElementisCovered };
  693. export default {
  694. namespaced: true,
  695. state,
  696. mutations,
  697. actions,
  698. };