CardDesign.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. <template>
  2. <div class="card-design">
  3. <div class="design-top">
  4. <div class="design-top-logo">
  5. <h1><i class="icon icon-back" @click="goback"></i>答题卡制作</h1>
  6. </div>
  7. <div class="design-top-info">
  8. <div class="info-help"><i class="icon icon-help"></i>帮助</div>
  9. </div>
  10. </div>
  11. <div class="design-main">
  12. <!-- menus -->
  13. <div class="design-head">
  14. <div class="design-steps">
  15. <div class="step-item" v-for="(step, index) in steps" :key="index">
  16. <i>{{ index + 1 }}</i>
  17. <span>{{ step }}</span>
  18. </div>
  19. </div>
  20. <div class="design-control">
  21. <div class="control-right">
  22. <el-button
  23. class="btn-white"
  24. @click="toPreview"
  25. :disabled="!pages.length"
  26. >预览</el-button
  27. >
  28. <save-page @confirm="toSave" ref="SavePage"></save-page>
  29. <el-button type="primary" @click="toSubmit">提交</el-button>
  30. </div>
  31. <div class="control-left">
  32. <el-button
  33. v-for="(page, pageNo) in pages"
  34. :key="pageNo"
  35. :class="{ 'btn-white': curPageNo === pageNo }"
  36. @click="swithPage(pageNo)"
  37. >第{{ pageNo + 1 }}页</el-button
  38. >
  39. </div>
  40. </div>
  41. </div>
  42. <!-- actions -->
  43. <div class="design-action">
  44. <div class="action-part">
  45. <div class="action-part-title"><h2>基本设置</h2></div>
  46. <div class="action-part-body">
  47. <page-prop-edit></page-prop-edit>
  48. </div>
  49. </div>
  50. <div class="action-part">
  51. <div class="action-part-title"><h2>试题配置</h2></div>
  52. <div class="action-part-body">
  53. <div class="type-list">
  54. <div
  55. class="type-item"
  56. v-for="(item, index) in TOPIC_LIST"
  57. :key="index"
  58. >
  59. <el-button @click="addNewTopic(item)"
  60. ><i class="el-icon-plus"></i>{{ item.name }}</el-button
  61. >
  62. </div>
  63. </div>
  64. </div>
  65. </div>
  66. <div class="action-part">
  67. <div class="action-part-title"><h2>插入元素</h2></div>
  68. <div class="action-part-body">
  69. <div class="type-list">
  70. <div
  71. class="type-item"
  72. v-for="(item, index) in ELEMENT_LIST"
  73. :key="index"
  74. draggable="true"
  75. @dragstart="dragstart(item)"
  76. >
  77. <el-button
  78. ><i class="el-icon-plus"></i>{{ item.name }}</el-button
  79. >
  80. </div>
  81. </div>
  82. </div>
  83. <!-- Develop btns -->
  84. <!-- <card-config-prop-edit></card-config-prop-edit>
  85. <br /><br />
  86. <el-button @click="initCard">新建页面</el-button> -->
  87. </div>
  88. </div>
  89. <!-- edit body -->
  90. <div class="design-body">
  91. <template v-for="(page, pageNo) in pages">
  92. <div
  93. :class="['page-box', `page-box-${pageNo % 2}`]"
  94. v-if="curPageNo === pageNo"
  95. :key="pageNo"
  96. >
  97. <div
  98. :class="[
  99. 'page-locators',
  100. `page-locators-${page.locators.length}`
  101. ]"
  102. >
  103. <ul
  104. class="page-locator-group"
  105. v-for="(locator, iind) in page.locators"
  106. :key="iind"
  107. >
  108. <li
  109. v-for="(elem, eindex) in locator"
  110. :key="eindex"
  111. :id="elem.id"
  112. ></li>
  113. </ul>
  114. </div>
  115. <!-- inner edit area -->
  116. <div class="page-main-inner">
  117. <div
  118. :class="['page-main', `page-main-${page.columns.length}`]"
  119. :style="{ margin: `0 -${page.columnGap / 2}px` }"
  120. >
  121. <div
  122. class="page-column"
  123. v-for="(column, columnNo) in page.columns"
  124. :key="columnNo"
  125. :style="{ padding: `0 ${page.columnGap / 2}px` }"
  126. >
  127. <div
  128. class="page-column-main"
  129. :id="[`column-${pageNo}-${columnNo}`]"
  130. >
  131. <div class="page-column-body" v-if="column.elements.length">
  132. <topic-element-edit
  133. class="page-column-element"
  134. v-for="element in column.elements"
  135. :key="element.id"
  136. :data="element"
  137. ></topic-element-edit>
  138. </div>
  139. <div class="page-column-body" v-else>
  140. <div
  141. class="page-column-forbid-area"
  142. v-if="cardConfig.showForbidArea"
  143. >
  144. <p>该区域严禁作答</p>
  145. </div>
  146. </div>
  147. </div>
  148. </div>
  149. </div>
  150. </div>
  151. <!-- outer edit area -->
  152. <div class="page-main-outer"></div>
  153. </div>
  154. </template>
  155. </div>
  156. <!-- design other pages -->
  157. <div class="design-other-pages">
  158. <template v-for="(page, pageNo) in pages">
  159. <div
  160. :class="['page-box', `page-box-${pageNo % 2}`]"
  161. v-if="curPageNo !== pageNo"
  162. :key="pageNo"
  163. >
  164. <div
  165. :class="[
  166. 'page-locators',
  167. `page-locators-${page.locators.length}`
  168. ]"
  169. >
  170. <ul
  171. class="page-locator-group"
  172. v-for="(locator, iind) in page.locators"
  173. :key="iind"
  174. >
  175. <li
  176. v-for="(elem, eindex) in locator"
  177. :key="eindex"
  178. :id="elem.id"
  179. ></li>
  180. </ul>
  181. </div>
  182. <!-- inner edit area -->
  183. <div class="page-main-inner">
  184. <div
  185. :class="['page-main', `page-main-${page.columns.length}`]"
  186. :style="{ margin: `0 -${page.columnGap / 2}px` }"
  187. >
  188. <div
  189. class="page-column"
  190. v-for="(column, columnNo) in page.columns"
  191. :key="columnNo"
  192. :style="{ padding: `0 ${page.columnGap / 2}px` }"
  193. >
  194. <div
  195. class="page-column-main"
  196. :id="[`column-${pageNo}-${columnNo}`]"
  197. >
  198. <div class="page-column-body">
  199. <topic-element-edit
  200. class="page-column-element"
  201. v-for="(element, eindex) in column.elements"
  202. :key="eindex"
  203. :data="element"
  204. ></topic-element-edit>
  205. </div>
  206. </div>
  207. </div>
  208. </div>
  209. </div>
  210. </div>
  211. </template>
  212. </div>
  213. </div>
  214. <!-- element-prop-edit -->
  215. <element-prop-edit></element-prop-edit>
  216. <!-- right-click-menu -->
  217. <right-click-menu></right-click-menu>
  218. <!-- card-head-sample -->
  219. <card-head-sample v-if="pages.length"></card-head-sample>
  220. <!-- card-view-frame -->
  221. <div class="design-preview-frame" v-if="cardPreviewUrl">
  222. <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
  223. </div>
  224. </div>
  225. </template>
  226. <script>
  227. import { mapState, mapMutations, mapActions } from "vuex";
  228. import {
  229. cardConfigInfos,
  230. cardDetailEdit,
  231. cardTempDetail,
  232. saveCard,
  233. submitCard
  234. } from "../api";
  235. import { saveWaitTask } from "@/modules/exam-center/api";
  236. import {
  237. getElementModel,
  238. getCardHeadModel,
  239. getNewPage,
  240. getTopicModel,
  241. ELEMENT_LIST,
  242. TOPIC_LIST
  243. } from "../elementModel";
  244. import { transformField, getAOrBSystem } from "../enumerate";
  245. import TopicElementEdit from "../components/TopicElementEdit";
  246. import PagePropEdit from "../components/PagePropEdit";
  247. // import CardConfigPropEdit from "../components/CardConfigPropEdit";
  248. import ElementPropEdit from "../components/elementPropEdit/ElementPropEdit";
  249. import RightClickMenu from "../components/RightClickMenu";
  250. import CardHeadSample from "../components/elementEdit/CardHeadSample";
  251. import SavePage from "../components/SavePage";
  252. export default {
  253. name: "card-design",
  254. components: {
  255. TopicElementEdit,
  256. PagePropEdit,
  257. // CardConfigPropEdit,
  258. ElementPropEdit,
  259. RightClickMenu,
  260. CardHeadSample,
  261. SavePage
  262. },
  263. data() {
  264. return {
  265. cardId: this.$route.params.cardId,
  266. cardDetailId: this.$ls.get("cardDetailId"),
  267. prepareTcPCard: this.$ls.get("prepareTcPCard", {}),
  268. ELEMENT_LIST,
  269. TOPIC_LIST,
  270. steps: ["添加标题", "基本设置", "试题配置", "试题配置"],
  271. columnWidth: 0,
  272. cardPreviewUrl: ""
  273. };
  274. },
  275. computed: {
  276. ...mapState("card", ["cardConfig", "pages", "curElement", "curPageNo"]),
  277. isEdit() {
  278. return !!this.cardId;
  279. }
  280. },
  281. mounted() {
  282. this.initCard();
  283. this.registWindowSubmit();
  284. },
  285. methods: {
  286. ...mapMutations("card", [
  287. "addPage",
  288. "setCurPageNo",
  289. "setCurElement",
  290. "setCardConfig",
  291. "setOpenElementEditDialog",
  292. "setCurDragElement",
  293. "setPages",
  294. "initState"
  295. ]),
  296. ...mapActions("card", [
  297. "removePage",
  298. "addElement",
  299. "modifyCardHead",
  300. "modifyElement",
  301. "rebuildPages"
  302. ]),
  303. async initCard() {
  304. await this.getCardConfig();
  305. if (this.isEdit) {
  306. this.getCardTempDetail();
  307. } else {
  308. this.initPageData();
  309. }
  310. this.addWatch();
  311. },
  312. async getCardTempDetail() {
  313. const detData = await cardDetailEdit(this.cardId);
  314. const tempData = await cardTempDetail(this.cardId);
  315. this.prepareTcPCard = Object.assign(this.prepareTcPCard, detData);
  316. // 可能存在题卡内容没有记录的情况
  317. if (tempData) {
  318. const cont = JSON.parse(tempData.content);
  319. this.cardDetailId = tempData.id;
  320. this.setPages(cont.pages);
  321. this.setCardConfig(cont.cardConfig);
  322. } else {
  323. // 没有题卡内容时,直接创建新的内容
  324. this.initPageData();
  325. }
  326. },
  327. initPageData() {
  328. this.addNewPage();
  329. this.addNewPage();
  330. this.$nextTick(() => {
  331. this.modifyCardHead({
  332. ...getCardHeadModel(this.cardConfig)
  333. });
  334. });
  335. },
  336. async getCardConfig() {
  337. const data = await cardConfigInfos();
  338. const aOrBSystem = getAOrBSystem(this.prepareTcPCard);
  339. let config = {
  340. ...transformField(data),
  341. pageSize: "A3",
  342. columnNumber: 2,
  343. columnGap: 20,
  344. showForbidArea: true,
  345. cardName: ""
  346. };
  347. if (aOrBSystem !== null) config.aOrBSystem = aOrBSystem;
  348. config.aOrB = !!config["aOrBSystem"];
  349. this.setCardConfig(config);
  350. },
  351. addNewTopic(item) {
  352. let element = getTopicModel(item.type);
  353. element.w = document.getElementById("column-0-0").offsetWidth;
  354. this.setCurElement(element);
  355. this.setOpenElementEditDialog(true);
  356. // to elementPropEdit/ElementPropEdit open topic edit dialog
  357. },
  358. addNewPage() {
  359. const page = getNewPage(this.pages.length, this.cardConfig.columnNumber);
  360. this.addPage(page);
  361. },
  362. // 元件编辑
  363. dragstart(element) {
  364. this.setCurDragElement(getElementModel(element.type));
  365. },
  366. addWatch() {
  367. this.$watch("cardConfig", val => {
  368. const element = getCardHeadModel(val);
  369. this.modifyCardHead(element);
  370. this.$nextTick(() => {
  371. this.rebuildPages();
  372. });
  373. });
  374. },
  375. // 操作
  376. async toPreview() {
  377. await this.toSave(this.$refs.SavePage.getPageModel());
  378. window.open(`/#/card/preview/${this.cardId}/view`);
  379. },
  380. swithPage(pindex) {
  381. if (this.curPageNo === pindex) return;
  382. this.setCurPageNo(pindex);
  383. this.setCurElement({});
  384. },
  385. // save
  386. getCardData(contentTemp = "") {
  387. const tcPCard = this.$objAssign(
  388. {
  389. examId: "",
  390. enablePaperType: "A",
  391. paperAttachmentId: "",
  392. courseName: "",
  393. courseCode: "",
  394. cardSource: "",
  395. title: ""
  396. },
  397. {
  398. ...this.prepareTcPCard,
  399. title: this.cardConfig.cardName,
  400. // todo:A,B最好前端配置一下,以便支持扩展
  401. enablePaperType: this.cardConfig.aOrB ? "A,B" : "A"
  402. }
  403. );
  404. let data = {
  405. tcPCard,
  406. tcPCardDetail: {
  407. content: this.$refs.SavePage.getPageModel()
  408. }
  409. };
  410. if (this.cardDetailId) data.tcPCardDetail.id = this.cardDetailId;
  411. if (contentTemp) data.tcPCardDetail.contentTemp = contentTemp;
  412. return data;
  413. },
  414. async save() {
  415. if (!this.cardConfig.cardName) {
  416. this.$message.error("题卡标题不能为空!");
  417. return Promise.reject("error:题卡标题不能为空");
  418. }
  419. const result = await saveCard(this.getCardData());
  420. this.cardDetailId = result.cardDetailId;
  421. this.$ls.set("cardDetailId", this.cardDetailId);
  422. this.cardId = result.cardId;
  423. },
  424. async toSave() {
  425. await this.save();
  426. this.$message.success("保存成功!");
  427. // 自助创建时暂存任务
  428. if (
  429. this.prepareTcPCard.taskId &&
  430. this.prepareTcPCard.cardSource === "1"
  431. ) {
  432. saveWaitTask({
  433. tcPExamTaskDetail: { ...this.prepareTcPCard, cardId: this.cardId }
  434. });
  435. }
  436. },
  437. registWindowSubmit() {
  438. window.submitCardTemp = async cardContentTemp => {
  439. await submitCard(this.getCardData(cardContentTemp));
  440. this.$message.success("提交成功!");
  441. this.cardPreviewUrl = "";
  442. };
  443. },
  444. async toSubmit() {
  445. await this.save();
  446. this.cardPreviewUrl = `/#/card/preview/${this.cardId}/frame`;
  447. }
  448. },
  449. beforeDestroy() {
  450. this.$ls.remove("cardDetailId");
  451. this.$ls.remove("prepareTcPCard");
  452. this.initState();
  453. }
  454. };
  455. </script>