exchange.js 10 KB

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