CardEdit.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. <template>
  2. <div class="card-edit">
  3. <component
  4. v-if="dataReady"
  5. :is="editComp"
  6. ref="CardDesign"
  7. :content="cardContent"
  8. :show-save-btn="!IS_GENERIC"
  9. @on-preview="toPreview"
  10. @on-save="toSave"
  11. @on-submit="toSubmit"
  12. @on-exit="toExit"
  13. ></component>
  14. <!-- card-view-frame -->
  15. <div class="design-preview-frame" v-if="cardPreviewUrl">
  16. <iframe :src="cardPreviewUrl" frameborder="0"></iframe>
  17. </div>
  18. <!-- card-view-dialog -->
  19. <el-dialog
  20. class="card-view-dialog"
  21. :visible.sync="dialogIsShow"
  22. :close-on-click-modal="false"
  23. :close-on-press-escape="false"
  24. append-to-body
  25. fullscreen
  26. @closed="dialogClosed"
  27. >
  28. <iframe
  29. v-if="dialogIsShow && cardDialogPreviewUrl"
  30. :src="cardDialogPreviewUrl"
  31. frameborder="0"
  32. :style="dialogFrameStyle"
  33. ></iframe>
  34. </el-dialog>
  35. </div>
  36. </template>
  37. <script>
  38. import { cardConfigInfos, cardDetail, saveCard } from "../api";
  39. import CardDesign from "../../../../card/components/CardDesign";
  40. import CardFreeDesign from "../../../../card/modules/free/components/CardFreeDesign";
  41. import { examRuleDetail } from "../../base/api";
  42. import { getEnums } from "../../login/api";
  43. export default {
  44. name: "card-edit",
  45. components: {
  46. CardDesign,
  47. CardFreeDesign,
  48. },
  49. props: {
  50. isDialog: {
  51. type: Boolean,
  52. default: false,
  53. },
  54. },
  55. data() {
  56. return {
  57. cardId: this.$route.params.cardId || this.$ls.get("cardId"),
  58. prepareTcPCard: this.$ls.get("prepareTcPCard", {}),
  59. cardType: "CUSTOM",
  60. cardCreateMethod: "STANDARD",
  61. cardContent: {},
  62. cardName: "", // 题卡自定义名称
  63. cardPreviewUrl: "",
  64. canSave: false,
  65. dataReady: false,
  66. // card-view-dialog
  67. dialogIsShow: false,
  68. cardDialogPreviewUrl: "",
  69. dialogFrameStyle: {},
  70. };
  71. },
  72. computed: {
  73. isEdit() {
  74. return !!this.cardId;
  75. },
  76. editComp() {
  77. return this.cardCreateMethod === "STANDARD"
  78. ? "card-design"
  79. : "card-free-design";
  80. },
  81. IS_GENERIC() {
  82. return this.cardType === "GENERIC";
  83. },
  84. },
  85. mounted() {
  86. this.cardCreateMethod = this.prepareTcPCard.createMethod || "STANDARD";
  87. this.cardId = this.cardId || this.prepareTcPCard.id;
  88. this.cardType = this.prepareTcPCard.type || "CUSTOM";
  89. // if (
  90. // !this.prepareTcPCard.examTaskId &&
  91. // !this.isEdit &&
  92. // this.cardType === "CUSTOM"
  93. // ) {
  94. // this.$message.error("找不到命题任务,请退出题卡制作!");
  95. // return;
  96. // }
  97. this.initCard();
  98. this.registWindowSubmit();
  99. // card-view
  100. this.initFrameStyle();
  101. window.addEventListener("resize", this.initFrameStyle);
  102. },
  103. methods: {
  104. initFrameStyle() {
  105. this.dialogFrameStyle = {
  106. width: window.innerWidth + "px",
  107. height: window.innerHeight + "px",
  108. overflow: "auto",
  109. border: "none",
  110. outline: "none",
  111. };
  112. },
  113. async initCard() {
  114. this.dataReady = false;
  115. if (this.isEdit) {
  116. await this.getCardTempDetail();
  117. } else {
  118. let cardConfig = await this.getCardConfig();
  119. if (this.IS_GENERIC) {
  120. cardConfig.cardTitle = this.prepareTcPCard.title;
  121. }
  122. this.cardContent = {
  123. pages: [],
  124. cardConfig,
  125. paperParams: {},
  126. };
  127. this.cardName = "";
  128. }
  129. this.dataReady = true;
  130. },
  131. getCardTitle(titleRule) {
  132. const fieldMap = {
  133. courseCode: this.prepareTcPCard.courseCode || "",
  134. courseName: this.prepareTcPCard.courseName || "",
  135. schoolName: this.prepareTcPCard.schoolName || "",
  136. };
  137. Object.entries(fieldMap).forEach(([key, val]) => {
  138. titleRule = titleRule.replace("${" + key + "}", val);
  139. });
  140. return titleRule;
  141. },
  142. async getCardTempDetail() {
  143. const detData = await cardDetail(this.cardId);
  144. // 可能存在题卡内容没有记录的情况
  145. if (detData.content) {
  146. this.cardContent = JSON.parse(detData.content);
  147. this.cardName = detData.title;
  148. } else {
  149. let cardConfig = await this.getCardConfig();
  150. // 没有题卡内容时,直接创建新的内容
  151. if (detData.makeMethod === "CUST" || detData.type === "GENERIC") {
  152. cardConfig.cardTitle = detData.title;
  153. }
  154. this.cardContent = {
  155. pages: [],
  156. cardConfig,
  157. paperParams: {},
  158. };
  159. this.cardName = "";
  160. }
  161. },
  162. async getCardConfig() {
  163. if (this.cardCreateMethod === "STANDARD") {
  164. const data = await cardConfigInfos(this.prepareTcPCard.cardRuleId);
  165. if (!data) {
  166. this.$message.error("找不到题卡规则!");
  167. return Promise.reject();
  168. }
  169. let config = {
  170. ...data,
  171. ...{
  172. pageSize: "A3",
  173. columnNumber: 2,
  174. columnGap: 20,
  175. showForbidArea: false,
  176. cardDesc: "",
  177. makeMethod: this.prepareTcPCard.makeMethod,
  178. },
  179. };
  180. config.fillNumber = data.examNumberDigit;
  181. config.aOrB = false; // 默认关闭A/B卷型,2023-01-31改
  182. config.requiredFields = JSON.parse(config.requiredFields);
  183. config.extendFields = JSON.parse(config.extendFields);
  184. config.cardTitle = this.getCardTitle(config.titleRule);
  185. return config;
  186. } else {
  187. let requiredFields = await getEnums("REQUIRED_FIELDS");
  188. requiredFields = requiredFields
  189. .filter((item) => item.enable)
  190. .map((item) => {
  191. return {
  192. code: item.code,
  193. name: item.name,
  194. enable: item.enable,
  195. };
  196. });
  197. const examRule = await examRuleDetail();
  198. const extendFields = examRule.extendFields || "[]";
  199. let config = {
  200. pageSize: "A3",
  201. columnNumber: 2,
  202. columnGap: 20,
  203. showForbidArea: false,
  204. cardDesc: "",
  205. requiredFields,
  206. extendFields: JSON.parse(extendFields),
  207. };
  208. return config;
  209. }
  210. },
  211. // 操作
  212. getRequestConfig() {
  213. return this.prepareTcPCard.makeMethod === "CUST"
  214. ? {
  215. headers: {
  216. schoolId: this.prepareTcPCard.schoolId,
  217. },
  218. }
  219. : {};
  220. },
  221. getCardInfo(data) {
  222. let cardInfo = {
  223. ...data,
  224. ...this.prepareTcPCard,
  225. title: this.cardName,
  226. };
  227. if (this.cardId) cardInfo.id = this.cardId;
  228. return cardInfo;
  229. },
  230. // card preview dialog
  231. dialogClosed() {
  232. this.cardDialogPreviewUrl = "";
  233. },
  234. toPreview(cardData) {
  235. window.cardData = {
  236. ...cardData,
  237. createMethod: this.cardCreateMethod,
  238. type: this.cardType,
  239. };
  240. const { href } = this.$router.resolve({
  241. name: "CardPreview",
  242. params: {
  243. cardId: 1,
  244. viewType: "frame-view",
  245. },
  246. query: {
  247. t: Date.now(),
  248. },
  249. });
  250. this.cardDialogPreviewUrl = href;
  251. this.dialogIsShow = true;
  252. },
  253. // save
  254. async toSave(datas) {
  255. if (!this.cardName) {
  256. const res = await this.$prompt("请输入题卡名称", "提示", {
  257. type: "warning",
  258. showInput: true,
  259. inputPlaceholder: "请输入题卡名称",
  260. inputValue: this.cardName,
  261. inputValidator: (val) => {
  262. if (!val) return "请输入题卡名称!";
  263. if (val.length > 50) return "题卡名称不得超过50个字符!";
  264. return true;
  265. },
  266. }).catch(() => {});
  267. if (!res || res.action !== "confirm") {
  268. this.$refs.CardDesign.unloading();
  269. return;
  270. }
  271. this.cardName = res.value;
  272. }
  273. let cardInfo = this.getCardInfo(datas);
  274. cardInfo.status = "STAGE";
  275. const result = await saveCard(cardInfo, this.getRequestConfig()).catch(
  276. () => {}
  277. );
  278. this.$refs.CardDesign.unloading();
  279. if (!result) return;
  280. this.cardId = result;
  281. this.$ls.set("cardId", this.cardId);
  282. this.$message.success("保存成功!");
  283. return this.cardId;
  284. },
  285. async toSubmit(cardData) {
  286. const res = await this.$prompt("确定要提交当前题卡吗?", "提示", {
  287. type: "warning",
  288. showInput: true,
  289. inputPlaceholder: "请输入题卡名称",
  290. inputValue: this.cardName,
  291. inputValidator: (val) => {
  292. if (!val) return "请输入题卡名称!";
  293. if (val.length > 50) return "题卡名称不得超过50个字符!";
  294. return true;
  295. },
  296. }).catch(() => {});
  297. if (!res || res.action !== "confirm") return;
  298. this.cardName = res.value;
  299. this.$refs.CardDesign.loading();
  300. window.cardData = {
  301. ...cardData,
  302. createMethod: this.cardCreateMethod,
  303. type: this.cardType,
  304. };
  305. const { href } = this.$router.resolve({
  306. name: "CardPreview",
  307. params: {
  308. cardId: 1,
  309. viewType: "frame",
  310. },
  311. query: {
  312. t: Date.now(),
  313. },
  314. });
  315. this.cardPreviewUrl = href;
  316. },
  317. registWindowSubmit() {
  318. window.submitCardTemp = async (htmlContent, model) => {
  319. if (!htmlContent || !model) {
  320. this.$refs.CardDesign.unloading();
  321. window.cardData = null;
  322. this.cardPreviewUrl = "";
  323. this.$message.error("提交失败,请重新尝试!");
  324. return;
  325. }
  326. this.cardPreviewUrl = "";
  327. const datas = this.$refs.CardDesign.getCardData(htmlContent, model);
  328. const cardInfo = this.getCardInfo(datas);
  329. cardInfo.status = "SUBMIT";
  330. const result = await saveCard(cardInfo, this.getRequestConfig()).catch(
  331. () => {}
  332. );
  333. this.$refs.CardDesign.unloading();
  334. window.cardData = null;
  335. if (result) {
  336. this.cardName = "";
  337. this.cardId = result;
  338. this.$ls.set("cardId", this.cardId);
  339. this.canSave = false;
  340. this.$message.success("提交成功!");
  341. this.goback();
  342. } else {
  343. this.$message.error("提交失败,请重新尝试!");
  344. }
  345. };
  346. },
  347. toExit() {
  348. this.$confirm(
  349. "请确保当前题卡已经正常保存,确定要退出当前题卡编辑吗?",
  350. "提示",
  351. {
  352. type: "warning",
  353. }
  354. )
  355. .then(() => {
  356. this.goback();
  357. })
  358. .catch(() => {});
  359. },
  360. goback() {
  361. if (this.isDialog) {
  362. this.$emit("exit", this.cardId);
  363. } else {
  364. this.$router.go(-1);
  365. }
  366. },
  367. },
  368. beforeDestroy() {
  369. this.$ls.remove("cardId");
  370. this.$ls.remove("prepareTcPCard");
  371. window.removeEventListener("resize", this.initFrameStyle);
  372. delete window.submitCardTemp;
  373. },
  374. };
  375. </script>