AddPaperSelect.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. <template>
  2. <section class="content add-paper-select">
  3. <div>
  4. <LinkTitlesCustom
  5. :current-paths="['题库管理 ', '卷库管理', '抽题模板管理']"
  6. />
  7. </div>
  8. <div class="box-body">
  9. <div class="top">
  10. <div class="flex items-center">
  11. <p>课程名称:{{ $route.query.courseName }}</p>
  12. <p style="margin-left: 100px">
  13. 课程代码:{{ $route.query.courseNo }}
  14. </p>
  15. </div>
  16. <el-button type="primary" size="small" @click="save">保存</el-button>
  17. </div>
  18. <div class="form">
  19. <el-form
  20. ref="form"
  21. :rules="rules"
  22. :model="form"
  23. label-position="left"
  24. label-width="120px"
  25. >
  26. <el-form-item label="组卷模板名称:" prop="name">
  27. <el-input v-model="form.name" style="width: 300px"></el-input>
  28. </el-form-item>
  29. <el-form-item label="选择组卷模式:" required>
  30. <el-radio-group
  31. v-model="form.paperStructType"
  32. @change="paperStructTypeChange"
  33. >
  34. <el-radio label="EXACT">精确结构</el-radio>
  35. <el-radio label="BLUEPRINT">蓝图结构</el-radio>
  36. </el-radio-group>
  37. </el-form-item>
  38. <el-form-item label="选择组卷结构:" prop="paperStructId">
  39. <el-select v-model="form.paperStructId">
  40. <el-option
  41. v-for="item in options1"
  42. :key="item.id"
  43. :label="item.name"
  44. :value="item.id"
  45. ></el-option>
  46. </el-select>
  47. <p style="display: inline-block; margin-left: 40px">
  48. 难度:<span style="color: #409eff; font-weight: bold">{{
  49. curStruct?.difficulty
  50. }}</span>
  51. </p>
  52. </el-form-item>
  53. <el-table
  54. v-loading="tableLoading1"
  55. :data="tableData1"
  56. border
  57. style="margin-top: 10px; margin-bottom: 20px; width: 700px"
  58. >
  59. <el-table-column
  60. v-for="(item, index) in tableColumns1"
  61. :key="index"
  62. :label="item.label"
  63. :prop="item.prop"
  64. :min-width="item.minWidth"
  65. >
  66. <template slot-scope="scope">
  67. <span
  68. v-if="
  69. !['hardInfo', 'mediumInfo', 'easyInfo'].includes(item.prop)
  70. "
  71. >{{ scope.row[item.prop] }}</span
  72. >
  73. <span v-else>{{ scope.row[item.prop]?.count }}</span>
  74. </template>
  75. </el-table-column>
  76. </el-table>
  77. <div class="flex" style="margin-top: 30px">
  78. <div style="width: 40%">
  79. <el-form-item label="选择题源范围:" style="margin-bottom: 0">
  80. <el-select
  81. v-model="form.paperType"
  82. placeholder="题源选择"
  83. style="width: 120px"
  84. @change="changePaperType"
  85. >
  86. <el-option label="题库来源" value="IMPORT"></el-option>
  87. <el-option label="卷库来源" value="GENERATE"></el-option>
  88. </el-select>
  89. <span
  90. v-if="checked && !multipleSelection.length"
  91. class="red"
  92. style="font-size: 12px; font-weight: bold; margin-left: 10px"
  93. >请选择数据</span
  94. >
  95. </el-form-item>
  96. <el-table
  97. ref="table2"
  98. :data="tableData2"
  99. border
  100. @selection-change="handleSelectionChange"
  101. >
  102. <el-table-column
  103. type="selection"
  104. width="55"
  105. :selectable="canSelect"
  106. >
  107. </el-table-column>
  108. <el-table-column
  109. v-for="(item, index) in tableColumns2"
  110. :key="index"
  111. :label="item.label"
  112. :prop="item.prop"
  113. :width="item.width"
  114. >
  115. </el-table-column>
  116. </el-table>
  117. </div>
  118. <div style="width: 60%; padding-left: 20px">
  119. <div style="color: #606266; font-size: 14px; line-height: 32px">
  120. <span>选中范围预览:</span>
  121. <span
  122. v-if="hasError()"
  123. class="red"
  124. style="font-size: 12px; font-weight: bold"
  125. >不满足最低要求</span
  126. >
  127. </div>
  128. <el-table
  129. v-loading="tableLoading3"
  130. :data="tableData3"
  131. border
  132. style="margin-top: 8px"
  133. >
  134. <el-table-column
  135. v-for="(item, index) in tableColumns3"
  136. :key="index"
  137. :label="item.label"
  138. :prop="item.prop"
  139. >
  140. <template slot-scope="scope">
  141. <span
  142. v-if="
  143. !['hardInfo', 'mediumInfo', 'easyInfo'].includes(
  144. item.prop
  145. )
  146. "
  147. :class="{ red: hasNumError(scope.row, item.prop) }"
  148. >{{ scope.row[item.prop] }}</span
  149. >
  150. <span
  151. v-else
  152. :class="{ red: hasNumError(scope.row, item.prop) }"
  153. >{{ scope.row[item.prop]?.count }}</span
  154. >
  155. </template>
  156. </el-table-column>
  157. </el-table>
  158. </div>
  159. </div>
  160. </el-form>
  161. </div>
  162. </div>
  163. </section>
  164. </template>
  165. <script>
  166. import qs from "qs";
  167. import { mapState } from "vuex";
  168. export default {
  169. data() {
  170. return {
  171. form: {
  172. paperStructType: "BLUEPRINT",
  173. name: "",
  174. paperStructId: "",
  175. paperType: "IMPORT",
  176. paperIds: "",
  177. },
  178. paperIdsArr: [],
  179. options1: [],
  180. tableData1: [],
  181. tableColumns1: [
  182. { label: "题型", prop: "detailName", minWidth: "100" },
  183. { label: "总分", prop: "totalScore", minWidth: "80" },
  184. { label: "数量", prop: "totalCount", minWidth: "80" },
  185. { label: "难", prop: "hardInfo", minWidth: "80" },
  186. { label: "中", prop: "mediumInfo", minWidth: "80" },
  187. { label: "易", prop: "easyInfo", minWidth: "80" },
  188. ],
  189. tableData2: [],
  190. tableColumns2: [
  191. { label: "名称", prop: "name" },
  192. { label: "小题数量", prop: "unitCount", width: "100px" },
  193. ],
  194. multipleSelection: [],
  195. tableData3: [],
  196. tableColumns3: [
  197. { label: "题型", prop: "detailName", minWidth: "100" },
  198. { label: "数量", prop: "totalCount", minWidth: "80" },
  199. { label: "难", prop: "hardInfo", minWidth: "80" },
  200. { label: "中", prop: "mediumInfo", minWidth: "80" },
  201. { label: "易", prop: "easyInfo", minWidth: "80" },
  202. ],
  203. lastRequestKey: "",
  204. tableLoading3: false,
  205. tableLoading1: false,
  206. checked: false,
  207. initSelectedRows: [],
  208. };
  209. },
  210. computed: {
  211. ...mapState({ user: (state) => state.user }),
  212. curStruct() {
  213. if (this.form.paperStructId) {
  214. return this.options1.find(
  215. (item) => item.id === this.form.paperStructId
  216. );
  217. } else {
  218. return {};
  219. }
  220. },
  221. rules() {
  222. return {
  223. name: { required: true, message: "请输入模板名称", trigger: "change" },
  224. paperStructId: {
  225. required: true,
  226. message: "请选择组卷结构",
  227. trigger: "change",
  228. },
  229. };
  230. },
  231. },
  232. watch: {
  233. "form.paperStructId"() {
  234. this.structChange();
  235. },
  236. // "form.paperStructType"() {
  237. // this.form.paperStructId = "";
  238. // this.getStruct();
  239. // this.tableData1 = [];
  240. // this.$refs.table2.clearSelection();
  241. // },
  242. multipleSelection(val) {
  243. this.form.paperIds = val.map((item) => item.id).join(",");
  244. },
  245. },
  246. async created() {
  247. let res = await this.getTplData();
  248. if (res) {
  249. this.getStruct();
  250. this.getTable2(true);
  251. }
  252. },
  253. methods: {
  254. paperStructTypeChange() {
  255. this.form.paperStructId = "";
  256. this.getStruct();
  257. this.tableData1 = [];
  258. this.$refs.table2.clearSelection();
  259. },
  260. async getTplData() {
  261. const { id } = this.$route.params;
  262. if (id) {
  263. try {
  264. const data = await this.$http.post(
  265. "/api/ecs_ques/randompaper/info",
  266. null,
  267. {
  268. params: {
  269. id,
  270. },
  271. }
  272. );
  273. const tplData = data.data;
  274. this.paperIdsArr = tplData.paperIds || [];
  275. Object.assign(this.form, tplData || {}, {
  276. paperIds: (tplData.paperIds || []).join(","),
  277. });
  278. return true;
  279. } catch (e) {
  280. return false;
  281. }
  282. } else {
  283. return true;
  284. }
  285. },
  286. save() {
  287. this.checked = true;
  288. this.$refs.form.validate((valid) => {
  289. if (valid) {
  290. if (this.multipleSelection.length && !this.hasError()) {
  291. let params = {
  292. courseId: this.$route.query.courseId,
  293. ...this.form,
  294. rootOrgId: this.user.rootOrgId,
  295. };
  296. if (this.$route.params.id) {
  297. params.id = this.$route.params.id;
  298. }
  299. this.$http
  300. .post("/api/ecs_ques/randompaper/save", qs.stringify(params))
  301. .then(() => {
  302. this.$message.success("保存成功");
  303. this.$router.back();
  304. });
  305. }
  306. } else {
  307. console.log("error submit!!");
  308. return false;
  309. }
  310. });
  311. },
  312. canSelect() {
  313. return !!this.form.paperStructId;
  314. },
  315. structChange() {
  316. this.tableLoading1 = true;
  317. this.$http
  318. .post("/api/ecs_ques/randompaper/struct/question/info", null, {
  319. params: { structId: this.form.paperStructId },
  320. headers: { "content-type": "application/x-www-form-urlencoded" },
  321. })
  322. .then((res) => {
  323. this.tableData1 = res.data.structQuestionInfo || [];
  324. this.tableLoading1 = false;
  325. });
  326. },
  327. getStruct() {
  328. let apiUrl = "/api/ecs_ques/paperStruct/1/10000";
  329. let params =
  330. this.form.paperStructType == "EXACT"
  331. ? { courseNo: "ALL", type: "EXACT" }
  332. : { type: "BLUEPRINT" };
  333. this.$http.get(apiUrl, { params }).then((res) => {
  334. this.options1 = res.data.content;
  335. });
  336. },
  337. getTable2(bool) {
  338. let apiUrl =
  339. this.form.paperType === "IMPORT"
  340. ? "/api/ecs_ques/importPaper/huoge/1/10000"
  341. : "/api/ecs_ques/genPaper/huoge/1/10000";
  342. this.$http
  343. .get(apiUrl, {
  344. params: { courseNo: this.$route.query.courseNo, ids: "" },
  345. })
  346. .then((res) => {
  347. this.tableData2 = res.data.content || [];
  348. if (bool) {
  349. this.tableData2.forEach((item) => {
  350. if (this.paperIdsArr.includes(item.id)) {
  351. this.initSelectedRows.push(item);
  352. }
  353. });
  354. setTimeout(() => {
  355. this.initSelectedRows.forEach((item) => {
  356. this.$refs.table2.toggleRowSelection(item, true);
  357. });
  358. }, 0);
  359. }
  360. });
  361. },
  362. changePaperType() {
  363. this.tableData2 = [];
  364. this.multipleSelection = [];
  365. this.getTable2();
  366. },
  367. getTable3() {
  368. let paperIds = this.multipleSelection.map((item) => item.id);
  369. if (!paperIds.length) {
  370. this.tableData3 = [];
  371. this.tableLoading3 = false;
  372. return false;
  373. }
  374. this.tableLoading3 = true;
  375. let str = new Date().getTime() + "";
  376. this.lastRequestKey = str;
  377. this.$http
  378. .post(
  379. "/api/ecs_ques/randompaper/struct/question/view/info",
  380. qs.stringify({
  381. paperIds: paperIds.join(","),
  382. structId: this.form.paperStructId,
  383. }),
  384. {
  385. headers: { "content-type": "application/x-www-form-urlencoded" },
  386. }
  387. )
  388. .then((res) => {
  389. if (this.lastRequestKey === str) {
  390. this.tableData3 = res.data.structQuestionInfo || [];
  391. this.tableLoading3 = false;
  392. }
  393. });
  394. },
  395. handleSelectionChange(val) {
  396. this.multipleSelection = val;
  397. this.getTable3();
  398. },
  399. hasNumError(row, prop) {
  400. if (prop === "detailName") {
  401. return false;
  402. } else {
  403. let targetName = row.detailName;
  404. let find = this.tableData1.find(
  405. (item) => item.detailName === targetName
  406. );
  407. if (!find) {
  408. return false;
  409. } else {
  410. if (prop === "totalCount") {
  411. return row[prop] < find[prop];
  412. } else {
  413. return row[prop].count < find[prop].count;
  414. }
  415. }
  416. }
  417. },
  418. hasError() {
  419. return (
  420. this.tableData1.length &&
  421. this.tableData3.length &&
  422. this.tableData3.every((item) => {
  423. return (
  424. this.hasNumError(item, "totalCount") ||
  425. this.hasNumError(item, "hardInfo") ||
  426. this.hasNumError(item, "mediumInfo") ||
  427. this.hasNumError(item, "easyInfo")
  428. );
  429. })
  430. );
  431. },
  432. },
  433. };
  434. </script>
  435. <style lang="scss" scoped>
  436. .add-paper-select {
  437. * {
  438. box-sizing: border-box;
  439. }
  440. .el-form .el-form-item {
  441. margin-bottom: 18px;
  442. }
  443. ::v-deep .red {
  444. color: #f56c6c;
  445. }
  446. p {
  447. margin: 0;
  448. }
  449. .top {
  450. display: flex;
  451. align-items: center;
  452. justify-content: space-between;
  453. padding-bottom: 15px;
  454. border-bottom: 1px solid #eee;
  455. p {
  456. font-size: 14px;
  457. }
  458. }
  459. .box-body {
  460. background-color: #fff;
  461. border-radius: 6px;
  462. padding: 15px 10px;
  463. .form {
  464. margin-top: 15px;
  465. }
  466. }
  467. }
  468. </style>