exchange.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. import { CARD_VERSION } from "../enumerate";
  2. import { deepCopy } from "../plugins/utils";
  3. const initIndex = {
  4. question: 1,
  5. absent: 1,
  6. breach: 1,
  7. paperType: 1,
  8. examNumber: 1,
  9. selective: 1,
  10. pageNumber: 1,
  11. };
  12. /**
  13. * 格式文档:https://doc.qmth.com.cn/pages/viewpage.action?pageId=19661052
  14. */
  15. export default {
  16. data() {
  17. return {
  18. fillAreaIndex: {
  19. ...initIndex,
  20. },
  21. VALID_ELEMENTS_FOR_EXTERNAL: [
  22. "LOCATOR",
  23. "BARCODE",
  24. "CARD_HEAD",
  25. "FILL_QUESTION",
  26. "FILL_LINE",
  27. "EXPLAIN",
  28. "COMPOSITION",
  29. ],
  30. };
  31. },
  32. methods: {
  33. getFillAreaIndex(type) {
  34. return this.fillAreaIndex[type]++;
  35. },
  36. getElementHumpName(cont) {
  37. return cont
  38. .split("_")
  39. .map((item) => item[0] + item.substr(1).toLowerCase())
  40. .join("");
  41. },
  42. getPreviewElementById(id) {
  43. return document.getElementById(`preview-${id}`);
  44. },
  45. parsePageExchange(pages) {
  46. const npages = deepCopy(pages);
  47. // 单页题卡不显示页码涂块
  48. const pageNumberInfo =
  49. pages.length <= 2 ? null : this.getPageNumberInfo();
  50. npages.forEach((page, pindex) => {
  51. let exchange = {
  52. card_type: 2,
  53. page_size: page.pageSize,
  54. page_image: "",
  55. locator: this.getLocatorInfo(page.locators),
  56. fill_locator: [],
  57. check_area: {
  58. black_line: [],
  59. white_line: [],
  60. },
  61. barcode: [],
  62. qrcode: [],
  63. ocr_area: [],
  64. info_area: [],
  65. fill_area: [],
  66. answer_area: [],
  67. extension: {
  68. barcode: [],
  69. fill_area: [],
  70. ocr_area: [],
  71. qrcode: [],
  72. },
  73. };
  74. const elements = [
  75. page.globals,
  76. ...page.columns.map((column) => column.elements),
  77. ];
  78. elements.forEach((elemGroup) => {
  79. elemGroup.forEach((element) => {
  80. if (this.VALID_ELEMENTS_FOR_EXTERNAL.includes(element.type)) {
  81. const funcName = this.getElementHumpName(element.type);
  82. // console.log(funcName);
  83. const info = this[`get${funcName}Info`](element);
  84. Object.entries(info).forEach(([key, vals]) => {
  85. exchange[key] = exchange[key].concat(vals);
  86. });
  87. }
  88. });
  89. });
  90. if (!(pindex % 2) && pageNumberInfo) {
  91. let pnoInfo = deepCopy(pageNumberInfo);
  92. pnoInfo[0].index = this.getFillAreaIndex("pageNumber");
  93. exchange.fill_area = exchange.fill_area.concat(pnoInfo);
  94. }
  95. page.exchange = exchange;
  96. });
  97. this.fillAreaIndex = { ...initIndex };
  98. return npages;
  99. },
  100. getPageNumberInfo() {
  101. const dom = document.querySelector(".page-box-0");
  102. let options = [];
  103. dom
  104. .querySelector(".page-number-rect-list")
  105. .childNodes.forEach((item, index) => {
  106. options[index] = this.getOffsetInfo(item);
  107. });
  108. // console.log(options);
  109. return [
  110. {
  111. field: "pageNumber",
  112. index: 1,
  113. single: true,
  114. horizontal: true,
  115. items: [
  116. {
  117. main_number: null,
  118. sub_number: null,
  119. options,
  120. recog_info: [],
  121. },
  122. ],
  123. },
  124. ];
  125. },
  126. getLocatorInfo(locators) {
  127. const tops = locators.top.map((locator) => {
  128. return this.getOffsetInfo(document.getElementById(locator.id));
  129. });
  130. const bottoms = locators.bottom.map((locator) => {
  131. return this.getOffsetInfo(document.getElementById(locator.id));
  132. });
  133. return {
  134. top: tops,
  135. bottom: bottoms,
  136. };
  137. },
  138. getCardHeadInfo(element) {
  139. const dom = this.getPreviewElementById(element.id);
  140. const headArea = this.getOffsetInfo(dom);
  141. let fill_area = [];
  142. let barcode = [];
  143. // 学生考号
  144. if (element.examNumberStyle === "FILL") {
  145. // fill_area
  146. let listInfos = [];
  147. dom
  148. .querySelectorAll(".stdno-fill-list")
  149. .forEach((questionItem, questionIndex) => {
  150. let options = [];
  151. questionItem.childNodes.forEach((optionItem, optionIndex) => {
  152. options[optionIndex] = this.getOffsetInfo(optionItem);
  153. });
  154. listInfos[questionIndex] = {
  155. main_number: null,
  156. sub_number: null,
  157. options,
  158. recog_info: [],
  159. };
  160. });
  161. fill_area.push({
  162. field: "examNumber",
  163. index: this.getFillAreaIndex("examNumber"),
  164. single: true,
  165. horizontal: false,
  166. items: listInfos,
  167. });
  168. } else {
  169. // barcode
  170. const stdnoDom =
  171. element.columnNumber <= 2
  172. ? dom.querySelector(".head-stdno").parentNode
  173. : dom.querySelector(".head-stdno");
  174. barcode.push({
  175. field: "examNumber",
  176. area: this.getOffsetInfo(stdnoDom),
  177. });
  178. }
  179. // 缺考涂填
  180. if (element.examAbsent && !element.isSimple) {
  181. fill_area.push({
  182. field: "absent",
  183. index: this.getFillAreaIndex("absent"),
  184. single: true,
  185. horizontal: true,
  186. items: [
  187. {
  188. main_number: null,
  189. sub_number: null,
  190. options: [
  191. this.getOffsetInfo(
  192. document.getElementById("dynamic-miss-area")
  193. ),
  194. ],
  195. recog_info: [],
  196. },
  197. ],
  198. });
  199. }
  200. // 违纪标记
  201. if (element.discipline && !element.isSimple) {
  202. fill_area.push({
  203. field: "breach",
  204. index: this.getFillAreaIndex("breach"),
  205. single: true,
  206. horizontal: true,
  207. items: [
  208. {
  209. main_number: null,
  210. sub_number: null,
  211. options: [
  212. this.getOffsetInfo(
  213. document.getElementById("dynamic-breach-area")
  214. ),
  215. ],
  216. recog_info: [],
  217. },
  218. ],
  219. });
  220. }
  221. // A/B卷类型
  222. if (element.aOrB && !element.isSimple) {
  223. if (element.paperType === "PRINT") {
  224. // barcode
  225. barcode.push({
  226. field: "paperType",
  227. area: this.getOffsetInfo(
  228. document.getElementById("dynamic-aorb-barcode")
  229. ),
  230. });
  231. } else {
  232. // fill_area
  233. let options = [];
  234. document
  235. .getElementById("head-dynamic-aorb")
  236. .querySelectorAll(".head-dynamic-rect")
  237. .forEach((optionItem, optionIndex) => {
  238. options[optionIndex] = this.getOffsetInfo(optionItem);
  239. });
  240. fill_area.push({
  241. field: "paperType",
  242. index: this.getFillAreaIndex("paperType"),
  243. single: true,
  244. horizontal: true,
  245. items: [
  246. {
  247. main_number: null,
  248. sub_number: null,
  249. options,
  250. recog_info: [],
  251. },
  252. ],
  253. });
  254. }
  255. }
  256. return {
  257. info_area: [headArea],
  258. fill_area,
  259. barcode,
  260. };
  261. },
  262. getFillQuestionInfo(element) {
  263. const dom = this.getPreviewElementById(element.id);
  264. const single = !element.isMultiply;
  265. const horizontal = element.optionDirection === "horizontal";
  266. let fillAreas = [];
  267. dom.querySelectorAll(".group-item").forEach((groupItem) => {
  268. let listInfos = [];
  269. groupItem
  270. .querySelectorAll(".question-item")
  271. .forEach((questionItem, questionIndex) => {
  272. let options = [];
  273. questionItem.childNodes.forEach((optionItem, optionIndex) => {
  274. if (optionIndex)
  275. options[optionIndex - 1] = this.getOffsetInfo(optionItem);
  276. });
  277. listInfos[questionIndex] = {
  278. main_number: element.topicNo,
  279. sub_number: questionItem.firstChild.textContent * 1,
  280. options,
  281. recog_info: [],
  282. };
  283. });
  284. fillAreas.push({
  285. field: "question",
  286. index: this.getFillAreaIndex("question"),
  287. single,
  288. horizontal,
  289. items: listInfos,
  290. });
  291. });
  292. return {
  293. fill_area: fillAreas,
  294. };
  295. },
  296. getFillLineInfo(element) {
  297. const dom = this.getPreviewElementById(element.id);
  298. let sub_numbers = [];
  299. for (
  300. let i = element.startNumber,
  301. len = element.startNumber + element.questionsCount;
  302. i < len;
  303. i++
  304. ) {
  305. sub_numbers.push(i);
  306. }
  307. return {
  308. answer_area: [
  309. {
  310. main_number: element.topicNo,
  311. sub_number: sub_numbers.join(),
  312. area: this.getOffsetInfo(dom),
  313. },
  314. ],
  315. };
  316. },
  317. getExplainInfo(element) {
  318. const dom = this.getPreviewElementById(element.id);
  319. return {
  320. answer_area: [
  321. {
  322. main_number: element.topicNo,
  323. sub_number: element.serialNumber,
  324. area: this.getOffsetInfo(dom),
  325. },
  326. ],
  327. };
  328. },
  329. getCompositionInfo(element) {
  330. const dom = this.getPreviewElementById(element.id);
  331. return {
  332. answer_area: [
  333. {
  334. main_number: element.topicNo,
  335. sub_number: null,
  336. area: this.getOffsetInfo(dom),
  337. },
  338. ],
  339. };
  340. },
  341. getOffsetInfo(dom) {
  342. let { offsetTop, offsetLeft } = dom;
  343. let parentNode = dom.offsetParent;
  344. while (parentNode.className.indexOf("page-box") === -1) {
  345. offsetTop += parentNode.offsetTop;
  346. offsetLeft += parentNode.offsetLeft;
  347. parentNode = parentNode.offsetParent;
  348. }
  349. const pw = parentNode.offsetWidth;
  350. const ph = parentNode.offsetHeight;
  351. const infos = [
  352. offsetLeft / pw,
  353. offsetTop / ph,
  354. dom.offsetWidth / pw,
  355. dom.offsetHeight / ph,
  356. ];
  357. return infos.map((num) => num.toFixed(10) * 1);
  358. },
  359. getPageModel({ cardConfig, paperParams, pages }) {
  360. let npages = this.parsePageExchange(pages);
  361. return JSON.stringify(
  362. {
  363. version: CARD_VERSION,
  364. cardConfig,
  365. paperParams,
  366. pages: npages,
  367. },
  368. (k, v) => (k.startsWith("_") ? undefined : v)
  369. );
  370. },
  371. },
  372. };