user.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. <template>
  2. <el-row>
  3. <el-col :span="5">
  4. <div class="box">
  5. <el-scrollbar>
  6. <basic-container>
  7. <avue-tree :option="treeOption" :data="treeData" @node-click="nodeClick"/>
  8. </basic-container>
  9. </el-scrollbar>
  10. </div>
  11. </el-col>
  12. <el-col :span="19">
  13. <basic-container>
  14. <avue-crud :option="option"
  15. :table-loading="loading"
  16. :data="data"
  17. ref="crud"
  18. v-model="form"
  19. :permission="permissionList"
  20. @row-del="rowDel"
  21. @row-update="rowUpdate"
  22. @row-save="rowSave"
  23. :before-open="beforeOpen"
  24. :page.sync="page"
  25. @search-change="searchChange"
  26. @search-reset="searchReset"
  27. @selection-change="selectionChange"
  28. @current-change="currentChange"
  29. @size-change="sizeChange"
  30. @refresh-change="refreshChange"
  31. @on-load="onLoad">
  32. <template slot="menuLeft">
  33. <el-button type="danger"
  34. size="small"
  35. plain
  36. icon="el-icon-delete"
  37. v-if="permission.user_delete"
  38. @click="handleDelete">删 除
  39. </el-button>
  40. <el-button type="info"
  41. size="small"
  42. plain
  43. v-if="permission.user_role"
  44. icon="el-icon-user"
  45. @click="handleGrant">角色配置
  46. </el-button>
  47. <el-button size="small"
  48. plain
  49. v-if="permission.user_reset"
  50. icon="el-icon-refresh"
  51. @click="handleReset">密码重置
  52. </el-button>
  53. <el-button type="success"
  54. size="small"
  55. plain
  56. v-if="userInfo.role_name.includes('admin')"
  57. icon="el-icon-upload2"
  58. @click="handleImport">导入
  59. </el-button>
  60. <el-button type="warning"
  61. size="small"
  62. plain
  63. v-if="userInfo.role_name.includes('admin')"
  64. icon="el-icon-download"
  65. @click="handleExport">导出
  66. </el-button>
  67. </template>
  68. <template slot-scope="{row}"
  69. slot="tenantName">
  70. <el-tag>{{row.tenantName}}</el-tag>
  71. </template>
  72. <template slot-scope="{row}"
  73. slot="roleName">
  74. <el-tag>{{row.roleName}}</el-tag>
  75. </template>
  76. <template slot-scope="{row}"
  77. slot="deptName">
  78. <el-tag>{{row.deptName}}</el-tag>
  79. </template>
  80. </avue-crud>
  81. <el-dialog title="用户角色配置"
  82. append-to-body
  83. :visible.sync="roleBox"
  84. width="345px">
  85. <el-tree :data="roleGrantList"
  86. show-checkbox
  87. check-strictly
  88. default-expand-all
  89. node-key="id"
  90. ref="treeRole"
  91. :default-checked-keys="roleTreeObj"
  92. :props="props">
  93. </el-tree>
  94. <span slot="footer" class="dialog-footer">
  95. <el-button @click="roleBox = false">取 消</el-button>
  96. <el-button type="primary"
  97. @click="submitRole">确 定</el-button>
  98. </span>
  99. </el-dialog>
  100. <el-dialog title="用户数据导入"
  101. append-to-body
  102. :visible.sync="excelBox"
  103. width="555px">
  104. <avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
  105. <template slot="excelTemplate">
  106. <el-button type="primary" @click="handleTemplate">
  107. 点击下载<i class="el-icon-download el-icon--right"></i>
  108. </el-button>
  109. </template>
  110. </avue-form>
  111. </el-dialog>
  112. </basic-container>
  113. </el-col>
  114. </el-row>
  115. </template>
  116. <script>
  117. import {
  118. getList,
  119. getUser,
  120. remove,
  121. update,
  122. add,
  123. grant,
  124. resetPassword
  125. } from "@/api/system/user";
  126. import {getDeptTree, getDeptLazyTree} from "@/api/system/dept";
  127. import {getRoleTree} from "@/api/system/role";
  128. import {getPostList} from "@/api/system/post";
  129. import {mapGetters} from "vuex";
  130. import website from '@/config/website';
  131. import {getToken} from '@/util/auth';
  132. export default {
  133. data() {
  134. const validatePass = (rule, value, callback) => {
  135. if (value === '') {
  136. callback(new Error('请输入密码'));
  137. } else {
  138. callback();
  139. }
  140. };
  141. const validatePass2 = (rule, value, callback) => {
  142. if (value === '') {
  143. callback(new Error('请再次输入密码'));
  144. } else if (value !== this.form.password) {
  145. callback(new Error('两次输入密码不一致!'));
  146. } else {
  147. callback();
  148. }
  149. };
  150. return {
  151. form: {},
  152. roleBox: false,
  153. excelBox: false,
  154. initFlag: true,
  155. selectionList: [],
  156. query: {},
  157. loading: true,
  158. page: {
  159. pageSize: 10,
  160. currentPage: 1,
  161. total: 0
  162. },
  163. init: {
  164. roleTree: [],
  165. deptTree: [],
  166. },
  167. props: {
  168. label: "title",
  169. value: "key"
  170. },
  171. roleGrantList: [],
  172. roleTreeObj: [],
  173. treeDeptId: '',
  174. treeData: [],
  175. treeOption: {
  176. nodeKey: 'id',
  177. lazy: true,
  178. treeLoad: function (node, resolve) {
  179. const parentId = (node.level === 0) ? 0 : node.data.id;
  180. getDeptLazyTree(parentId).then(res => {
  181. resolve(res.data.data.map(item => {
  182. return {
  183. ...item,
  184. leaf: !item.hasChildren
  185. }
  186. }))
  187. });
  188. },
  189. addBtn: false,
  190. menu: false,
  191. size: 'small',
  192. props: {
  193. labelText: '标题',
  194. label: 'title',
  195. value: 'value',
  196. children: 'children'
  197. }
  198. },
  199. option: {
  200. height: 'auto',
  201. calcHeight: 80,
  202. tip: false,
  203. searchShow: true,
  204. searchMenuSpan: 6,
  205. border: true,
  206. index: true,
  207. selection: true,
  208. viewBtn: true,
  209. //dialogType: 'drawer',
  210. dialogClickModal: false,
  211. column: [
  212. {
  213. label: "登录账号",
  214. prop: "account",
  215. search: true,
  216. display: false
  217. },
  218. {
  219. label: "所属租户",
  220. prop: "tenantName",
  221. slot: true,
  222. display: false
  223. },
  224. {
  225. label: "用户姓名",
  226. prop: "realName",
  227. search: true,
  228. display: false
  229. },
  230. {
  231. label: "所属角色",
  232. prop: "roleName",
  233. slot: true,
  234. display: false
  235. },
  236. {
  237. label: "所属部门",
  238. prop: "deptName",
  239. slot: true,
  240. display: false
  241. },
  242. ],
  243. group: [
  244. {
  245. label: '基础信息',
  246. prop: 'baseInfo',
  247. icon: 'el-icon-user-solid',
  248. column: [
  249. {
  250. label: "登录账号",
  251. prop: "account",
  252. rules: [{
  253. required: true,
  254. message: "请输入登录账号",
  255. trigger: "blur"
  256. }],
  257. span: website.tenantMode ? 12 : 24,
  258. },
  259. {
  260. label: "所属租户",
  261. prop: "tenantId",
  262. type: "tree",
  263. dicUrl: "/api/blade-system/tenant/select",
  264. props: {
  265. label: "tenantName",
  266. value: "tenantId"
  267. },
  268. hide: !website.tenantMode,
  269. addDisplay: website.tenantMode,
  270. editDisplay: website.tenantMode,
  271. viewDisplay: website.tenantMode,
  272. rules: [{
  273. required: true,
  274. message: "请输入所属租户",
  275. trigger: "click"
  276. }]
  277. },
  278. {
  279. label: '密码',
  280. prop: 'password',
  281. hide: true,
  282. editDisplay: false,
  283. viewDisplay: false,
  284. rules: [{required: true, validator: validatePass, trigger: 'blur'}]
  285. },
  286. {
  287. label: '确认密码',
  288. prop: 'password2',
  289. hide: true,
  290. editDisplay: false,
  291. viewDisplay: false,
  292. rules: [{required: true, validator: validatePass2, trigger: 'blur'}]
  293. },
  294. ]
  295. },
  296. {
  297. label: '详细信息',
  298. prop: 'detailInfo',
  299. icon: 'el-icon-s-order',
  300. column: [
  301. {
  302. label: "用户昵称",
  303. prop: "name",
  304. hide: true,
  305. rules: [{
  306. required: true,
  307. message: "请输入用户昵称",
  308. trigger: "blur"
  309. }]
  310. },
  311. {
  312. label: "用户姓名",
  313. prop: "realName",
  314. rules: [{
  315. required: true,
  316. message: "请输入用户姓名",
  317. trigger: "blur"
  318. }, {
  319. min: 2,
  320. max: 5,
  321. message: '姓名长度在2到5个字符'
  322. }]
  323. },
  324. {
  325. label: "手机号码",
  326. prop: "phone",
  327. overHidden: true
  328. },
  329. {
  330. label: "电子邮箱",
  331. prop: "email",
  332. hide: true,
  333. overHidden: true
  334. },
  335. {
  336. label: "用户性别",
  337. prop: "sex",
  338. type: "select",
  339. dicData: [
  340. {
  341. label: "男",
  342. value: 1
  343. },
  344. {
  345. label: "女",
  346. value: 2
  347. },
  348. {
  349. label: "未知",
  350. value: 3
  351. }
  352. ],
  353. hide: true
  354. },
  355. {
  356. label: "用户生日",
  357. type: "date",
  358. prop: "birthday",
  359. format: "yyyy-MM-dd hh:mm:ss",
  360. valueFormat: "yyyy-MM-dd hh:mm:ss",
  361. hide: true
  362. },
  363. {
  364. label: "账号状态",
  365. prop: "statusName",
  366. hide: true,
  367. display: false
  368. }
  369. ]
  370. },
  371. {
  372. label: '职责信息',
  373. prop: 'dutyInfo',
  374. icon: 'el-icon-s-custom',
  375. column: [
  376. {
  377. label: "用户编号",
  378. prop: "code",
  379. },
  380. {
  381. label: "所属角色",
  382. prop: "roleId",
  383. multiple: true,
  384. type: "tree",
  385. dicData: [],
  386. props: {
  387. label: "title"
  388. },
  389. checkStrictly: true,
  390. slot: true,
  391. rules: [{
  392. required: true,
  393. message: "请选择所属角色",
  394. trigger: "click"
  395. }]
  396. },
  397. {
  398. label: "所属部门",
  399. prop: "deptId",
  400. type: "tree",
  401. multiple: true,
  402. dicData: [],
  403. props: {
  404. label: "title"
  405. },
  406. checkStrictly: true,
  407. slot: true,
  408. rules: [{
  409. required: true,
  410. message: "请选择所属部门",
  411. trigger: "click"
  412. }]
  413. },
  414. {
  415. label: "所属岗位",
  416. prop: "postId",
  417. type: "tree",
  418. multiple: true,
  419. dicData: [],
  420. props: {
  421. label: "postName",
  422. value: "id"
  423. },
  424. rules: [{
  425. required: true,
  426. message: "请选择所属岗位",
  427. trigger: "click"
  428. }],
  429. },
  430. ]
  431. },
  432. ]
  433. },
  434. data: [],
  435. excelForm: {},
  436. excelOption: {
  437. submitBtn: false,
  438. emptyBtn: false,
  439. column: [
  440. {
  441. label: '模板上传',
  442. prop: 'excelFile',
  443. type: 'upload',
  444. drag: true,
  445. loadText: '模板上传中,请稍等',
  446. span: 24,
  447. propsHttp: {
  448. res: 'data'
  449. },
  450. tip: '请上传 .xls,.xlsx 标准格式文件',
  451. action: "/api/blade-user/import-user"
  452. },
  453. {
  454. label: "数据覆盖",
  455. prop: "isCovered",
  456. type: "switch",
  457. align: "center",
  458. width: 80,
  459. dicData: [
  460. {
  461. label: "否",
  462. value: 0
  463. },
  464. {
  465. label: "是",
  466. value: 1
  467. }
  468. ],
  469. value: 0,
  470. slot: true,
  471. rules: [
  472. {
  473. required: true,
  474. message: "请选择是否覆盖",
  475. trigger: "blur"
  476. }
  477. ]
  478. },
  479. {
  480. label: '模板下载',
  481. prop: 'excelTemplate',
  482. formslot: true,
  483. span: 24,
  484. }
  485. ]
  486. }
  487. };
  488. },
  489. watch: {
  490. 'form.tenantId'() {
  491. if (this.form.tenantId !== '' && this.initFlag) {
  492. this.initData(this.form.tenantId);
  493. }
  494. },
  495. 'excelForm.isCovered'() {
  496. if (this.excelForm.isCovered !== '') {
  497. const column = this.findObject(this.excelOption.column, "excelFile");
  498. column.action = `/api/blade-user/import-user?isCovered=${this.excelForm.isCovered}`;
  499. }
  500. }
  501. },
  502. computed: {
  503. ...mapGetters(["userInfo", "permission"]),
  504. permissionList() {
  505. return {
  506. addBtn: this.vaildData(this.permission.user_add, false),
  507. viewBtn: this.vaildData(this.permission.user_view, false),
  508. delBtn: this.vaildData(this.permission.user_delete, false),
  509. editBtn: this.vaildData(this.permission.user_edit, false)
  510. };
  511. },
  512. ids() {
  513. let ids = [];
  514. this.selectionList.forEach(ele => {
  515. ids.push(ele.id);
  516. });
  517. return ids.join(",");
  518. },
  519. },
  520. mounted() {
  521. // 非租户模式默认加载管理组数据
  522. if (!website.tenantMode) {
  523. this.initData(website.tenantId);
  524. }
  525. },
  526. methods: {
  527. nodeClick(data) {
  528. this.treeDeptId = data.id;
  529. this.page.currentPage = 1;
  530. this.onLoad(this.page);
  531. },
  532. initData(tenantId) {
  533. getRoleTree(tenantId).then(res => {
  534. const column = this.findObject(this.option.group, "roleId");
  535. column.dicData = res.data.data;
  536. });
  537. getDeptTree(tenantId).then(res => {
  538. const column = this.findObject(this.option.group, "deptId");
  539. column.dicData = res.data.data;
  540. });
  541. getPostList(tenantId).then(res => {
  542. const column = this.findObject(this.option.group, "postId");
  543. column.dicData = res.data.data;
  544. });
  545. },
  546. submitRole() {
  547. const roleList = this.$refs.treeRole.getCheckedKeys().join(",");
  548. grant(this.ids, roleList).then(() => {
  549. this.roleBox = false;
  550. this.$message({
  551. type: "success",
  552. message: "操作成功!"
  553. });
  554. this.onLoad(this.page);
  555. });
  556. },
  557. rowSave(row, done, loading) {
  558. row.deptId = row.deptId.join(",");
  559. row.roleId = row.roleId.join(",");
  560. row.postId = row.postId.join(",");
  561. add(row).then(() => {
  562. this.initFlag = false;
  563. this.onLoad(this.page);
  564. this.$message({
  565. type: "success",
  566. message: "操作成功!"
  567. });
  568. done();
  569. }, error => {
  570. window.console.log(error);
  571. loading();
  572. });
  573. },
  574. rowUpdate(row, index, done, loading) {
  575. row.deptId = row.deptId.join(",");
  576. row.roleId = row.roleId.join(",");
  577. row.postId = row.postId.join(",");
  578. update(row).then(() => {
  579. this.initFlag = false;
  580. this.onLoad(this.page);
  581. this.$message({
  582. type: "success",
  583. message: "操作成功!"
  584. });
  585. done();
  586. }, error => {
  587. window.console.log(error);
  588. loading();
  589. });
  590. },
  591. rowDel(row) {
  592. this.$confirm("确定将选择数据删除?", {
  593. confirmButtonText: "确定",
  594. cancelButtonText: "取消",
  595. type: "warning"
  596. })
  597. .then(() => {
  598. return remove(row.id);
  599. })
  600. .then(() => {
  601. this.onLoad(this.page);
  602. this.$message({
  603. type: "success",
  604. message: "操作成功!"
  605. });
  606. });
  607. },
  608. searchReset() {
  609. this.query = {};
  610. this.treeDeptId = '';
  611. this.onLoad(this.page);
  612. },
  613. searchChange(params, done) {
  614. this.query = params;
  615. this.page.currentPage = 1;
  616. this.onLoad(this.page, params);
  617. done();
  618. },
  619. selectionChange(list) {
  620. this.selectionList = list;
  621. },
  622. selectionClear() {
  623. this.selectionList = [];
  624. this.$refs.crud.toggleSelection();
  625. },
  626. handleDelete() {
  627. if (this.selectionList.length === 0) {
  628. this.$message.warning("请选择至少一条数据");
  629. return;
  630. }
  631. this.$confirm("确定将选择数据删除?", {
  632. confirmButtonText: "确定",
  633. cancelButtonText: "取消",
  634. type: "warning"
  635. })
  636. .then(() => {
  637. return remove(this.ids);
  638. })
  639. .then(() => {
  640. this.onLoad(this.page);
  641. this.$message({
  642. type: "success",
  643. message: "操作成功!"
  644. });
  645. this.$refs.crud.toggleSelection();
  646. });
  647. },
  648. handleReset() {
  649. if (this.selectionList.length === 0) {
  650. this.$message.warning("请选择至少一条数据");
  651. return;
  652. }
  653. this.$confirm("确定将选择账号密码重置为123456?", {
  654. confirmButtonText: "确定",
  655. cancelButtonText: "取消",
  656. type: "warning"
  657. })
  658. .then(() => {
  659. return resetPassword(this.ids);
  660. })
  661. .then(() => {
  662. this.$message({
  663. type: "success",
  664. message: "操作成功!"
  665. });
  666. this.$refs.crud.toggleSelection();
  667. });
  668. },
  669. handleGrant() {
  670. if (this.selectionList.length === 0) {
  671. this.$message.warning("请选择至少一条数据");
  672. return;
  673. }
  674. this.roleTreeObj = [];
  675. if (this.selectionList.length === 1) {
  676. this.roleTreeObj = this.selectionList[0].roleId.split(",");
  677. }
  678. getRoleTree().then(res => {
  679. this.roleGrantList = res.data.data;
  680. this.roleBox = true;
  681. });
  682. },
  683. handleImport() {
  684. this.excelBox = true;
  685. },
  686. uploadAfter(res, done, loading, column) {
  687. window.console.log(column);
  688. this.excelBox = false;
  689. this.refreshChange();
  690. done();
  691. },
  692. handleExport() {
  693. this.$confirm("是否导出用户数据?", "提示", {
  694. confirmButtonText: "确定",
  695. cancelButtonText: "取消",
  696. type: "warning"
  697. }).then(() => {
  698. const searchForm = this.$refs.crud.searchForm;
  699. window.open(`/api/blade-user/export-user?${this.website.tokenHeader}=${getToken()}&account=${searchForm.account}&realName=${searchForm.realName}`);
  700. });
  701. },
  702. handleTemplate() {
  703. window.open(`/api/blade-user/export-template?${this.website.tokenHeader}=${getToken()}`);
  704. },
  705. beforeOpen(done, type) {
  706. if (["edit", "view"].includes(type)) {
  707. getUser(this.form.id).then(res => {
  708. this.form = res.data.data;
  709. if(this.form.hasOwnProperty("deptId")){
  710. this.form.deptId = this.form.deptId.split(",");
  711. }
  712. if(this.form.hasOwnProperty("roleId")){
  713. this.form.roleId = this.form.roleId.split(",");
  714. }
  715. if(this.form.hasOwnProperty("postId")){
  716. this.form.postId = this.form.postId.split(",");
  717. }
  718. });
  719. }
  720. this.initFlag = true;
  721. done();
  722. },
  723. currentChange(currentPage) {
  724. this.page.currentPage = currentPage;
  725. },
  726. sizeChange(pageSize) {
  727. this.page.pageSize = pageSize;
  728. },
  729. refreshChange() {
  730. this.onLoad(this.page, this.query);
  731. },
  732. onLoad(page, params = {}) {
  733. this.loading = true;
  734. getList(page.currentPage, page.pageSize, Object.assign(params, this.query), this.treeDeptId).then(res => {
  735. const data = res.data.data;
  736. this.page.total = data.total;
  737. this.data = data.records;
  738. this.loading = false;
  739. this.selectionClear();
  740. });
  741. }
  742. }
  743. };
  744. </script>
  745. <style>
  746. .box {
  747. height: 800px;
  748. }
  749. .el-scrollbar {
  750. height: 100%;
  751. }
  752. .box .el-scrollbar__wrap {
  753. overflow: scroll;
  754. }
  755. </style>