exchange.js 11 KB

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