Course.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. <template>
  2. <section
  3. v-loading.body="fileLoading"
  4. v-loading.fullscreen="loading"
  5. element-loading-text="请稍后..."
  6. class="content"
  7. >
  8. <div class="part-box">
  9. <h2 class="part-box-title">课程列表</h2>
  10. <el-form
  11. ref="primaryForm"
  12. class="part-filter-form"
  13. inline
  14. :model="formSearch"
  15. >
  16. <el-form-item label="课程名称">
  17. <el-input v-model="formSearch.name" placeholder="请输入课程名称" />
  18. </el-form-item>
  19. <el-form-item label="课程代码">
  20. <el-input v-model="formSearch.code" placeholder="请输入课程代码" />
  21. </el-form-item>
  22. <el-form-item label="课程状态">
  23. <el-select v-model="formSearch.enable" placeholder="请选择" clearable>
  24. <el-option
  25. v-for="item in statusList"
  26. :key="item.value"
  27. :label="item.label"
  28. :value="item.value"
  29. />
  30. </el-select>
  31. </el-form-item>
  32. <el-form-item label="层次">
  33. <el-select v-model="formSearch.level" placeholder="请选择" clearable>
  34. <el-option
  35. v-for="item in levelList"
  36. :key="item.value"
  37. :label="item.label"
  38. :value="item.value"
  39. />
  40. </el-select>
  41. </el-form-item>
  42. <el-form-item>
  43. <el-button type="danger" @click="handleSearchBtn">查询</el-button>
  44. <el-button type="danger" plain @click="resetPrimaryForm">
  45. 重置
  46. </el-button>
  47. </el-form-item>
  48. </el-form>
  49. <div class="part-box-action">
  50. <div>
  51. <el-button
  52. type="danger"
  53. plain
  54. :disabled="noBatchSelected"
  55. icon="icon icon-play"
  56. @click="deleteByIds"
  57. >删除
  58. </el-button>
  59. <el-button
  60. type="primary"
  61. plain
  62. icon="icon icon-import"
  63. @click="impCourse"
  64. >导入</el-button
  65. >
  66. <el-button
  67. type="primary"
  68. plain
  69. icon="icon icon-export"
  70. @click="exportCourse"
  71. >导出</el-button
  72. >
  73. </div>
  74. <el-button
  75. type="primary"
  76. icon="icon icon-plus-white"
  77. @click="insertCourse"
  78. >新增</el-button
  79. >
  80. </div>
  81. </div>
  82. <div class="part-box">
  83. <!-- 页面列表 -->
  84. <!-- FIXME: element-ui style bug https://github.com/ElemeFE/element/issues/16167 -->
  85. <el-table :data="tableData" resizable @selection-change="selectChange">
  86. <el-table-column type="selection" width="50" align="center" />
  87. <el-table-column prop="id" label="课程ID" width="80" />
  88. <el-table-column prop="name" label="课程名称" width="180" />
  89. <el-table-column prop="code" label="课程代码" />
  90. <el-table-column label="层次">
  91. <span slot-scope="scope">{{ getLevel(scope.row.level) }}</span>
  92. </el-table-column>
  93. <el-table-column width="50" label="状态">
  94. <span slot-scope="scope">
  95. <span v-if="scope.row.enable">
  96. <el-tooltip
  97. class="item"
  98. effect="dark"
  99. content="启用"
  100. placement="left"
  101. >
  102. <i class="icon icon-right"></i>
  103. </el-tooltip>
  104. </span>
  105. <span v-else>
  106. <el-tooltip
  107. class="item"
  108. effect="dark"
  109. content="禁用"
  110. placement="left"
  111. >
  112. <i class="icon icon-error"></i>
  113. </el-tooltip>
  114. </span>
  115. </span>
  116. </el-table-column>
  117. <el-table-column
  118. sortable
  119. prop="updateTime"
  120. label="更新时间"
  121. width="170"
  122. />
  123. <el-table-column label="操作" width="160">
  124. <div slot-scope="scope">
  125. <!-- <el-button
  126. size="mini"
  127. type="primary"
  128. plain
  129. icon="el-icon-share"
  130. @click="relation(scope.row)"
  131. >
  132. 关联专业
  133. </el-button> -->
  134. <el-button
  135. size="mini"
  136. type="primary"
  137. plain
  138. @click="editCourse(scope.row)"
  139. >
  140. 编辑
  141. </el-button>
  142. <el-button
  143. size="mini"
  144. plain
  145. type="danger"
  146. @click="deleteById(scope.row)"
  147. >
  148. 删除
  149. </el-button>
  150. </div>
  151. </el-table-column>
  152. </el-table>
  153. <div class="part-page">
  154. <el-pagination
  155. :current-page="currentPage"
  156. :page-size="10"
  157. :page-sizes="[10, 20, 50, 100, 200, 300]"
  158. layout="total, sizes, prev, pager, next, jumper"
  159. :total="total"
  160. @current-change="handleCurrentChange"
  161. @size-change="handleSizeChange"
  162. >
  163. </el-pagination>
  164. </div>
  165. </div>
  166. <!-- 添加或新增课程弹出框 -->
  167. <el-dialog
  168. title="课程"
  169. width="520px"
  170. :visible.sync="courseDialog"
  171. :modal="false"
  172. append-to-body
  173. custom-class="side-dialog"
  174. @close="dialogBeforeClose"
  175. >
  176. <el-form
  177. ref="courseForm"
  178. :inline="true"
  179. inline-message
  180. class="form-tight"
  181. :model="courseForm"
  182. :rules="rules"
  183. label-width="90px"
  184. >
  185. <el-form-item label="课程代码" prop="code">
  186. <el-input
  187. v-model="courseForm.code"
  188. :disabled="null != courseForm.id"
  189. class="dialog-input-width"
  190. auto-complete="off"
  191. placeholder="请输入课程代码"
  192. />
  193. </el-form-item>
  194. <el-form-item label="课程名称" prop="name">
  195. <el-input
  196. v-model="courseForm.name"
  197. class="dialog-input-width"
  198. auto-complete="off"
  199. placeholder="请输入课程名称"
  200. />
  201. </el-form-item>
  202. <el-form-item label="层次" prop="level">
  203. <el-select
  204. v-model="courseForm.level"
  205. class="dialog-input-width"
  206. placeholder="请选择"
  207. >
  208. <el-option
  209. v-for="item in levelList"
  210. :key="item.value"
  211. :label="item.label"
  212. :value="item.value"
  213. />
  214. </el-select>
  215. </el-form-item>
  216. <el-form-item label="课程状态" prop="enable">
  217. <el-radio-group
  218. v-model="courseForm.enable"
  219. class="dialog-input-width"
  220. >
  221. <el-radio label="true">启用</el-radio>
  222. <el-radio label="false">禁用</el-radio>
  223. </el-radio-group>
  224. </el-form-item>
  225. </el-form>
  226. <div slot="footer">
  227. <el-button type="primary" @click="submitForm">保存</el-button>
  228. <el-button type="danger" plain @click="courseDialog = false"
  229. >取消</el-button
  230. >
  231. </div>
  232. </el-dialog>
  233. <!-- 导入弹窗 -->
  234. <el-dialog
  235. title="导入课程"
  236. width="520px"
  237. :visible.sync="impDialog"
  238. :modal="false"
  239. append-to-body
  240. custom-class="side-dialog"
  241. >
  242. <el-form>
  243. <el-form-item style="margin-left: 20px">
  244. <el-upload
  245. ref="upload"
  246. class="form_left"
  247. accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  248. :action="uploadAction"
  249. :headers="uploadHeaders"
  250. :data="uploadData"
  251. :before-upload="beforeUpload"
  252. :on-progress="uploadProgress"
  253. :on-success="uploadSuccess"
  254. :on-error="uploadError"
  255. :file-list="fileList"
  256. :auto-upload="false"
  257. :multiple="false"
  258. >
  259. <el-button
  260. slot="trigger"
  261. size="small"
  262. type="primary"
  263. icon="icon icon-search-white"
  264. >
  265. 选择文件
  266. </el-button>
  267. <el-button
  268. size="small"
  269. type="primary"
  270. icon="icon icon-save-white"
  271. @click="submitUpload"
  272. >
  273. 确认上传
  274. </el-button>
  275. <el-button
  276. size="small"
  277. type="primary"
  278. icon="icon icon-delete-white"
  279. @click="removeFile"
  280. >
  281. 清空文件
  282. </el-button>
  283. <el-button
  284. size="small"
  285. type="primary"
  286. icon="icon icon-export-white"
  287. @click="exportFile"
  288. >
  289. 下载模板
  290. </el-button>
  291. <div slot="tip" class="el-upload__tip">只能上传xlsx文件</div>
  292. </el-upload>
  293. </el-form-item>
  294. </el-form>
  295. </el-dialog>
  296. <!-- 导入错误信息列表 -->
  297. <el-dialog title="错误提示" :visible.sync="errDialog" append-to-body>
  298. <div
  299. v-for="errMessage in errMessages"
  300. :key="errMessage.lineNum"
  301. class="text-danger"
  302. >
  303. 第{{ errMessage.lineNum }}行:{{ errMessage.msg }}
  304. </div>
  305. <span slot="footer" class="dialog-footer">
  306. <el-button @click="errDialog = false">确定</el-button>
  307. </span>
  308. </el-dialog>
  309. </section>
  310. </template>
  311. <script>
  312. import { QUESTION_API } from "@/constants/constants.js";
  313. import { ENABLE_TYPE, LEVEL_TYPE } from "../constants/constants.js";
  314. import { mapState } from "vuex";
  315. export default {
  316. name: "Course",
  317. data() {
  318. return {
  319. formSearch: {
  320. name: "",
  321. code: "",
  322. enable: "",
  323. level: "",
  324. },
  325. courseForm: {
  326. id: null,
  327. name: "",
  328. code: "",
  329. level: "ALL",
  330. enable: "true",
  331. },
  332. statusList: ENABLE_TYPE,
  333. levelList: LEVEL_TYPE,
  334. selectedCourseIds: [],
  335. tableData: [],
  336. currentPage: 1,
  337. pageSize: 10,
  338. total: 10,
  339. impDialog: false,
  340. uploadAction: QUESTION_API + "/course/import",
  341. uploadHeaders: {},
  342. uploadData: {},
  343. errMessages: [],
  344. errDialog: false,
  345. fileLoading: false,
  346. loading: false,
  347. fileList: [],
  348. courseDialog: false,
  349. relationDialog: false,
  350. rules: {
  351. name: [
  352. {
  353. required: true,
  354. message: "请输入课程名称",
  355. trigger: "blur",
  356. },
  357. {
  358. max: 30,
  359. message: "课程名称最多30个字符",
  360. trigger: "blur",
  361. },
  362. ],
  363. code: [
  364. {
  365. required: true,
  366. message: "请输入课程代码",
  367. trigger: "blur",
  368. },
  369. {
  370. max: 30,
  371. message: "课程代码最多30个字符",
  372. trigger: "blur",
  373. },
  374. ],
  375. level: [
  376. {
  377. required: true,
  378. message: "请选择层次",
  379. trigger: "change",
  380. },
  381. ],
  382. status: [
  383. {
  384. required: true,
  385. message: "请选择状态",
  386. trigger: "change",
  387. },
  388. ],
  389. },
  390. };
  391. },
  392. computed: {
  393. ...mapState({
  394. user: (state) => state.user,
  395. }),
  396. noBatchSelected() {
  397. return this.selectedCourseIds.length === 0;
  398. },
  399. courseIds() {
  400. var courseIds = "";
  401. for (let courseId of this.selectedCourseIds) {
  402. if (!courseIds) {
  403. courseIds += courseId;
  404. } else {
  405. courseIds += "," + courseId;
  406. }
  407. }
  408. return courseIds;
  409. },
  410. },
  411. //初始化查询
  412. created() {
  413. this.searchForm();
  414. this.uploadHeaders = {
  415. key: this.user.key,
  416. token: this.user.token,
  417. };
  418. },
  419. methods: {
  420. handleSizeChange(val) {
  421. this.pageSize = val;
  422. this.currentPage = 1;
  423. this.searchForm();
  424. },
  425. dialogBeforeClose() {
  426. this.$refs.courseForm.clearValidate();
  427. },
  428. submitAddRelationForm() {
  429. this.$refs.addRelationForm.validate((valid) => {
  430. if (valid) {
  431. var param = new URLSearchParams(this.addRelationForm);
  432. var url = QUESTION_API + "/courseSpeciatlyRelation/add?" + param;
  433. this.$httpWithMsg.post(url, this.speciallyForm).then(() => {
  434. this.$notify({
  435. type: "success",
  436. message: "添加成功!",
  437. });
  438. this.addRelationDialog = false;
  439. });
  440. } else {
  441. console.log("error submit!");
  442. return false;
  443. }
  444. });
  445. },
  446. getTag(status) {
  447. if (status == true) {
  448. return "success";
  449. } else if (status == false) {
  450. return "danger";
  451. }
  452. return status;
  453. },
  454. getLevel(level) {
  455. if (level == "ZSB") {
  456. return "专升本";
  457. } else if (level == "GQZ") {
  458. return "高起专";
  459. } else if (level == "GQB") {
  460. return "高起本";
  461. } else {
  462. return "不限";
  463. }
  464. },
  465. handleSearchBtn() {
  466. this.currentPage = 1;
  467. this.searchForm();
  468. },
  469. searchForm() {
  470. let searchLock = true;
  471. setTimeout(() => {
  472. if (searchLock) {
  473. this.loading = true;
  474. }
  475. }, 500);
  476. var param = new URLSearchParams(this.formSearch);
  477. var url =
  478. QUESTION_API +
  479. "/course/coursePage/" +
  480. this.currentPage +
  481. "/" +
  482. this.pageSize +
  483. "?" +
  484. param;
  485. this.$httpWithMsg
  486. .get(url)
  487. .then((response) => {
  488. this.tableData = response.data.content;
  489. this.total = response.data.totalElements;
  490. })
  491. .finally(() => {
  492. searchLock = false;
  493. this.loading = false;
  494. });
  495. },
  496. handleCurrentChange(val) {
  497. this.currentPage = val;
  498. this.searchForm();
  499. },
  500. selectChange(row) {
  501. this.selectedCourseIds = [];
  502. row.forEach((element) => {
  503. this.selectedCourseIds.push(element.id);
  504. });
  505. console.log(this.selectedCourseIds);
  506. },
  507. //新增
  508. insertCourse() {
  509. this.courseForm.id = null;
  510. this.courseForm.name = null;
  511. this.courseForm.code = null;
  512. this.courseForm.level = "ALL";
  513. this.courseForm.enable = "true";
  514. this.courseDialog = true;
  515. },
  516. //修改
  517. editCourse(row) {
  518. this.courseForm = Object.assign({}, row);
  519. this.courseForm.enable = row.enable ? "true" : "false";
  520. this.courseId = row.id;
  521. this.courseDialog = true;
  522. },
  523. exportCourse() {
  524. var param = new URLSearchParams(this.formSearch);
  525. window.open(
  526. QUESTION_API +
  527. "/course/export?$key=" +
  528. this.user.key +
  529. "&$token=" +
  530. this.user.token +
  531. "&" +
  532. param
  533. );
  534. },
  535. closeCourse() {
  536. this.courseDialog = false;
  537. },
  538. saveRelation() {},
  539. //保存(新增/修改)
  540. submitForm() {
  541. var url = QUESTION_API + "/course";
  542. if (null != this.courseForm.id) {
  543. //修改
  544. this.$refs.courseForm.validate((valid) => {
  545. if (valid) {
  546. this.$httpWithMsg.put(url, this.courseForm).then(() => {
  547. this.$notify({
  548. type: "success",
  549. message: "修改成功!",
  550. });
  551. this.searchForm();
  552. this.resetForm();
  553. this.courseDialog = false;
  554. });
  555. } else {
  556. console.log("error submit!");
  557. return false;
  558. }
  559. });
  560. } else {
  561. this.$refs.courseForm.validate((valid) => {
  562. if (valid) {
  563. this.$httpWithMsg.post(url, this.courseForm).then(() => {
  564. this.$notify({
  565. type: "success",
  566. message: "添加成功",
  567. });
  568. this.searchForm();
  569. this.resetForm();
  570. this.courseDialog = false;
  571. });
  572. } else {
  573. console.log("error submit!");
  574. return false;
  575. }
  576. });
  577. }
  578. },
  579. //重置
  580. resetForm() {
  581. this.$refs.courseForm.resetFields();
  582. },
  583. resetPrimaryForm() {
  584. this.formSearch = {
  585. name: "",
  586. code: "",
  587. enable: "",
  588. level: "",
  589. };
  590. this.$refs.primaryForm.resetFields();
  591. },
  592. //删除单个数据
  593. deleteById(row) {
  594. this.$confirm("是否删除该课程?", "提示", {
  595. confirmButtonText: "确定",
  596. cancelButtonText: "取消",
  597. type: "warning",
  598. }).then(() => {
  599. var url = QUESTION_API + "/course/" + row.id;
  600. this.$httpWithMsg.delete(url).then(() => {
  601. this.$notify({
  602. type: "success",
  603. message: "删除成功!",
  604. });
  605. this.searchForm();
  606. });
  607. });
  608. },
  609. //删除多条数据
  610. deleteByIds() {
  611. if (this.selectedCourseIds.length === 0) {
  612. this.$notify({
  613. type: "warning",
  614. message: "请选择要删除的课程",
  615. });
  616. } else {
  617. this.$confirm("是否删除这些课程?", "提示", {
  618. confirmButtonText: "确定",
  619. cancelButtonText: "取消",
  620. type: "error",
  621. }).then(() => {
  622. var url = QUESTION_API + "/course/delete";
  623. this.$httpWithMsg
  624. .post(
  625. url,
  626. new URLSearchParams({ courseIds: this.selectedCourseIds })
  627. )
  628. .then(() => {
  629. this.$notify({
  630. type: "success",
  631. message: "删除成功!",
  632. });
  633. this.searchForm();
  634. });
  635. });
  636. }
  637. },
  638. //启用
  639. enableByIds() {
  640. if (this.selectedCourseIds.length === 0) {
  641. this.$notify({
  642. type: "warning",
  643. message: "请选择要启用的课程",
  644. });
  645. } else {
  646. this.$confirm("是否启用这些课程?", "提示", {
  647. confirmButtonText: "确定",
  648. cancelButtonText: "取消",
  649. type: "warning",
  650. }).then(() => {
  651. var url = QUESTION_API + "/course/enable/" + this.courseIds;
  652. this.$httpWithMsg.put(url, {}).then(() => {
  653. this.$notify({
  654. type: "success",
  655. message: "启用成功!",
  656. });
  657. this.searchForm();
  658. });
  659. });
  660. }
  661. },
  662. enableById(row) {
  663. this.$confirm("是否启用该课程?", "提示", {
  664. confirmButtonText: "确定",
  665. cancelButtonText: "取消",
  666. type: "warning",
  667. }).then(() => {
  668. var url = QUESTION_API + "/course/enable/" + row.id;
  669. this.$httpWithMsg.put(url, {}).then(() => {
  670. this.$notify({
  671. type: "success",
  672. message: "启用成功!",
  673. });
  674. this.searchForm();
  675. });
  676. });
  677. },
  678. //禁用
  679. disableByIds() {
  680. if (this.selectedCourseIds.length === 0) {
  681. this.$notify({
  682. type: "warning",
  683. message: "请选择要禁用的课程",
  684. });
  685. } else {
  686. this.$confirm("是否禁用这些课程?", "提示", {
  687. confirmButtonText: "确定",
  688. cancelButtonText: "取消",
  689. type: "error",
  690. }).then(() => {
  691. var url = QUESTION_API + "/course/disable/" + this.courseIds;
  692. this.$httpWithMsg.put(url, {}).then(() => {
  693. this.$notify({
  694. type: "success",
  695. message: "禁用成功!",
  696. });
  697. this.searchForm();
  698. });
  699. });
  700. }
  701. },
  702. //禁用
  703. disableById(row) {
  704. this.$confirm("是否禁用该课程?", "提示", {
  705. confirmButtonText: "确定",
  706. cancelButtonText: "取消",
  707. type: "error",
  708. }).then(() => {
  709. var url = QUESTION_API + "/course/disable/" + row.id;
  710. return this.$httpWithMsg.put(url, {}).then(() => {
  711. this.$notify({
  712. type: "success",
  713. message: "禁用成功!",
  714. });
  715. return this.searchForm();
  716. });
  717. });
  718. },
  719. //导入
  720. impCourse() {
  721. this.impDialog = true;
  722. this.initUpload();
  723. },
  724. initUpload() {
  725. this.fileList = [];
  726. },
  727. beforeUpload(file) {
  728. console.log(file);
  729. },
  730. uploadProgress() {
  731. console.log("uploadProgress");
  732. },
  733. uploadSuccess(response) {
  734. if (!response.hasError) {
  735. this.$notify({
  736. message: "上传成功",
  737. type: "success",
  738. });
  739. this.fileLoading = false;
  740. this.impDialog = false;
  741. this.searchForm();
  742. } else {
  743. this.fileLoading = false;
  744. this.impDialog = false;
  745. this.errMessages = response.failRecords;
  746. this.errDialog = true;
  747. }
  748. },
  749. uploadError(response) {
  750. var json = JSON.parse(response.message);
  751. if (response.status == 500) {
  752. this.$notify({
  753. message: json.desc,
  754. type: "error",
  755. });
  756. }
  757. this.fileLoading = false;
  758. },
  759. //确定上传
  760. submitUpload() {
  761. if (!this.checkUpload()) {
  762. return false;
  763. }
  764. this.$refs.upload.submit();
  765. this.fileLoading = true;
  766. },
  767. checkUpload() {
  768. var fileList = this.$refs.upload.uploadFiles;
  769. if (fileList.length == 0) {
  770. this.$notify({
  771. message: "上传文件不能为空",
  772. type: "error",
  773. });
  774. return false;
  775. }
  776. if (fileList.length > 1) {
  777. this.$notify({
  778. message: "每次只能上传一个文件",
  779. type: "error",
  780. });
  781. return false;
  782. }
  783. for (let file of fileList) {
  784. if (!file.name.endsWith(".xlsx")) {
  785. this.$notify({
  786. message: "上传文件必须为xlsx格式",
  787. type: "error",
  788. });
  789. this.initUpload();
  790. return false;
  791. }
  792. }
  793. return true;
  794. },
  795. //清空文件
  796. removeFile() {
  797. // this.fileList = [];
  798. this.$refs.upload.clearFiles();
  799. },
  800. //下载模板
  801. exportFile() {
  802. window.location.href =
  803. QUESTION_API +
  804. "/course/importTemplate?$key=" +
  805. this.user.key +
  806. "&$token=" +
  807. this.user.token;
  808. },
  809. },
  810. };
  811. </script>