ManageUser.vue 37 KB

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