ExamStructure.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. <template>
  2. <section class="content" style="margin-top: 20px;">
  3. <div class="box box-info">
  4. <!-- 正文信息 -->
  5. <div class="box-body">
  6. <el-form
  7. :model="formSearch"
  8. :inline="true"
  9. label-position="right"
  10. label-width="100px"
  11. >
  12. <el-form-item label="学校">
  13. <el-select
  14. v-model="formSearch.orgId"
  15. placeholder="请选择"
  16. clearable
  17. @change="loadExamList(formSearch.orgId)"
  18. >
  19. <el-option
  20. v-for="item in orgList"
  21. :label="item.orgName"
  22. :value="item.orgId"
  23. :key="item.orgId"
  24. ></el-option>
  25. </el-select>
  26. </el-form-item>
  27. <el-form-item label="考试">
  28. <el-select
  29. v-model="formSearch.examId"
  30. placeholder="请选择"
  31. @change="searchRecords(1)"
  32. clearable
  33. >
  34. <el-option
  35. v-for="item in examList"
  36. :label="item.examName"
  37. :value="item.examId"
  38. :key="item.examId"
  39. ></el-option>
  40. </el-select>
  41. </el-form-item>
  42. <el-form-item>
  43. <el-button
  44. size="small"
  45. type="primary"
  46. icon="el-icon-search"
  47. @click="searchRecords(1)"
  48. >查询
  49. </el-button>
  50. <el-button
  51. size="small"
  52. type="primary"
  53. icon="el-icon-plus"
  54. :disabled="!hasPermit"
  55. v-show="!checkEmptyNumber(formSearch.orgId)"
  56. @click="openAddStructureDialog"
  57. >新增
  58. </el-button>
  59. </el-form-item>
  60. </el-form>
  61. <!-- 数据列表 -->
  62. <el-table
  63. v-loading="loading"
  64. :data="tableData"
  65. element-loading-text="数据加载中"
  66. style="width:100%;"
  67. border
  68. stripe
  69. >
  70. <el-table-column label="学校名称" prop="orgName" />
  71. <el-table-column label="考试名称" prop="examName" />
  72. <el-table-column width="220" label="题数">
  73. <template slot-scope="scope">
  74. 单选:{{ scope.row.questionStructure.singleChoiceTotal }}<br />
  75. 多选:{{ scope.row.questionStructure.multipleChoiceTotal }}<br />
  76. 判断:{{ scope.row.questionStructure.boolQuestionTotal }}
  77. </template>
  78. </el-table-column>
  79. <el-table-column width="200px" label="操作" :context="_self">
  80. <template slot-scope="scope">
  81. <el-button
  82. size="mini"
  83. icon="el-icon-menu"
  84. @click="openCloneStructureDialog(scope.row)"
  85. :disabled="!hasPermit"
  86. plain
  87. >复用
  88. </el-button>
  89. <el-button
  90. size="mini"
  91. type="danger"
  92. icon="el-icon-delete"
  93. @click="removeStructure(scope.row)"
  94. :disabled="!hasPermit"
  95. plain
  96. >删除
  97. </el-button>
  98. </template>
  99. </el-table-column>
  100. </el-table>
  101. <!-- 分页 -->
  102. <div class="page pull-right">
  103. <el-pagination
  104. @current-change="handlePagerNo"
  105. :current-page="formSearch.pageNo"
  106. @size-change="handlePagerSize"
  107. :page-size="formSearch.pageSize"
  108. :page-sizes="[10, 20, 50, 100]"
  109. :total="totalElements"
  110. layout="total, sizes, prev, pager, next, jumper"
  111. ></el-pagination>
  112. </div>
  113. </div>
  114. <!-- 新增考试结构弹窗 -->
  115. <el-dialog
  116. title="新增考试结构"
  117. width="400px"
  118. :visible.sync="addStructureDialog"
  119. @close="closeAddStructureDialog"
  120. >
  121. <el-form
  122. :model="addStructureForm"
  123. ref="addStructureForm"
  124. :rules="rules"
  125. label-position="right"
  126. label-width="100px"
  127. inline-message
  128. >
  129. <el-form-item label="学校名称" prop="orgId">
  130. <el-select
  131. v-model="addStructureForm.orgId"
  132. placeholder="请选择"
  133. :disabled="true"
  134. class="w220"
  135. >
  136. <el-option
  137. v-for="item in orgList"
  138. :label="item.orgName"
  139. :value="item.orgId"
  140. :key="item.orgId"
  141. ></el-option>
  142. </el-select>
  143. </el-form-item>
  144. <el-form-item label="考试名称" prop="examId">
  145. <el-select
  146. v-model="addStructureForm.examId"
  147. placeholder="请选择"
  148. class="w220"
  149. >
  150. <el-option
  151. v-for="item in examList"
  152. :label="item.examName"
  153. :value="item.examId"
  154. :key="item.examId"
  155. ></el-option>
  156. </el-select>
  157. </el-form-item>
  158. <el-form-item label="单选题数量" prop="singleChoiceTotal">
  159. <el-input
  160. v-model="addStructureForm.questionStructure.singleChoiceTotal"
  161. class="w220"
  162. ></el-input>
  163. </el-form-item>
  164. <el-form-item label="多选题数量" prop="multipleChoiceTotal">
  165. <el-input
  166. v-model="addStructureForm.questionStructure.multipleChoiceTotal"
  167. class="w220"
  168. ></el-input>
  169. </el-form-item>
  170. <el-form-item label="判断题数量" prop="boolQuestionTotal">
  171. <el-input
  172. v-model="addStructureForm.questionStructure.boolQuestionTotal"
  173. class="w220"
  174. ></el-input>
  175. </el-form-item>
  176. <div style="text-align: center">
  177. <el-button type="primary" @click="addStructure">确 定</el-button>
  178. <el-button @click="closeAddStructureDialog">取 消</el-button>
  179. </div>
  180. </el-form>
  181. </el-dialog>
  182. <!-- 复用考试结构弹窗 -->
  183. <el-dialog
  184. title="复用考试结构"
  185. width="450px"
  186. :visible.sync="cloneStructureDialog"
  187. @close="closeCloneStructureDialog"
  188. >
  189. <el-form
  190. :model="cloneStructureForm"
  191. ref="cloneStructureForm"
  192. :rules="rules"
  193. label-position="right"
  194. label-width="80px"
  195. inline-message
  196. >
  197. <el-tabs v-model="sourceTab">
  198. <el-tab-pane label="原结构信息" name="first">
  199. <el-form-item label="学校名称" prop="orgId">
  200. <el-select
  201. v-model="cloneStructureForm.orgId"
  202. placeholder="请选择"
  203. :disabled="true"
  204. class="w220"
  205. >
  206. <el-option
  207. v-for="item in orgList"
  208. :label="item.orgName"
  209. :value="item.orgId"
  210. :key="item.orgId"
  211. ></el-option>
  212. </el-select>
  213. </el-form-item>
  214. <el-form-item label="考试名称" prop="examId">
  215. <el-select
  216. v-model="cloneStructureForm.examId"
  217. placeholder="请选择"
  218. :disabled="true"
  219. class="w220"
  220. >
  221. <el-option
  222. v-for="item in examList"
  223. :label="item.examName"
  224. :value="item.examId"
  225. :key="item.examId"
  226. ></el-option>
  227. </el-select>
  228. </el-form-item>
  229. </el-tab-pane>
  230. </el-tabs>
  231. <el-tabs v-model="targetTab">
  232. <el-tab-pane label="新结构信息" name="first">
  233. <el-form-item label="学校名称" prop="newOrgId">
  234. <el-select
  235. v-model="cloneStructureForm.newOrgId"
  236. @change="loadCloneExamList(cloneStructureForm.newOrgId)"
  237. placeholder="请选择"
  238. class="w220"
  239. >
  240. <el-option
  241. v-for="item in orgList"
  242. :label="item.orgName"
  243. :value="item.orgId"
  244. :key="item.orgId"
  245. ></el-option>
  246. </el-select>
  247. </el-form-item>
  248. <el-form-item label="考试名称" prop="newExamId">
  249. <el-select
  250. v-model="cloneStructureForm.newExamId"
  251. placeholder="请选择"
  252. class="w220"
  253. >
  254. <el-option
  255. v-for="item in cloneExamList"
  256. :label="item.examName"
  257. :value="item.examId"
  258. :key="item.examId"
  259. ></el-option>
  260. </el-select>
  261. </el-form-item>
  262. </el-tab-pane>
  263. </el-tabs>
  264. <div style="text-align: center">
  265. <el-button type="primary" @click="cloneStructure">确 定</el-button>
  266. <el-button @click="closeCloneStructureDialog">取 消</el-button>
  267. </div>
  268. </el-form>
  269. </el-dialog>
  270. </div>
  271. </section>
  272. </template>
  273. <script>
  274. import { PRINT_API } from "@/constants/constants";
  275. import {} from "../constants/constants.js";
  276. import { mapState } from "vuex";
  277. import { checkEmptyNumber } from "../utils/common.js";
  278. export default {
  279. data() {
  280. let validateSingleChoiceTotal = (rule, value, callback) => {
  281. if (
  282. checkEmptyNumber(
  283. this.addStructureForm.questionStructure.singleChoiceTotal
  284. )
  285. ) {
  286. callback(new Error("请输入正确的数值!"));
  287. return;
  288. }
  289. if (this.addStructureForm.questionStructure.singleChoiceTotal > 1000) {
  290. callback(new Error("请输入合理有效的数值!"));
  291. return;
  292. }
  293. callback();
  294. };
  295. let validateMultipleChoiceTotal = (rule, value, callback) => {
  296. if (
  297. checkEmptyNumber(
  298. this.addStructureForm.questionStructure.multipleChoiceTotal
  299. )
  300. ) {
  301. callback(new Error("请输入正确的数值!"));
  302. return;
  303. }
  304. if (this.addStructureForm.questionStructure.multipleChoiceTotal > 1000) {
  305. callback(new Error("请输入合理有效的数值!"));
  306. return;
  307. }
  308. callback();
  309. };
  310. let validateBoolChoiceTotal = (rule, value, callback) => {
  311. if (
  312. checkEmptyNumber(
  313. this.addStructureForm.questionStructure.boolQuestionTotal
  314. )
  315. ) {
  316. callback(new Error("请输入正确的数值!"));
  317. return;
  318. }
  319. if (this.addStructureForm.questionStructure.boolQuestionTotal > 1000) {
  320. callback(new Error("请输入合理有效的数值!"));
  321. return;
  322. }
  323. callback();
  324. };
  325. return {
  326. formSearch: {
  327. orgId: "",
  328. examId: "",
  329. pageNo: 1,
  330. pageSize: 10
  331. },
  332. curUserRole: {},
  333. hasPermit: false,
  334. totalElements: 0,
  335. loading: false,
  336. tableData: [],
  337. orgList: [],
  338. examList: [],
  339. addStructureDialog: false,
  340. addStructureForm: {
  341. examId: "",
  342. examName: "",
  343. orgId: "",
  344. orgName: "",
  345. questionStructure: {
  346. singleChoiceTotal: 0,
  347. multipleChoiceTotal: 0,
  348. boolQuestionTotal: 0
  349. }
  350. },
  351. cloneStructureDialog: false,
  352. cloneExamList: [],
  353. sourceTab: "first",
  354. targetTab: "first",
  355. cloneStructureForm: {
  356. examId: "",
  357. orgId: "",
  358. newExamId: "",
  359. newExamName: "",
  360. newOrgId: "",
  361. newOrgName: ""
  362. },
  363. rules: {
  364. orgId: [
  365. { required: true, message: "学校不能为空!", trigger: "change" }
  366. ],
  367. examId: [
  368. { required: true, message: "考试不能为空!", trigger: "change" }
  369. ],
  370. newOrgId: [
  371. { required: true, message: "学校不能为空!", trigger: "change" }
  372. ],
  373. newExamId: [
  374. { required: true, message: "考试不能为空!", trigger: "change" }
  375. ],
  376. singleChoiceTotal: [
  377. {
  378. required: true,
  379. validator: validateSingleChoiceTotal,
  380. trigger: "change"
  381. }
  382. ],
  383. multipleChoiceTotal: [
  384. {
  385. required: true,
  386. validator: validateMultipleChoiceTotal,
  387. trigger: "change"
  388. }
  389. ],
  390. boolQuestionTotal: [
  391. {
  392. required: true,
  393. validator: validateBoolChoiceTotal,
  394. trigger: "change"
  395. }
  396. ]
  397. }
  398. };
  399. },
  400. methods: {
  401. handlePagerNo(pageNo) {
  402. /* 处理分页 */
  403. this.searchRecords(pageNo);
  404. },
  405. handlePagerSize(pageSize) {
  406. /* 处理每页条数 */
  407. this.formSearch.pageSize = pageSize;
  408. this.searchRecords(1);
  409. },
  410. searchRecords(pageNo) {
  411. this.formSearch.pageNo = pageNo;
  412. /* 查询记录列表 */
  413. let orgId = this.formSearch.orgId;
  414. if (checkEmptyNumber(orgId)) {
  415. this.$notify({
  416. title: "提示",
  417. message: "请选择学校!",
  418. type: "warning"
  419. });
  420. return;
  421. }
  422. this.loading = true;
  423. let url = PRINT_API + "/examStructure/list";
  424. this.$http.post(url, this.formSearch).then(
  425. response => {
  426. this.tableData = response.data.content;
  427. this.totalElements = response.data.totalElements;
  428. this.loading = false;
  429. },
  430. error => {
  431. console.log(error);
  432. this.loading = false;
  433. }
  434. );
  435. },
  436. selectDefault() {
  437. if (this.orgList.length > 0) {
  438. let firstOrgId = this.orgList[0].orgId;
  439. this.formSearch.orgId = firstOrgId;
  440. this.loadExamList(firstOrgId);
  441. }
  442. },
  443. loadOrgList() {
  444. /* 查询学校列表 */
  445. let url = PRINT_API + "/printing/project/org/list";
  446. this.$http.post(url).then(
  447. response => {
  448. this.orgList = response.data;
  449. this.selectDefault();
  450. },
  451. error => {
  452. console.log(error.response);
  453. // ignore
  454. }
  455. );
  456. },
  457. loadExamList(orgId) {
  458. /* 查询考试列表 */
  459. this.formSearch.examId = "";
  460. this.examList = [];
  461. this.tableData = [];
  462. if (!checkEmptyNumber(orgId)) {
  463. let url = PRINT_API + "/printing/project/exam/list?orgId=" + orgId;
  464. this.$http.post(url).then(response => {
  465. this.examList = response.data;
  466. if (this.examList.length > 0) {
  467. this.formSearch.examId = this.examList[0].examId;
  468. this.searchRecords(1);
  469. }
  470. });
  471. }
  472. },
  473. loadCloneExamList(orgId) {
  474. /* 查询考试列表 */
  475. this.cloneStructureForm.newExamId = "";
  476. this.cloneExamList = [];
  477. if (!checkEmptyNumber(orgId)) {
  478. let url = PRINT_API + "/printing/project/exam/list?orgId=" + orgId;
  479. this.$http.post(url).then(response => {
  480. this.cloneExamList = response.data;
  481. });
  482. }
  483. },
  484. getOrgNameById(orgList, orgId) {
  485. for (let i = 0; i < orgList.length; i++) {
  486. if (orgList[i].orgId == orgId) {
  487. return orgList[i].orgName;
  488. }
  489. }
  490. return "";
  491. },
  492. getExamNameById(examList, examId) {
  493. for (let i = 0; i < examList.length; i++) {
  494. if (examList[i].examId == examId) {
  495. return examList[i].examName;
  496. }
  497. }
  498. return "";
  499. },
  500. openAddStructureDialog() {
  501. /* 打开考试结构弹窗 */
  502. this.addStructureDialog = true;
  503. this.addStructureForm.orgId = this.formSearch.orgId;
  504. this.addStructureForm.examId = this.formSearch.examId;
  505. this.addStructureForm.questionStructure.singleChoiceTotal = 0;
  506. this.addStructureForm.questionStructure.multipleChoiceTotal = 0;
  507. this.addStructureForm.questionStructure.boolQuestionTotal = 0;
  508. },
  509. closeAddStructureDialog() {
  510. /* 关闭考试结构弹窗 */
  511. this.addStructureDialog = false;
  512. },
  513. addStructure() {
  514. /* 新增考试结构 */
  515. this.$refs.addStructureForm.validate(valid => {
  516. if (!valid) {
  517. return false;
  518. }
  519. this.addStructureForm.orgName = this.getOrgNameById(
  520. this.orgList,
  521. this.addStructureForm.orgId
  522. );
  523. this.addStructureForm.examName = this.getExamNameById(
  524. this.examList,
  525. this.addStructureForm.examId
  526. );
  527. let curLoading = this.$loading({
  528. lock: true,
  529. text: "数据保存中!"
  530. });
  531. let url = PRINT_API + "/examStructure/save";
  532. this.$http.post(url, this.addStructureForm).then(
  533. () => {
  534. curLoading.close();
  535. this.$notify({
  536. title: "提示",
  537. message: "考试结构新增成功!",
  538. type: "success"
  539. });
  540. this.addStructureDialog = false;
  541. this.searchRecords(1);
  542. },
  543. error => {
  544. console.log(error.response);
  545. curLoading.close();
  546. this.$notify({
  547. title: "错误",
  548. type: "error",
  549. message: error.response.data.desc
  550. });
  551. }
  552. );
  553. });
  554. },
  555. openCloneStructureDialog(row) {
  556. /* 打开复用考试结构弹窗 */
  557. this.cloneStructureDialog = true;
  558. this.cloneStructureForm.orgId = row.orgId;
  559. this.cloneStructureForm.examId = row.examId;
  560. this.cloneStructureForm.newOrgId = "";
  561. this.cloneStructureForm.newExamId = "";
  562. },
  563. closeCloneStructureDialog() {
  564. /* 关闭复用考试结构弹窗 */
  565. this.cloneStructureDialog = false;
  566. },
  567. cloneStructure() {
  568. /* 复用考试结构 */
  569. this.$refs.cloneStructureForm.validate(valid => {
  570. if (!valid) {
  571. return false;
  572. }
  573. this.cloneStructureForm.newOrgName = this.getOrgNameById(
  574. this.orgList,
  575. this.cloneStructureForm.newOrgId
  576. );
  577. this.cloneStructureForm.newExamName = this.getExamNameById(
  578. this.cloneExamList,
  579. this.cloneStructureForm.newExamId
  580. );
  581. let curLoading = this.$loading({
  582. lock: true,
  583. text: "数据保存中!"
  584. });
  585. let url = PRINT_API + "/examStructure/clone";
  586. this.$http.post(url, this.cloneStructureForm).then(
  587. () => {
  588. curLoading.close();
  589. this.$notify({
  590. title: "提示",
  591. message: "考试结构复用成功!",
  592. type: "success"
  593. });
  594. this.cloneStructureDialog = false;
  595. this.searchRecords(1);
  596. },
  597. error => {
  598. console.log(error.response);
  599. curLoading.close();
  600. this.$notify({
  601. title: "错误",
  602. type: "error",
  603. message: error.response.data.desc
  604. });
  605. }
  606. );
  607. });
  608. },
  609. removeStructure(row) {
  610. /* 删除考试结构 */
  611. this.$confirm("确定删除当前结构吗?", "提示", {
  612. confirmButtonText: "确定",
  613. cancelButtonText: "取消",
  614. type: "warning"
  615. })
  616. .then(() => {
  617. let url = PRINT_API + "/examStructure/delete/" + row.id;
  618. this.$http.post(url).then(
  619. () => {
  620. this.$notify({
  621. title: "提示",
  622. type: "success",
  623. message: "删除当前结构成功!"
  624. });
  625. this.searchRecords(1);
  626. },
  627. error => {
  628. console.log(error.response);
  629. this.$notify({
  630. title: "错误",
  631. type: "error",
  632. message: error.response.data.desc
  633. });
  634. }
  635. );
  636. })
  637. .catch(() => {
  638. /*ignore*/
  639. });
  640. },
  641. checkEmptyNumber: checkEmptyNumber
  642. },
  643. computed: {
  644. ...mapState({ user: state => state.user })
  645. },
  646. created() {
  647. this.loadOrgList();
  648. this.loadUserRole(this.user);
  649. if (this.curUserRole.isSuperLeader || this.curUserRole.isPM) {
  650. this.hasPermit = true;
  651. } else {
  652. this.hasPermit = false;
  653. }
  654. }
  655. };
  656. </script>
  657. <style scoped>
  658. .page {
  659. margin-top: 10px;
  660. }
  661. .pull-right {
  662. float: right;
  663. }
  664. .pull-left {
  665. float: left;
  666. }
  667. .w220 {
  668. width: 220px;
  669. }
  670. </style>