card.js 23 KB

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