ManageUser.vue 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473
  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. "password": null,
  277. // 确认密码
  278. "password2": null
  279. });
  280. /**
  281. * 添加,字段相关验证规则
  282. */
  283. let addRules = ref({
  284. userName: [{ required: true, message: '请填写账号', trigger: 'blur' }],
  285. nickName: [{ required: true, message: '请填写姓名', trigger: 'blur' }],
  286. userType: [{ required: true, message: '请选择账号类型', trigger: 'change', }],
  287. classInfo: [{ required: true, message: '请选择班级', trigger: 'change', }],
  288. password: [{ required: true, message: '请填写密码', trigger: 'blur' }],
  289. password2: [{ required: true, message: '请填写确认密码', trigger: 'blur' }],
  290. });
  291. /**
  292. * 详情数据
  293. */
  294. let detailsDb = ref({
  295. "id": null,
  296. // 账号
  297. "userName": null,
  298. // 0 表示教师,1表示学生
  299. "userType": 0,
  300. // 昵称
  301. "nickName": null,
  302. // 用户分配到的班级数组,可以是多个
  303. "classInfo": [],
  304. });
  305. /**
  306. * 因为这里的接口返回的不是分页的接口,是全部数据,所以这里就记录全部数据
  307. * 后面通过逻辑,来控制分页的显示
  308. */
  309. let pageDb = [];
  310. /**
  311. * 查找分页数据
  312. */
  313. const pageUpdateEvent = () => {
  314. console.log(
  315. "查找分页数据 pageUpdateEvent", state.value.listParams
  316. );
  317. let keyword = state.value.listParams.keyword;
  318. let pageNum = state.value.listParams.page;
  319. let pageSize = state.value.listParams.limit;
  320. let userType = state.value.listParams.userType;
  321. let submit = {
  322. // 第几页
  323. "pageNum": pageNum,
  324. // 每页显示多少数据
  325. "pageSize": pageSize,
  326. // 0 - 老师, 1 - 学生
  327. "userType" : userType,
  328. // 昵称
  329. "nickName" : "",
  330. // 账号
  331. "userName": keyword,
  332. };
  333. userList(submit)
  334. .then(response => {
  335. let dataOld = response?.data?.data;
  336. let data = dataOld?.list;
  337. state.value.total = dataOld.total;
  338. state.value.list = data;
  339. // console.log(
  340. // "userListEvent response", response, data
  341. // );
  342. // pageDb = [];
  343. // pageDb = data;
  344. // pageDbUpdateEvent();
  345. }).catch(error => {
  346. });
  347. }
  348. // /**
  349. // * 因为接口不是标准的分页接口,是直接返回所有数据的
  350. // * 这里进行单独处理,来展示第几页的数据
  351. // */
  352. // const pageDbUpdateEvent = () => {
  353. // // console.log(
  354. // // "查找分页数据 pageDbUpdateEvent", state.value.listParams, pageDb
  355. // // );
  356. // state.value.total = pageDb.length;
  357. // state.value.list = [];
  358. // let page = state.value.listParams.page;
  359. // let limit = state.value.listParams.limit;
  360. // let keyword = state.value.listParams.keyword;
  361. // // 从第几个数组下标开始
  362. // let indexStart = (page - 1) * limit;
  363. // // 到第几个数组下标结束
  364. // let indexEnd = indexStart + limit - 1;
  365. // for (let i = 0; i < pageDb.length; i++) {
  366. // // 在范围内
  367. // if (i >= indexStart && i <= indexEnd) {
  368. // let thisPageDb = pageDb[i];
  369. // state.value.list.push(thisPageDb);
  370. // }
  371. // }
  372. // }
  373. /**
  374. * 弹出添加UI
  375. */
  376. const btnAddStudent = (formEl) => {
  377. state.value.showDialog = true;
  378. if (!formEl) {
  379. return;
  380. }
  381. formEl.resetFields();
  382. }
  383. /**
  384. * 触发搜索逻辑
  385. */
  386. const btnSearchName = () => {
  387. state.value.listParams.page = 1;
  388. pageUpdateEvent();
  389. }
  390. /**
  391. * 更新选中的班级逻辑
  392. * @param val
  393. */
  394. const classChange = (val) => {
  395. state.value.listParams.page = 1;
  396. pageUpdateEvent();
  397. }
  398. /**
  399. * 上一页,下一页改变
  400. */
  401. const handleSizeChange = (val) => {
  402. // 重新查找分页数据
  403. pageUpdateEvent();
  404. }
  405. /**
  406. * 上一页,下一页改变
  407. */
  408. const handleCurrentChange = (val) => {
  409. // 重新查找分页数据
  410. pageUpdateEvent();
  411. }
  412. /**
  413. * 分页多选,更新多选的相关数据
  414. * @param res
  415. */
  416. const selectionChange = (res) => {
  417. state.value.ids = [];
  418. res.forEach((item) => {
  419. // state.value.ids.push(item.id);
  420. state.value.ids.push(item);
  421. });
  422. console.log("分页多选,更新多选的相关数据", state.value.ids);
  423. }
  424. /**
  425. * 添加 点击提交
  426. * @param formEl
  427. */
  428. const submitForm = async (formEl) => {
  429. if (!formEl) return
  430. await formEl.validate((valid, fields) => {
  431. if (valid) {
  432. // console.log(
  433. // "addParams", addParams.value
  434. // );
  435. let classInfoArray = [];
  436. if (addParams.value.classInfo != null && addParams.value.classInfo != undefined) {
  437. for (let i = 0; i < addParams.value.classInfo.length; i++) {
  438. let thisClassInfo = addParams.value.classInfo[i];
  439. let schoolIdJson = getSchoolIdJson(thisClassInfo);
  440. if (schoolIdJson != null && schoolIdJson != undefined) {
  441. // console.log( "schoolIdJson", schoolIdJson );
  442. // 特殊处理,必须选中的是班级
  443. if (schoolIdJson.type == 3) {
  444. let addJson = {
  445. "id": schoolIdJson.id,
  446. "name": schoolIdJson.name,
  447. "remark": schoolIdJson.remark,
  448. "schoolId": schoolIdJson.schoolId,
  449. };
  450. classInfoArray.push(addJson);
  451. }
  452. }
  453. }
  454. }
  455. let submit = {
  456. // 账号
  457. "userName": addParams.value.userName,
  458. // 0 表示教师,1表示学生
  459. "userType": addParams.value.userType,
  460. // 昵称
  461. "nickName": addParams.value.nickName,
  462. // 用户分配到的班级数组,可以是多个
  463. // "classInfo": [
  464. // // {
  465. // // "id": 0,
  466. // // "name": "",
  467. // // "remark": "",
  468. // // "schoolId": 0
  469. // // }
  470. // ],
  471. "classInfo": classInfoArray,
  472. // 密码
  473. "password": addParams.value.password,
  474. // 确认密码
  475. "password2": addParams.value.password2
  476. };
  477. // console.log(
  478. // "添加账号的相关数据 submit", submit
  479. // );
  480. userAdd(submit)
  481. .then(response => {
  482. let data = response?.data?.data;
  483. // console.log(
  484. // "userAddEvent response", response, data
  485. // );
  486. // 重新查找分页数据
  487. pageUpdateEvent();
  488. // 接口请求完成,隐藏模态框
  489. state.value.showDialog = false;
  490. }).catch(error => {
  491. });
  492. } else {
  493. // console.log('error submit!', fields)
  494. }
  495. })
  496. }
  497. /**
  498. * 更新
  499. * @param formEl
  500. */
  501. const updateForm = async (formEl) => {
  502. if (!formEl) return
  503. await formEl.validate((valid, fields) => {
  504. if (valid) {
  505. // console.log(
  506. // "detailsDb", detailsDb.value
  507. // );
  508. let classInfoArray = [];
  509. if (detailsDb.value.classInfo != null && detailsDb.value.classInfo != undefined) {
  510. for (let i = 0; i < detailsDb.value.classInfo.length; i++) {
  511. let thisClassInfo = detailsDb.value.classInfo[i];
  512. let schoolIdJson = getSchoolIdJson(thisClassInfo);
  513. if (schoolIdJson != null && schoolIdJson != undefined) {
  514. // console.log( "schoolIdJson", schoolIdJson );
  515. // 特殊处理,必须选中的是班级
  516. if (schoolIdJson.type == 3) {
  517. let addJson = {
  518. "id": schoolIdJson.id,
  519. "name": schoolIdJson.name,
  520. "remark": schoolIdJson.remark,
  521. "schoolId": schoolIdJson.schoolId,
  522. };
  523. classInfoArray.push(addJson);
  524. }
  525. }
  526. }
  527. }
  528. let submit = {
  529. "id": detailsDb.value.id,
  530. // 账号
  531. "userName": detailsDb.value.userName,
  532. // 0 表示教师,1表示学生
  533. "userType": detailsDb.value.userType,
  534. // 昵称
  535. "nickName": detailsDb.value.nickName,
  536. // 用户分配到的班级数组,可以是多个【通过测试,班级不可以减少,但是新增多个,则会新增】
  537. "classInfo": classInfoArray,
  538. };
  539. // console.log(
  540. // "submit", submit
  541. // );
  542. userUpdate(submit)
  543. .then(response => {
  544. // let data = response?.data?.data;
  545. // console.log(
  546. // "userUpdateEvent response", response, data
  547. // );
  548. // 重新查找分页数据
  549. pageUpdateEvent();
  550. // 接口请求完成,隐藏模态框
  551. state.value.showDetails = false;
  552. }).catch(error => {
  553. });
  554. } else {
  555. // console.log('error submit!', fields)
  556. }
  557. })
  558. }
  559. /**
  560. * 批量删除
  561. * delIdArray 批量删除的id值
  562. * 例如
  563. * [ { "id" : "1" }, { "id" : "2" } ]
  564. * callback 全部删除完成,回调
  565. *
  566. */
  567. const idDelAll = (delIdArray, callback) => {
  568. // 删除完成次数
  569. let okNum = 0;
  570. for (let i = 0; i < delIdArray.length; i++) {
  571. let thisDelIdArray = delIdArray[i];
  572. // console.log(
  573. // "thisDelIdArray", thisDelIdArray
  574. // );
  575. let submit = {
  576. "id": thisDelIdArray.id,
  577. };
  578. userDelete(submit)
  579. .then(response => {
  580. // let data = response?.data?.data;
  581. // console.log(
  582. // "userDeleteEvent response", response, data
  583. // );
  584. okNum += 1;
  585. if (okNum >= delIdArray.length) {
  586. callback("yes");
  587. }
  588. }).catch(error => {
  589. });
  590. }
  591. }
  592. /**
  593. * 删除选中分页的某个数据
  594. * @param res
  595. */
  596. const btnDel = (res) => {
  597. // console.log(
  598. // "删除选中分页的某个数据", res
  599. // );
  600. let delIdArray = [
  601. { "id" : res.id }
  602. ];
  603. idDelAll(
  604. delIdArray,
  605. function() {
  606. ElMessage({
  607. type: 'success',
  608. message: '删除成功',
  609. });
  610. // 重新查找分页数据
  611. pageUpdateEvent();
  612. }
  613. );
  614. }
  615. /**
  616. * 弹出批量删除模态框逻辑
  617. */
  618. const btnShowDel = () => {
  619. if (state.value.ids && state.value.ids.length > 0) {
  620. state.value.showDel = true
  621. }
  622. }
  623. /**
  624. * 开始批量删除处理
  625. */
  626. const btnBatchDel = () => {
  627. // console.log(
  628. // "开始批量删除处理", state.value.ids
  629. // );
  630. let delIdArray = [];
  631. for (let i = 0; i < state.value.ids.length; i++) {
  632. let thisIds = state.value.ids[i];
  633. let addJson = { "id" : thisIds.id };
  634. delIdArray.push(addJson);
  635. }
  636. // console.log(
  637. // "btnBatchDel delIdArray ===>", delIdArray
  638. // );
  639. idDelAll(
  640. delIdArray,
  641. function() {
  642. ElMessage({
  643. type: 'success',
  644. message: '删除成功',
  645. });
  646. // 重新查找分页数据
  647. pageUpdateEvent();
  648. // 批量选中的列表重置
  649. state.value.ids = [];
  650. // 隐藏模态框
  651. state.value.showDel = false;
  652. }
  653. );
  654. }
  655. /**
  656. * 弹出指定分页详情
  657. * @param res
  658. */
  659. const btnDetail = (res) => {
  660. console.log('弹出指定分页详情', res);
  661. let submit = {
  662. id: res.id,
  663. };
  664. userDetail(submit)
  665. .then(response => {
  666. let data = response?.data?.data;
  667. // console.log(
  668. // "userDetailEvent response", response, data
  669. // );
  670. let classInfoIdArray = [];
  671. if (data.classInfo != null && data.classInfo != undefined) {
  672. for (let i = 0; i < data.classInfo.length; i++) {
  673. let thisClassInfo = data.classInfo[i];
  674. // console.log("thisClassInfo", thisClassInfo);
  675. classInfoIdArray.push(thisClassInfo.id);
  676. }
  677. }
  678. detailsDb.value = {
  679. "id": data.id,
  680. // 账号
  681. "userName": data.userName,
  682. // 0 表示教师,1表示学生
  683. "userType": data.userType,
  684. // 昵称
  685. "nickName": data.nickName,
  686. // 用户分配到的班级数组,可以是多个
  687. "classInfo": classInfoIdArray,
  688. // // 密码
  689. // "password": data.password,
  690. // // 确认密码
  691. // "password2": data.password2
  692. }
  693. // console.log("detailsDb.value", detailsDb.value);
  694. state.value.showDetails = true;
  695. }).catch(error => {
  696. });
  697. }
  698. /**
  699. * 记录,学校到班级对应的所有id和json数据
  700. *
  701. */
  702. let schoolIdJson = {};
  703. /**
  704. * 本次遍历的数组
  705. * array
  706. */
  707. const schoolIdJsonUpdateEvent = (array) => {
  708. if (array == null || array == undefined) {
  709. return array;
  710. }
  711. for (let i = 0; i < array.length; i++) {
  712. let objArray = array[i];
  713. let id = objArray.id;
  714. schoolIdJson[id] = objArray;
  715. if (objArray.children != null && objArray.children != undefined) {
  716. let children = objArray.children;
  717. schoolIdJsonUpdateEvent(children);
  718. }
  719. }
  720. }
  721. /**
  722. * 根据id得到对应学校到班级的数据
  723. * id
  724. * 存在返回json数据,否则返回 null
  725. */
  726. const getSchoolIdJson = (id) => {
  727. if (schoolIdJson[id] == null || schoolIdJson[id] == undefined) {
  728. return null;
  729. }
  730. let objJson = schoolIdJson[id];
  731. return objJson;
  732. }
  733. /**
  734. * 获取父子级的树形结构数据
  735. */
  736. const optionsUpdate = () => {
  737. // 先获取所有最顶级的数据
  738. let submit = {
  739. // 默认优先去顶级所有,传 0 。 否则你要看到 学院, 专业 ,班级 的列表,这里固定就传 学院这个的id值
  740. "schoolId" : 0,
  741. };
  742. schoolTree(submit)
  743. .then(response => {
  744. options = [];
  745. let data = response?.data?.data;
  746. options = data;
  747. // console.log(
  748. // "schoolTreeEvent response", response, data
  749. // );
  750. for (let i = 0; i < options.length; i++) {
  751. let thisOptions = options[i];
  752. // console.log(
  753. // "thisOptions", thisOptions
  754. // );
  755. optionsChildrenUpdate(i, thisOptions.id);
  756. }
  757. }).catch(error => {
  758. });
  759. }
  760. /**
  761. * 将对应子级的数据获取
  762. * optionsIndex options 对应的数组下标
  763. * id 当前父级 的id值
  764. */
  765. const optionsChildrenUpdate = (optionsIndex, id) => {
  766. let submit = {
  767. // 默认优先去顶级所有,传 0 。 否则你要看到 学院, 专业 ,班级 的列表,这里固定就传 学院这个的id值
  768. "schoolId" : id,
  769. };
  770. schoolTree(submit)
  771. .then(response => {
  772. let data = response?.data?.data;
  773. // console.log(
  774. // "optionsChildrenUpdate response", data
  775. // );
  776. options[optionsIndex] = data[0];
  777. schoolIdJsonUpdateEvent(options);
  778. // console.log("options ===>", options, schoolIdJson);
  779. }).catch(error => {
  780. });
  781. }
  782. onMounted(function() {
  783. pageUpdateEvent();
  784. optionsUpdate();
  785. });
  786. </script>
  787. <template>
  788. <div class="ManageUser">
  789. <div class="content commonsScrollbar">
  790. <div class="contentTitle">
  791. <div class="contentTitle-line"></div>
  792. <div class="contentTitle-text">账号管理</div>
  793. </div>
  794. <div class="contentRow">
  795. <div class="rowLeft">
  796. <div class="rowSelect">
  797. <el-select
  798. v-model="state.listParams.userType"
  799. clearable
  800. placeholder="账号类型"
  801. size="large"
  802. style="width: 15rem"
  803. @change="classChange"
  804. >
  805. <el-option v-for="item in state.optionUserType" :key="item.value" :label="item.name" :value="item.value" />
  806. </el-select>
  807. </div>
  808. <div class="rowBtn rowBtn1" @click="btnAddStudent(ruleFormRef)">
  809. <img src="./assets/img/manageUser/add.svg" alt="" />
  810. <span class="rowBtn-text rowBtn-text1">添加账号</span>
  811. </div>
  812. <!-- <div class="rowBtn rowBtn2">
  813. <img src="./assets/img/manageUser/download.svg" alt="" />
  814. <span class="rowBtn-text rowBtn-text2">模板下载</span>
  815. </div>
  816. <div class="rowBtn rowBtn1">
  817. <img src="./assets/img/manageUser/import.svg" alt="" />
  818. <span class="rowBtn-text rowBtn-text1">批量导入</span>
  819. </div>
  820. <div class="rowBtn rowBtn1">
  821. <img src="./assets/img/manageUser/export.svg" alt="" />
  822. <span class="rowBtn-text rowBtn-text1">批量导出</span>
  823. </div> -->
  824. <div class="rowBtn rowBtn3" @click="btnShowDel">
  825. <img src="./assets/img/manageUser/delete.svg" alt="" />
  826. <span class="rowBtn-text rowBtn-text3">批量删除</span>
  827. </div>
  828. </div>
  829. <div class="rowRight">
  830. <el-input
  831. v-model="state.listParams.keyword"
  832. :prefix-icon="Search"
  833. clearable
  834. style="width: 17rem; height: 3rem"
  835. placeholder="搜索账号"
  836. @clear="btnSearchName"
  837. />
  838. <div class="rowSearch" @click="btnSearchName">搜索</div>
  839. </div>
  840. </div>
  841. <div class="contentTable commonsScrollbar">
  842. <el-table :data="state.list" style="width: 100%" @selection-change="selectionChange">
  843. <el-table-column align="center" type="selection" width="110" />
  844. <el-table-column prop="userName" label="账号" />
  845. <el-table-column prop="nickName" label="姓名" />
  846. <el-table-column label="操作">
  847. <template #default="scope">
  848. <el-button link type="primary" @click="btnDetail(scope.row)">编辑</el-button>
  849. <el-popconfirm :title="`你确定删除${scope.row.userName}吗?`" @confirm="btnDel(scope.row)">
  850. <template #reference>
  851. <el-button link type="danger" style="margin-left: 3rem">移除</el-button>
  852. </template>
  853. </el-popconfirm>
  854. </template>
  855. </el-table-column>
  856. </el-table>
  857. </div>
  858. <el-pagination
  859. class="classPagination"
  860. v-model:current-page="state.listParams.page"
  861. v-model:page-size="state.listParams.limit"
  862. :page-sizes="[10, 20, 50, 100]"
  863. background
  864. :layout="' ->, total,prev, pager, next, jumper'"
  865. :total="state.total"
  866. @size-change="handleSizeChange"
  867. @current-change="handleCurrentChange"
  868. >
  869. </el-pagination>
  870. </div>
  871. <!-- 添加模态框 -->
  872. <el-dialog v-model="state.showDialog" class="dialog">
  873. <template #header>
  874. <div class="dialog-title">添加账号</div>
  875. </template>
  876. <div class="dialogBody">
  877. <el-form ref="ruleFormRef" :model="addParams" :rules="addRules">
  878. <el-form-item label="账号" prop="userName">
  879. <el-input v-model="addParams.userName" clearable placeholder="请填写账号"></el-input>
  880. </el-form-item>
  881. <el-form-item label="姓名" prop="nickName">
  882. <el-input v-model="addParams.nickName" clearable placeholder="请填写姓名"></el-input>
  883. </el-form-item>
  884. <el-form-item label="账号类型" prop="userType">
  885. <el-select
  886. v-model="addParams.userType"
  887. clearable
  888. placeholder="请选择账号类型"
  889. size="large"
  890. >
  891. <el-option v-for="item in state.optionUserType" :key="item.value" :label="item.name" :value="item.value" />
  892. </el-select>
  893. </el-form-item>
  894. <el-form-item label="班级" prop="classInfo">
  895. <el-cascader v-model="addParams.classInfo"
  896. :options="options"
  897. :props="{
  898. children: 'children',
  899. label: 'name',
  900. value: 'id',
  901. checkStrictly: false,
  902. expandTrigger: 'hover',
  903. emitPath: false,
  904. multiple: true,
  905. }"
  906. clearable placeholder="请选择班级" />
  907. </el-form-item>
  908. <el-form-item label="密码" prop="password">
  909. <el-input v-model="addParams.password" clearable placeholder="请填写密码"></el-input>
  910. </el-form-item>
  911. <el-form-item label="确认密码" prop="password2">
  912. <el-input v-model="addParams.password2" clearable placeholder="请填写确认密码"></el-input>
  913. </el-form-item>
  914. </el-form>
  915. <div class="dialogFoot">
  916. <el-button color="#EAEAEA" @click="state.showDialog = false" style="margin-right: 0.6rem">取消</el-button>
  917. <el-button color="#2C68FF" @click="submitForm(ruleFormRef)">确认</el-button>
  918. </div>
  919. </div>
  920. </el-dialog>
  921. <!-- 详情模态框 -->
  922. <el-dialog v-model="state.showDetails" class="dialog">
  923. <template #header>
  924. <div class="dialog-title">账号详情</div>
  925. </template>
  926. <div class="dialogBody">
  927. <el-form ref="ruleFormRef" :model="detailsDb" :rules="addRules">
  928. <el-form-item label="账号" prop="userName">
  929. <el-input v-model="detailsDb.userName" clearable placeholder="请填写账号"></el-input>
  930. </el-form-item>
  931. <el-form-item label="姓名" prop="nickName">
  932. <el-input v-model="detailsDb.nickName" clearable placeholder="请填写姓名"></el-input>
  933. </el-form-item>
  934. <el-form-item label="账号类型" prop="userType">
  935. <el-select
  936. v-model="detailsDb.userType"
  937. clearable
  938. placeholder="请选择账号类型"
  939. size="large"
  940. >
  941. <el-option v-for="item in state.optionUserType" :key="item.value" :label="item.name" :value="item.value" />
  942. </el-select>
  943. </el-form-item>
  944. <el-form-item label="班级" prop="classInfo">
  945. <el-cascader v-model="detailsDb.classInfo"
  946. :options="options"
  947. :props="{
  948. children: 'children',
  949. label: 'name',
  950. value: 'id',
  951. checkStrictly: false,
  952. expandTrigger: 'hover',
  953. emitPath: false,
  954. multiple: true,
  955. }"
  956. clearable placeholder="请选择班级" />
  957. </el-form-item>
  958. </el-form>
  959. <div class="dialogFoot">
  960. <el-button color="#EAEAEA" @click="state.showDetails = false" style="margin-right: 0.6rem">取消</el-button>
  961. <el-button color="#2C68FF" @click="updateForm(ruleFormRef)">更新</el-button>
  962. </div>
  963. </div>
  964. </el-dialog>
  965. <!-- 用于批量删除模态框 -->
  966. <el-dialog v-model="state.showDel" width="600" class="del">
  967. <template #header>
  968. <div style="display: flex; align-items: center">
  969. <img class="del-img" src="./assets/img/manageUser/error-line.svg" alt="" />
  970. <div class="del-name">批量删除</div>
  971. </div>
  972. </template>
  973. <span class="delBody">是否确认批量删除?删除后不可恢复!</span>
  974. <template #footer>
  975. <div class="dialog-footer">
  976. <el-button @click="state.showDel = false">取消</el-button>
  977. <el-button type="primary" @click="btnBatchDel">删除</el-button>
  978. </div>
  979. </template>
  980. </el-dialog>
  981. </div>
  982. </template>
  983. <style lang="scss" scoped>
  984. @use './css/commonsScrollbar.scss';
  985. .ManageUser * {
  986. -moz-user-select: none;
  987. -webkit-user-select: none;
  988. -ms-user-select: none;
  989. -khtml-user-select: none;
  990. user-select: none;
  991. box-sizing:border-box;
  992. -moz-box-sizing:border-box; /* Firefox */
  993. -webkit-box-sizing:border-box; /* Safari */
  994. }
  995. .ManageUser {
  996. position: relative;
  997. z-index: 0;
  998. width: 100%;
  999. height: 100%;
  1000. top: 0px;
  1001. left: 0px;
  1002. background-color: #ffffff;
  1003. .content {
  1004. position: relative;
  1005. width: 100%;
  1006. height: 100%;
  1007. border-radius: 1rem;
  1008. background: #ffffff;
  1009. padding: 2rem 4rem 2rem 3rem;
  1010. box-sizing: border-box;
  1011. .contentTitle {
  1012. padding: 0 0.5rem;
  1013. box-sizing: border-box;
  1014. display: flex;
  1015. align-items: center;
  1016. .contentTitle-line {
  1017. width: 0.35rem;
  1018. height: 2rem;
  1019. background: #2c68ff;
  1020. border-radius: 0.85rem;
  1021. }
  1022. .contentTitle-text {
  1023. font-weight: bold;
  1024. font-size: 1.8rem;
  1025. color: #373737;
  1026. margin-left: 1.4rem;
  1027. }
  1028. }
  1029. .contentRow {
  1030. margin-top: 2.8rem;
  1031. padding: 0 1.5rem 0 1rem;
  1032. box-sizing: border-box;
  1033. display: flex;
  1034. align-items: center;
  1035. justify-content: space-between;
  1036. .rowLeft {
  1037. display: flex;
  1038. align-items: center;
  1039. .rowSelect {
  1040. :deep(.el-select__wrapper) {
  1041. height: 3rem;
  1042. font-size: 1.1rem;
  1043. min-height: 0;
  1044. border-radius: 0.57rem;
  1045. }
  1046. }
  1047. .rowBtn {
  1048. cursor: pointer;
  1049. width: 8.8rem;
  1050. height: 3rem;
  1051. border-radius: 0.57rem;
  1052. display: flex;
  1053. align-items: center;
  1054. justify-content: center;
  1055. margin-left: 2rem;
  1056. img {
  1057. width: 1.5rem;
  1058. height: 1.5rem;
  1059. }
  1060. .rowBtn-text {
  1061. font-size: 1.15rem;
  1062. margin-left: 0.7rem;
  1063. }
  1064. .rowBtn-text1 {
  1065. color: #3d7cff;
  1066. }
  1067. .rowBtn-text2 {
  1068. color: #d8a216;
  1069. }
  1070. .rowBtn-text3 {
  1071. color: #e84d4d;
  1072. }
  1073. }
  1074. .rowBtn1 {
  1075. background: #e5eeff;
  1076. &:hover {
  1077. background: #edf3ff;
  1078. }
  1079. }
  1080. .rowBtn2 {
  1081. background: rgba(214, 154, 1, 0.1);
  1082. &:hover {
  1083. background: #f4e5bd;
  1084. }
  1085. }
  1086. .rowBtn3 {
  1087. background: rgba(232, 77, 77, 0.05);
  1088. &:hover {
  1089. background: #fad4d4;
  1090. }
  1091. }
  1092. }
  1093. .rowRight {
  1094. display: flex;
  1095. :deep(.el-input__wrapper) {
  1096. font-size: 1rem;
  1097. border-radius: 0.57rem;
  1098. }
  1099. .rowSearch {
  1100. cursor: pointer;
  1101. width: 3.9rem;
  1102. height: 3rem;
  1103. background: #3d7cff;
  1104. border-radius: 0.57rem;
  1105. font-size: 1.1rem;
  1106. color: #ffffff;
  1107. display: flex;
  1108. align-items: center;
  1109. justify-content: center;
  1110. margin-left: 0.7rem;
  1111. &:hover {
  1112. background: #77a3ff;
  1113. }
  1114. }
  1115. }
  1116. }
  1117. .contentTable {
  1118. margin-top: 2.3rem;
  1119. height: 36rem;
  1120. :deep(tr) {
  1121. border-radius: 0.57rem;
  1122. }
  1123. :deep(th) {
  1124. background: #f5f7fa;
  1125. height: 5rem;
  1126. font-size: 1.4rem;
  1127. color: #333746;
  1128. font-weight: 500;
  1129. }
  1130. :deep(td) {
  1131. height: 5rem;
  1132. font-size: 1.2rem;
  1133. color: #333333;
  1134. }
  1135. :deep(.cell) {
  1136. height: 2rem;
  1137. }
  1138. :deep(.el-checkbox) {
  1139. --el-checkbox-input-height: 2rem;
  1140. --el-checkbox-input-width: 2rem;
  1141. --el-checkbox-border-radius: 0.28rem;
  1142. // border: 1px solid #cad2d8;
  1143. }
  1144. :deep(.el-checkbox__inner:after) {
  1145. height: 1.07rem;
  1146. left: 0.64rem;
  1147. top: 0.21rem;
  1148. width: 0.5rem;
  1149. }
  1150. :deep(.el-checkbox__inner::before) {
  1151. top: 0.85rem;
  1152. }
  1153. :deep(.el-button) {
  1154. font-size: 1.2rem;
  1155. }
  1156. :deep(.el-table__inner-wrapper::before) {
  1157. height: 0px;
  1158. }
  1159. }
  1160. :deep(.classPagination) {
  1161. .el-pagination__rightwrapper {
  1162. justify-content: center;
  1163. .el-pagination__total {
  1164. color: #000000;
  1165. }
  1166. .btn-prev,
  1167. .btn-next {
  1168. background: #f7f7f7;
  1169. color: #000000;
  1170. border-radius: 1rem;
  1171. }
  1172. .number {
  1173. background: #f7f7f7;
  1174. color: #000000;
  1175. border-radius: 0.5rem;
  1176. }
  1177. .is-active {
  1178. background: #2c68ff;
  1179. color: #ffffff;
  1180. }
  1181. .el-pagination__jump {
  1182. color: #000000;
  1183. .el-pagination__editor.el-input {
  1184. width: 4.57rem;
  1185. .el-input__wrapper {
  1186. border-radius: 1rem;
  1187. }
  1188. }
  1189. }
  1190. }
  1191. }
  1192. }
  1193. :deep(.dialog) {
  1194. width: 55rem;
  1195. padding: 0;
  1196. border-radius: 1.1rem;
  1197. .el-dialog__header {
  1198. padding: 0px;
  1199. margin: 0px;
  1200. }
  1201. .dialog-title {
  1202. // width: 55rem;
  1203. width: 100%;
  1204. height: 5.9rem;
  1205. background: #2c68ff;
  1206. font-size: 1.8rem;
  1207. color: #ffffff;
  1208. display: flex;
  1209. align-items: center;
  1210. border-radius: 1.1rem 1.1rem 0 0;
  1211. padding-left: 4rem;
  1212. box-sizing: border-box;
  1213. }
  1214. .el-dialog__headerbtn {
  1215. font-size: 1.7rem;
  1216. width: 3.57rem;
  1217. height: 3.57rem;
  1218. top: 1rem;
  1219. right: 1rem;
  1220. .el-dialog__close {
  1221. color: #ffffff;
  1222. font-size: 3.7rem;
  1223. }
  1224. }
  1225. .dialogBody {
  1226. padding: 0.8rem 8rem 3.5rem 6.7rem;
  1227. box-sizing: border-box;
  1228. .el-form-item {
  1229. align-items: center;
  1230. margin-bottom: 2rem;
  1231. }
  1232. .el-form-item__label {
  1233. font-size: 1.2rem;
  1234. color: #000000;
  1235. padding-right: 2rem;
  1236. }
  1237. .el-input {
  1238. // height: 3.7rem;
  1239. // 这里不要限制高度,否则,多级联动多选的时候,样式会有问题,可以限制,最小高度
  1240. min-height: 3.7rem;
  1241. --el-input-bg-color: #efefef;
  1242. font-size: 1.2rem;
  1243. --el-input-border-radius: 0.57rem;
  1244. --el-input-border-color: #efefef;
  1245. --el-input-focus-border-color: #efefef;
  1246. --el-input-hover-border-color: #efefef;
  1247. .el-input__wrapper {
  1248. padding: 0 2rem;
  1249. }
  1250. }
  1251. .el-select {
  1252. .el-select__wrapper {
  1253. height: 3.7rem;
  1254. background-color: #efefef;
  1255. font-size: 1.2rem;
  1256. border-radius: 0.57rem;
  1257. // box-shadow: 0 0 0 1px #efefef inset;
  1258. padding: 0 2rem;
  1259. .el-select__caret {
  1260. color: #555555;
  1261. }
  1262. }
  1263. }
  1264. .dialogFoot {
  1265. display: flex;
  1266. justify-content: end;
  1267. margin-top: 4rem;
  1268. .el-button {
  1269. width: 8.5rem;
  1270. height: 3.2rem;
  1271. border-radius: 0.57rem;
  1272. font-size: 1.2rem;
  1273. }
  1274. }
  1275. .el-cascader {
  1276. width: 100%;
  1277. }
  1278. }
  1279. }
  1280. :deep(.del) {
  1281. .del-img {
  1282. width: 1.5rem;
  1283. height: 1.5rem;
  1284. margin-right: 0.3rem;
  1285. }
  1286. .del-name {
  1287. font-size: 1.2rem;
  1288. color: #e84d4d;
  1289. }
  1290. .el-dialog__body {
  1291. width: 100%;
  1292. height: 10rem;
  1293. display: flex;
  1294. justify-content: center;
  1295. padding-top: 3rem;
  1296. color: #000000;
  1297. box-sizing: border-box;
  1298. font-size: 1.2rem;
  1299. }
  1300. }
  1301. }
  1302. </style>