ManageCourse.vue 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278
  1. <script setup>
  2. import { computed, onMounted, onUnmounted, watch, ref } from "vue";
  3. import { useRouter } from 'vue-router';
  4. import { Search } from '@element-plus/icons-vue';
  5. import { ElMessage } from 'element-plus';
  6. const router = useRouter();
  7. import { schoolTree, schoolAdd, schoolItem, schoolUpdate, schoolDelete } from "../../api/admin/school";
  8. import { courseList, courseAdd, courseDetail, courseUpdate, courseDelete } from "../../api/admin/course";
  9. // let state = ref({
  10. // listParams: {
  11. // keyword: '',
  12. // page: 1,
  13. // limit: 5,
  14. // },
  15. // list: [],
  16. // total: 0,
  17. // showDialog: false,
  18. // ids: [],
  19. // showDel: false,
  20. // });
  21. let state = ref({
  22. // 提交分页查询数据
  23. "listParams": {
  24. // 搜索的内容
  25. "keyword": "",
  26. // 第几页
  27. "page": 1,
  28. // 每页显示多少数据
  29. "limit": 5
  30. },
  31. // 具体分页的数据
  32. "list": [
  33. // {
  34. // "id": 661434193309765,
  35. // "createTime": "2025-04-03 10:42:39",
  36. // "name": "课程_1",
  37. // },
  38. // {
  39. // "id": 661434193309766,
  40. // "createTime": "2025-04-03 10:42:39",
  41. // "name": "课程_2",
  42. // },
  43. // {
  44. // "id": 661434193309767,
  45. // "createTime": "2025-04-03 10:42:39",
  46. // "name": "课程_3",
  47. // },
  48. // {
  49. // "id": 661434193309768,
  50. // "createTime": "2025-04-03 10:42:39",
  51. // "name": "课程_4",
  52. // },
  53. // {
  54. // "id": 661434193309769,
  55. // "createTime": "2025-04-03 10:42:39",
  56. // "name": "课程_5",
  57. // },
  58. // {
  59. // "id": 661434193309770,
  60. // "createTime": "2025-04-03 10:42:39",
  61. // "name": "课程_6",
  62. // },
  63. ],
  64. // 用户总数量
  65. "total": 0,
  66. // 是否弹出添加UI
  67. "showDialog": false,
  68. // 当前分页列表,多选的数据
  69. "ids": [],
  70. // 批量删除模态框
  71. "showDel": false,
  72. // 详情模态框
  73. "showDetails": false,
  74. });
  75. // 用于表单验证规则必须要的对象
  76. let ruleFormRef = ref();
  77. // 父子级的学校到班级的数据参数
  78. let options = [
  79. // {
  80. // id: 1,
  81. // name: 'Asia',
  82. // children: [
  83. // {
  84. // id: 2,
  85. // name: 'China',
  86. // children: [
  87. // { id: 3, name: 'Beijing' },
  88. // { id: 4, name: 'Shanghai' },
  89. // { id: 5, name: 'Hangzhou' },
  90. // ],
  91. // },
  92. // {
  93. // id: 6,
  94. // name: 'Japan',
  95. // children: [
  96. // { id: 7, name: 'Tokyo' },
  97. // { id: 8, name: 'Osaka' },
  98. // { id: 9, name: 'Kyoto' },
  99. // ],
  100. // },
  101. // {
  102. // id: 10,
  103. // name: 'Korea',
  104. // children: [
  105. // { id: 11, name: 'Seoul' },
  106. // { id: 12, name: 'Busan' },
  107. // { id: 13, name: 'Taegu' },
  108. // ],
  109. // },
  110. // ],
  111. // },
  112. // {
  113. // id: 14,
  114. // name: 'Europe',
  115. // children: [
  116. // {
  117. // id: 15,
  118. // name: 'France',
  119. // children: [
  120. // { id: 16, name: 'Paris' },
  121. // { id: 17, name: 'Marseille' },
  122. // { id: 18, name: 'Lyon' },
  123. // ],
  124. // },
  125. // {
  126. // id: 19,
  127. // name: 'UK',
  128. // children: [
  129. // { id: 20, name: 'London' },
  130. // { id: 21, name: 'Birmingham' },
  131. // { id: 22, name: 'Manchester' },
  132. // ],
  133. // },
  134. // ],
  135. // },
  136. // {
  137. // id: 23,
  138. // name: 'North America',
  139. // children: [
  140. // {
  141. // id: 24,
  142. // name: 'US',
  143. // children: [
  144. // { id: 25, name: 'New York' },
  145. // { id: 26, name: 'Los Angeles' },
  146. // { id: 27, name: 'Washington' },
  147. // ],
  148. // },
  149. // {
  150. // id: 28,
  151. // name: 'Canada',
  152. // children: [
  153. // { id: 29, name: 'Toronto' },
  154. // { id: 30, name: 'Montreal' },
  155. // { id: 31, name: 'Ottawa' },
  156. // ],
  157. // },
  158. // ],
  159. // },
  160. ]
  161. /**
  162. * 添加,最后提交的数据
  163. */
  164. let addParams = ref({
  165. name: '',
  166. classIds: [],
  167. });
  168. /**
  169. * 初始化添加的数据
  170. */
  171. const addParamsInit = () => {
  172. addParams.value = {
  173. name: '',
  174. classIds:
  175. [
  176. // 29,
  177. // 30,
  178. // 31
  179. ],
  180. }
  181. }
  182. /**
  183. * 添加,字段相关验证规则
  184. */
  185. let addRules = ref({
  186. name: [
  187. { required: true, message: '请填写课程', trigger: 'blur' },
  188. ],
  189. classIds: [
  190. {
  191. required: true,
  192. message: '请选择班级',
  193. trigger: 'change',
  194. },
  195. // {
  196. // required: true,
  197. // validator: (rule, value, callback) => {
  198. // if (value && value.length > 0) {
  199. // callback()
  200. // } else {
  201. // callback(new Error('请选择班级'))
  202. // }
  203. // },
  204. // trigger: 'change'
  205. // }
  206. ],
  207. });
  208. /**
  209. * 详情数据
  210. */
  211. let detailsDb = ref({
  212. id: null,
  213. name: null,
  214. classIds: [],
  215. });
  216. /**
  217. * 因为这里的接口返回的不是分页的接口,是全部数据,所以这里就记录全部数据
  218. * 后面通过逻辑,来控制分页的显示
  219. */
  220. let pageDb = [];
  221. /**
  222. * 查找分页数据
  223. */
  224. const pageUpdateEvent = () => {
  225. // console.log(
  226. // "查找分页数据 pageUpdateEvent", state.value.listParams
  227. // );
  228. let keyword = state.value.listParams.keyword;
  229. // let submit = {
  230. // // 默认优先去顶级所有,传 0 。 否则你要看到 学院, 专业 ,班级 的列表,这里固定就传 学院这个的id值
  231. // "schoolId" : 0,
  232. // };
  233. // schoolTree(submit)
  234. // .then(response => {
  235. // let data = response?.data?.data;
  236. // // console.log(
  237. // // "schoolTreeEvent response", response, data
  238. // // );
  239. // pageDb = [];
  240. // // 重新拼接合理要展示的分页数据
  241. // for (let i = 0; i < data.length; i++) {
  242. // let thisData = data[i];
  243. // if (typeof keyword == 'string' && keyword != '' && keyword != ' ') {
  244. // if (typeof thisData.name == 'string' && thisData.name.indexOf(keyword) >= 0) {
  245. // pageDb.push(thisData);
  246. // }
  247. // } else {
  248. // pageDb.push(thisData);
  249. // }
  250. // }
  251. // pageDbUpdateEvent();
  252. // }).catch(error => {
  253. // });
  254. /**
  255. * 看起来像是分页的接口,实际有bug
  256. * 不管如何传参,都是全部数据返回
  257. */
  258. let submit = {
  259. // "dto" : JSON.stringify({
  260. // // 第几页
  261. // "pageNum": 1,
  262. // // 每页显示多少数据
  263. // "pageSize": 3,
  264. // // 搜索的名称
  265. // "name": ""
  266. // })
  267. // 第几页
  268. "pageNum": 1,
  269. // 每页显示多少数据
  270. "pageSize": 5,
  271. // 搜索的名称
  272. "name": ""
  273. };
  274. courseList(submit)
  275. .then(response => {
  276. let dataOld = response?.data?.data;
  277. // console.log(
  278. // "courseListEvent response", response, dataOld
  279. // );
  280. let data = dataOld?.list;
  281. pageDb = [];
  282. // 重新拼接合理要展示的分页数据
  283. for (let i = 0; i < data.length; i++) {
  284. let thisData = data[i];
  285. if (typeof keyword == 'string' && keyword != '' && keyword != ' ') {
  286. if (typeof thisData.name == 'string' && thisData.name.indexOf(keyword) >= 0) {
  287. pageDb.push(thisData);
  288. }
  289. } else {
  290. pageDb.push(thisData);
  291. }
  292. }
  293. pageDbUpdateEvent();
  294. }).catch(error => {
  295. });
  296. }
  297. /**
  298. * 因为接口不是标准的分页接口,是直接返回所有数据的
  299. * 这里进行单独处理,来展示第几页的数据
  300. */
  301. const pageDbUpdateEvent = () => {
  302. // console.log(
  303. // "查找分页数据 pageDbUpdateEvent", state.value.listParams, pageDb
  304. // );
  305. state.value.total = pageDb.length;
  306. state.value.list = [];
  307. let page = state.value.listParams.page;
  308. let limit = state.value.listParams.limit;
  309. let keyword = state.value.listParams.keyword;
  310. // 从第几个数组下标开始
  311. let indexStart = (page - 1) * limit;
  312. // 到第几个数组下标结束
  313. let indexEnd = indexStart + limit - 1;
  314. for (let i = 0; i < pageDb.length; i++) {
  315. // 在范围内
  316. if (i >= indexStart && i <= indexEnd) {
  317. let thisPageDb = pageDb[i];
  318. state.value.list.push(thisPageDb);
  319. }
  320. }
  321. }
  322. /**
  323. * 弹出添加UI
  324. */
  325. const btnAddStudent = (formEl) => {
  326. addParamsInit();
  327. state.value.showDialog = true;
  328. if (!formEl) {
  329. return;
  330. }
  331. formEl.resetFields();
  332. }
  333. /**
  334. * 触发搜索逻辑
  335. */
  336. const btnSearchName = () => {
  337. pageUpdateEvent();
  338. }
  339. /**
  340. * 上一页,下一页改变
  341. */
  342. const handleSizeChange = (val) => {
  343. // 重新查找分页数据
  344. pageUpdateEvent();
  345. }
  346. /**
  347. * 上一页,下一页改变
  348. */
  349. const handleCurrentChange = (val) => {
  350. // 重新查找分页数据
  351. pageUpdateEvent();
  352. }
  353. /**
  354. * 分页多选,更新多选的相关数据
  355. * @param res
  356. */
  357. const selectionChange = (res) => {
  358. state.value.ids = [];
  359. res.forEach((item) => {
  360. // state.value.ids.push(item.id);
  361. state.value.ids.push(item);
  362. });
  363. console.log("分页多选,更新多选的相关数据", state.value.ids);
  364. }
  365. /**
  366. * 添加 点击提交
  367. * @param formEl
  368. */
  369. const submitForm = async (formEl) => {
  370. if (!formEl) return
  371. await formEl.validate((valid, fields) => {
  372. if (valid) {
  373. // console.log(
  374. // "addParams", addParams.value
  375. // );
  376. let submit = {
  377. // 课程名称
  378. "name": addParams.value.name,
  379. // 可以传多个班级的id
  380. "classIds": addParams.value.classIds,
  381. // "classIds": [
  382. // "1385288575779737600",
  383. // "1385288584269008896",
  384. // ],
  385. };
  386. courseAdd(submit)
  387. .then(response => {
  388. // let data = response?.data?.data;
  389. // console.log(
  390. // "courseAddEvent response", response, data
  391. // );
  392. // 重新查找分页数据
  393. pageUpdateEvent();
  394. // 接口请求完成,隐藏模态框
  395. state.value.showDialog = false;
  396. }).catch(error => {
  397. });
  398. } else {
  399. // console.log('error submit!', fields)
  400. }
  401. })
  402. }
  403. /**
  404. * 更新
  405. * @param formEl
  406. */
  407. const updateForm = async (formEl) => {
  408. if (!formEl) return
  409. await formEl.validate((valid, fields) => {
  410. if (valid) {
  411. // console.log(
  412. // "detailsDb", detailsDb.value
  413. // );
  414. let submit = {
  415. "id": detailsDb.value.id,
  416. "name": detailsDb.value.name,
  417. "classIds": detailsDb.value.classIds,
  418. };
  419. courseUpdate(submit)
  420. .then(response => {
  421. // let data = response?.data?.data;
  422. // console.log(
  423. // "courseUpdateEvent response", response, data
  424. // );
  425. // 重新查找分页数据
  426. pageUpdateEvent();
  427. // 接口请求完成,隐藏模态框
  428. state.value.showDetails = false;
  429. }).catch(error => {
  430. });
  431. // let submit = {
  432. // // 通过 schoolItem 接口得到的 id 值,是什么就传什么
  433. // "id" : detailsDb.value.id,
  434. // // 名称
  435. // "name": detailsDb.value.name,
  436. // // 0 表示学校 1 表示学院 2表示专业,3 表示班级, 通过 schoolItem 接口得到的 type 值,是什么就传什么
  437. // "type": detailsDb.value.type,
  438. // // 备注
  439. // "remark": detailsDb.value.remark,
  440. // // 上级id,默认顶级是 0, 通过 schoolItem 接口得到的 parentId 值,是什么就传什么
  441. // "parentId": detailsDb.value.parentId,
  442. // // 新增时传入0即可,修改则传入原始值, 通过 schoolItem 接口得到的 schoolId 值,是什么就传什么
  443. // "schoolId": detailsDb.value.schoolId,
  444. // };
  445. // schoolUpdate(submit)
  446. // .then(response => {
  447. // let data = response?.data?.data;
  448. // // console.log(
  449. // // "schoolUpdateEvent response", response, data
  450. // // );
  451. // // 重新查找分页数据
  452. // pageUpdateEvent();
  453. // // 接口请求完成,隐藏模态框
  454. // state.value.showDetails = false;
  455. // }).catch(error => {
  456. // });
  457. } else {
  458. // console.log('error submit!', fields)
  459. }
  460. })
  461. }
  462. /**
  463. * 批量删除
  464. * delIdArray 批量删除的id值
  465. * 例如
  466. * [ { "id" : "1" }, { "id" : "2" } ]
  467. * callback 全部删除完成,回调
  468. *
  469. */
  470. const idDelAll = (delIdArray, callback) => {
  471. // 删除完成次数
  472. let okNum = 0;
  473. for (let i = 0; i < delIdArray.length; i++) {
  474. let thisDelIdArray = delIdArray[i];
  475. // console.log(
  476. // "thisDelIdArray", thisDelIdArray
  477. // );
  478. let submit = {
  479. "id": thisDelIdArray.id,
  480. };
  481. courseDelete(submit)
  482. .then(response => {
  483. // let data = response?.data?.data;
  484. // console.log(
  485. // "courseDeleteEvent response", response, data
  486. // );
  487. okNum += 1;
  488. if (okNum >= delIdArray.length) {
  489. callback("yes");
  490. }
  491. }).catch(error => {
  492. });
  493. }
  494. }
  495. /**
  496. * 删除选中分页的某个数据
  497. * @param res
  498. */
  499. const btnDel = (res) => {
  500. // console.log(
  501. // "删除选中分页的某个数据", res
  502. // );
  503. let delIdArray = [
  504. { "id" : res.id }
  505. ];
  506. idDelAll(
  507. delIdArray,
  508. function() {
  509. ElMessage({
  510. type: 'success',
  511. message: '删除成功',
  512. });
  513. // 重新查找分页数据
  514. pageUpdateEvent();
  515. }
  516. );
  517. }
  518. /**
  519. * 弹出批量删除模态框逻辑
  520. */
  521. const btnShowDel = () => {
  522. if (state.value.ids && state.value.ids.length > 0) {
  523. state.value.showDel = true
  524. }
  525. }
  526. /**
  527. * 开始批量删除处理
  528. */
  529. const btnBatchDel = () => {
  530. // console.log(
  531. // "开始批量删除处理", state.value.ids
  532. // );
  533. let delIdArray = [];
  534. for (let i = 0; i < state.value.ids.length; i++) {
  535. let thisIds = state.value.ids[i];
  536. let addJson = { "id" : thisIds.id };
  537. delIdArray.push(addJson);
  538. }
  539. idDelAll(
  540. delIdArray,
  541. function() {
  542. ElMessage({
  543. type: 'success',
  544. message: '批量删除成功',
  545. });
  546. // 重新查找分页数据
  547. pageUpdateEvent();
  548. // 批量选中的列表重置
  549. state.value.ids = [];
  550. // 隐藏模态框
  551. state.value.showDel = false;
  552. }
  553. );
  554. }
  555. /**
  556. * 弹出指定分页详情
  557. * @param res
  558. */
  559. const btnDetail = (res) => {
  560. console.log('弹出指定分页详情', res);
  561. let submit = {
  562. id : res.id,
  563. };
  564. courseDetail(submit)
  565. .then(response => {
  566. let data = response?.data?.data;
  567. // console.log(
  568. // "courseDetailEvent response", response, data
  569. // );
  570. detailsDb.value = {
  571. id: data.id,
  572. name: data.name,
  573. classIds: data.classIds,
  574. }
  575. state.value.showDetails = true;
  576. }).catch(error => {
  577. });
  578. }
  579. /**
  580. * 获取父子级的树形结构数据
  581. */
  582. const optionsUpdate = () => {
  583. // 先获取所有最顶级的数据
  584. let submit = {
  585. // 默认优先去顶级所有,传 0 。 否则你要看到 学院, 专业 ,班级 的列表,这里固定就传 学院这个的id值
  586. "schoolId" : 0,
  587. };
  588. schoolTree(submit)
  589. .then(response => {
  590. options = [];
  591. let data = response?.data?.data;
  592. options = data;
  593. // console.log(
  594. // "schoolTreeEvent response", response, data
  595. // );
  596. for (let i = 0; i < options.length; i++) {
  597. let thisOptions = options[i];
  598. // console.log(
  599. // "thisOptions", thisOptions
  600. // );
  601. optionsChildrenUpdate(i, thisOptions.id);
  602. }
  603. }).catch(error => {
  604. });
  605. }
  606. /**
  607. * 将对应子级的数据获取
  608. * optionsIndex options 对应的数组下标
  609. * id 当前父级 的id值
  610. */
  611. const optionsChildrenUpdate = (optionsIndex, id) => {
  612. let submit = {
  613. // 默认优先去顶级所有,传 0 。 否则你要看到 学院, 专业 ,班级 的列表,这里固定就传 学院这个的id值
  614. "schoolId" : id,
  615. };
  616. schoolTree(submit)
  617. .then(response => {
  618. let data = response?.data?.data;
  619. // console.log(
  620. // "optionsChildrenUpdate response", data
  621. // );
  622. options[optionsIndex] = data[0];
  623. }).catch(error => {
  624. });
  625. }
  626. onMounted(function() {
  627. pageUpdateEvent();
  628. optionsUpdate();
  629. });
  630. </script>
  631. <template>
  632. <div class="ManageCourse">
  633. <div class="content commonsScrollbar">
  634. <div class="contentTitle">
  635. <div class="contentTitle-line"></div>
  636. <div class="contentTitle-text">管理课程</div>
  637. </div>
  638. <div class="contentRow">
  639. <div class="rowLeft">
  640. <div class="rowBtn rowBtn1" @click="btnAddStudent(ruleFormRef)">
  641. <img src="./assets/img/manageUser/add.svg" alt="" />
  642. <span class="rowBtn-text rowBtn-text1">添加课程</span>
  643. </div>
  644. <!-- <div class="rowBtn rowBtn2">
  645. <img src="./assets/img/manageUser/download.svg" alt="" />
  646. <span class="rowBtn-text rowBtn-text2">模板下载</span>
  647. </div>
  648. <div class="rowBtn rowBtn1">
  649. <img src="./assets/img/manageUser/import.svg" alt="" />
  650. <span class="rowBtn-text rowBtn-text1">批量导入</span>
  651. </div>
  652. <div class="rowBtn rowBtn1">
  653. <img src="./assets/img/manageUser/export.svg" alt="" />
  654. <span class="rowBtn-text rowBtn-text1">批量导出</span>
  655. </div> -->
  656. <div class="rowBtn rowBtn3" @click="btnShowDel">
  657. <img src="./assets/img/manageUser/delete.svg" alt="" />
  658. <span class="rowBtn-text rowBtn-text3">批量删除</span>
  659. </div>
  660. </div>
  661. <div class="rowRight">
  662. <el-input
  663. v-model="state.listParams.keyword"
  664. :prefix-icon="Search"
  665. clearable
  666. style="width: 17rem; height: 3rem"
  667. placeholder="搜索课程"
  668. @clear="btnSearchName"
  669. />
  670. <div class="rowSearch" @click="btnSearchName">搜索</div>
  671. </div>
  672. </div>
  673. <div class="contentTable commonsScrollbar">
  674. <el-table :data="state.list" style="width: 100%" @selection-change="selectionChange">
  675. <el-table-column align="center" type="selection" width="110" />
  676. <el-table-column prop="name" label="课程" />
  677. <el-table-column label="操作">
  678. <template #default="scope">
  679. <el-button link type="primary" @click="btnDetail(scope.row)">编辑</el-button>
  680. <el-popconfirm :title="`你确定删除${scope.row.name}吗?`" @confirm="btnDel(scope.row)">
  681. <template #reference>
  682. <el-button link type="danger" style="margin-left: 3rem">移除</el-button>
  683. </template>
  684. </el-popconfirm>
  685. </template>
  686. </el-table-column>
  687. </el-table>
  688. </div>
  689. <el-pagination
  690. class="classPagination"
  691. v-model:current-page="state.listParams.page"
  692. v-model:page-size="state.listParams.limit"
  693. :page-sizes="[10, 20, 50, 100]"
  694. background
  695. :layout="' ->, total,prev, pager, next, jumper'"
  696. :total="state.total"
  697. @size-change="handleSizeChange"
  698. @current-change="handleCurrentChange"
  699. >
  700. </el-pagination>
  701. </div>
  702. <!-- 添加模态框 -->
  703. <el-dialog v-model="state.showDialog" class="dialog">
  704. <template #header>
  705. <div class="dialog-title">添加课程</div>
  706. </template>
  707. <div class="dialogBody">
  708. <el-form ref="ruleFormRef" :model="addParams" :rules="addRules">
  709. <el-form-item label="课程" prop="name">
  710. <el-input v-model="addParams.name" clearable placeholder="请填写课程"></el-input>
  711. </el-form-item>
  712. <el-form-item label="班级" prop="classIds">
  713. <el-cascader v-model="addParams.classIds"
  714. :options="options"
  715. :props="{
  716. children: 'children',
  717. label: 'name',
  718. value: 'id',
  719. checkStrictly: false,
  720. expandTrigger: 'hover',
  721. emitPath: false,
  722. multiple: true,
  723. }"
  724. clearable placeholder="请选择班级" />
  725. </el-form-item>
  726. </el-form>
  727. <div class="dialogFoot">
  728. <el-button color="#EAEAEA" @click="state.showDialog = false" style="margin-right: 0.6rem">取消</el-button>
  729. <el-button color="#2C68FF" @click="submitForm(ruleFormRef)">确认</el-button>
  730. </div>
  731. </div>
  732. </el-dialog>
  733. <!-- 详情模态框 -->
  734. <el-dialog v-model="state.showDetails" class="dialog">
  735. <template #header>
  736. <div class="dialog-title">课程详情</div>
  737. </template>
  738. <div class="dialogBody">
  739. <el-form ref="ruleFormRef" :model="detailsDb" :rules="addRules">
  740. <el-form-item label="课程" prop="name">
  741. <el-input v-model="detailsDb.name" clearable placeholder="请填写课程"></el-input>
  742. </el-form-item>
  743. <el-form-item label="班级" prop="classIds">
  744. <el-cascader v-model="detailsDb.classIds"
  745. :options="options"
  746. :props="{
  747. children: 'children',
  748. label: 'name',
  749. value: 'id',
  750. checkStrictly: false,
  751. expandTrigger: 'hover',
  752. emitPath: false,
  753. multiple: true,
  754. }"
  755. clearable placeholder="请选择班级" />
  756. </el-form-item>
  757. </el-form>
  758. <div class="dialogFoot">
  759. <el-button color="#EAEAEA" @click="state.showDetails = false" style="margin-right: 0.6rem">取消</el-button>
  760. <el-button color="#2C68FF" @click="updateForm(ruleFormRef)">更新</el-button>
  761. </div>
  762. </div>
  763. </el-dialog>
  764. <!-- 用于批量删除模态框 -->
  765. <el-dialog v-model="state.showDel" width="600" class="del">
  766. <template #header>
  767. <div style="display: flex; align-items: center">
  768. <img class="del-img" src="./assets/img/manageUser/error-line.svg" alt="" />
  769. <div class="del-name">批量删除</div>
  770. </div>
  771. </template>
  772. <span class="delBody">是否确认批量删除?删除后不可恢复!</span>
  773. <template #footer>
  774. <div class="dialog-footer">
  775. <el-button @click="state.showDel = false">取消</el-button>
  776. <el-button type="primary" @click="btnBatchDel">删除</el-button>
  777. </div>
  778. </template>
  779. </el-dialog>
  780. </div>
  781. </template>
  782. <style lang="scss" scoped>
  783. @use './css/commonsScrollbar.scss';
  784. .ManageCourse * {
  785. -moz-user-select: none;
  786. -webkit-user-select: none;
  787. -ms-user-select: none;
  788. -khtml-user-select: none;
  789. user-select: none;
  790. box-sizing:border-box;
  791. -moz-box-sizing:border-box; /* Firefox */
  792. -webkit-box-sizing:border-box; /* Safari */
  793. }
  794. .ManageCourse {
  795. position: relative;
  796. z-index: 0;
  797. width: 100%;
  798. height: 100%;
  799. top: 0px;
  800. left: 0px;
  801. background-color: #ffffff;
  802. .newPage {
  803. position: absolute;
  804. top: 0px;
  805. left: 0px;
  806. width: 100%;
  807. height: 100%;
  808. z-index: 1;
  809. }
  810. .content {
  811. position: relative;
  812. width: 100%;
  813. height: 100%;
  814. border-radius: 1rem;
  815. background: #ffffff;
  816. padding: 2rem 4rem 2rem 3rem;
  817. box-sizing: border-box;
  818. .contentTitle {
  819. padding: 0 0.5rem;
  820. box-sizing: border-box;
  821. display: flex;
  822. align-items: center;
  823. .contentTitle-line {
  824. width: 0.35rem;
  825. height: 2rem;
  826. background: #2c68ff;
  827. border-radius: 0.85rem;
  828. }
  829. .contentTitle-text {
  830. font-weight: bold;
  831. font-size: 1.8rem;
  832. color: #373737;
  833. margin-left: 1.4rem;
  834. }
  835. }
  836. .contentRow {
  837. margin-top: 2.8rem;
  838. padding: 0 1.5rem 0 1rem;
  839. box-sizing: border-box;
  840. display: flex;
  841. align-items: center;
  842. justify-content: space-between;
  843. .rowLeft {
  844. display: flex;
  845. align-items: center;
  846. .rowSelect {
  847. :deep(.el-select__wrapper) {
  848. height: 3rem;
  849. font-size: 1.1rem;
  850. min-height: 0;
  851. border-radius: 0.57rem;
  852. }
  853. }
  854. .rowBtn {
  855. cursor: pointer;
  856. width: 8.8rem;
  857. height: 3rem;
  858. border-radius: 0.57rem;
  859. display: flex;
  860. align-items: center;
  861. justify-content: center;
  862. margin-left: 2rem;
  863. img {
  864. width: 1.5rem;
  865. height: 1.5rem;
  866. }
  867. .rowBtn-text {
  868. font-size: 1.15rem;
  869. margin-left: 0.7rem;
  870. }
  871. .rowBtn-text1 {
  872. color: #3d7cff;
  873. }
  874. .rowBtn-text2 {
  875. color: #d8a216;
  876. }
  877. .rowBtn-text3 {
  878. color: #e84d4d;
  879. }
  880. }
  881. .rowBtn1 {
  882. background: #e5eeff;
  883. &:hover {
  884. background: #edf3ff;
  885. }
  886. }
  887. .rowBtn2 {
  888. background: rgba(214, 154, 1, 0.1);
  889. &:hover {
  890. background: #f4e5bd;
  891. }
  892. }
  893. .rowBtn3 {
  894. background: rgba(232, 77, 77, 0.05);
  895. &:hover {
  896. background: #fad4d4;
  897. }
  898. }
  899. }
  900. .rowRight {
  901. display: flex;
  902. :deep(.el-input__wrapper) {
  903. font-size: 1rem;
  904. border-radius: 0.57rem;
  905. }
  906. .rowSearch {
  907. cursor: pointer;
  908. width: 3.9rem;
  909. height: 3rem;
  910. background: #3d7cff;
  911. border-radius: 0.57rem;
  912. font-size: 1.1rem;
  913. color: #ffffff;
  914. display: flex;
  915. align-items: center;
  916. justify-content: center;
  917. margin-left: 0.7rem;
  918. &:hover {
  919. background: #77a3ff;
  920. }
  921. }
  922. }
  923. }
  924. .contentTable {
  925. margin-top: 2.3rem;
  926. height: 36rem;
  927. :deep(tr) {
  928. border-radius: 0.57rem;
  929. }
  930. :deep(th) {
  931. background: #f5f7fa;
  932. height: 5rem;
  933. font-size: 1.4rem;
  934. color: #333746;
  935. font-weight: 500;
  936. }
  937. :deep(td) {
  938. height: 5rem;
  939. font-size: 1.2rem;
  940. color: #333333;
  941. }
  942. :deep(.cell) {
  943. height: 2rem;
  944. }
  945. :deep(.el-checkbox) {
  946. --el-checkbox-input-height: 2rem;
  947. --el-checkbox-input-width: 2rem;
  948. --el-checkbox-border-radius: 0.28rem;
  949. // border: 1px solid #cad2d8;
  950. }
  951. :deep(.el-checkbox__inner:after) {
  952. height: 1.07rem;
  953. left: 0.64rem;
  954. top: 0.21rem;
  955. width: 0.5rem;
  956. }
  957. :deep(.el-checkbox__inner::before) {
  958. top: 0.85rem;
  959. }
  960. :deep(.el-button) {
  961. font-size: 1.2rem;
  962. }
  963. :deep(.el-table__inner-wrapper::before) {
  964. height: 0px;
  965. }
  966. }
  967. :deep(.classPagination) {
  968. .el-pagination__rightwrapper {
  969. justify-content: center;
  970. .el-pagination__total {
  971. color: #000000;
  972. }
  973. .btn-prev,
  974. .btn-next {
  975. background: #f7f7f7;
  976. color: #000000;
  977. border-radius: 1rem;
  978. }
  979. .number {
  980. background: #f7f7f7;
  981. color: #000000;
  982. border-radius: 0.5rem;
  983. }
  984. .is-active {
  985. background: #2c68ff;
  986. color: #ffffff;
  987. }
  988. .el-pagination__jump {
  989. color: #000000;
  990. .el-pagination__editor.el-input {
  991. width: 4.57rem;
  992. .el-input__wrapper {
  993. border-radius: 1rem;
  994. }
  995. }
  996. }
  997. }
  998. }
  999. }
  1000. :deep(.dialog) {
  1001. width: 55rem;
  1002. padding: 0;
  1003. border-radius: 1.1rem;
  1004. .el-dialog__header {
  1005. padding: 0px;
  1006. margin: 0px;
  1007. }
  1008. .dialog-title {
  1009. // width: 55rem;
  1010. width: 100%;
  1011. height: 5.9rem;
  1012. background: #2c68ff;
  1013. font-size: 1.8rem;
  1014. color: #ffffff;
  1015. display: flex;
  1016. align-items: center;
  1017. border-radius: 1.1rem 1.1rem 0 0;
  1018. padding-left: 4rem;
  1019. box-sizing: border-box;
  1020. }
  1021. .el-dialog__headerbtn {
  1022. font-size: 1.7rem;
  1023. width: 3.57rem;
  1024. height: 3.57rem;
  1025. top: 1rem;
  1026. right: 1rem;
  1027. .el-dialog__close {
  1028. color: #ffffff;
  1029. font-size: 3.7rem;
  1030. }
  1031. }
  1032. .dialogBody {
  1033. padding: 0.8rem 8rem 3.5rem 6.7rem;
  1034. box-sizing: border-box;
  1035. .el-form-item {
  1036. align-items: center;
  1037. margin-bottom: 2rem;
  1038. }
  1039. .el-form-item__label {
  1040. font-size: 1.2rem;
  1041. color: #000000;
  1042. padding-right: 2rem;
  1043. }
  1044. .el-input {
  1045. // height: 3.7rem;
  1046. // 这里不要限制高度,否则,多级联动多选的时候,样式会有问题,可以限制,最小高度
  1047. min-height: 3.7rem;
  1048. --el-input-bg-color: #efefef;
  1049. font-size: 1.2rem;
  1050. --el-input-border-radius: 0.57rem;
  1051. --el-input-border-color: #efefef;
  1052. --el-input-focus-border-color: #efefef;
  1053. --el-input-hover-border-color: #efefef;
  1054. .el-input__wrapper {
  1055. padding: 0 2rem;
  1056. }
  1057. }
  1058. .el-select {
  1059. .el-select__wrapper {
  1060. height: 3.7rem;
  1061. background-color: #efefef;
  1062. font-size: 1.2rem;
  1063. border-radius: 0.57rem;
  1064. // box-shadow: 0 0 0 1px #efefef inset;
  1065. padding: 0 2rem;
  1066. .el-select__caret {
  1067. color: #555555;
  1068. }
  1069. }
  1070. }
  1071. .dialogFoot {
  1072. display: flex;
  1073. justify-content: end;
  1074. margin-top: 4rem;
  1075. .el-button {
  1076. width: 8.5rem;
  1077. height: 3.2rem;
  1078. border-radius: 0.57rem;
  1079. font-size: 1.2rem;
  1080. }
  1081. }
  1082. .el-cascader {
  1083. width: 100%;
  1084. }
  1085. }
  1086. }
  1087. :deep(.del) {
  1088. .del-img {
  1089. width: 1.5rem;
  1090. height: 1.5rem;
  1091. margin-right: 0.3rem;
  1092. }
  1093. .del-name {
  1094. font-size: 1.2rem;
  1095. color: #e84d4d;
  1096. }
  1097. .el-dialog__body {
  1098. width: 100%;
  1099. height: 10rem;
  1100. display: flex;
  1101. justify-content: center;
  1102. padding-top: 3rem;
  1103. color: #000000;
  1104. box-sizing: border-box;
  1105. font-size: 1.2rem;
  1106. }
  1107. }
  1108. }
  1109. </style>