card.js 24 KB

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