index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. <template>
  2. <div class="app-container">
  3. <el-form :model="queryParams" ref="queryForm" v-show="showSearch" :inline="true">
  4. <el-form-item label="角色名称" prop="name">
  5. <el-input v-model="queryParams.name" placeholder="请输入角色名称" clearable size="small" style="width: 240px"
  6. @keyup.enter.native="handleQuery"/>
  7. </el-form-item>
  8. <el-form-item label="角色标识" prop="code">
  9. <el-input v-model="queryParams.code" placeholder="请输入角色标识" clearable size="small" style="width: 240px"
  10. @keyup.enter.native="handleQuery"/>
  11. </el-form-item>
  12. <el-form-item label="状态" prop="status">
  13. <el-select v-model="queryParams.status" placeholder="角色状态" clearable size="small" style="width: 240px">
  14. <el-option v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
  15. </el-select>
  16. </el-form-item>
  17. <el-form-item label="创建时间">
  18. <el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="yyyy-MM-dd" type="daterange"
  19. range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
  20. </el-form-item>
  21. <el-form-item>
  22. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  23. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  24. </el-form-item>
  25. </el-form>
  26. <el-row :gutter="10" class="mb8">
  27. <el-col :span="1.5">
  28. <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  29. v-hasPermi="['system:role:create']">新增</el-button>
  30. </el-col>
  31. <el-col :span="1.5">
  32. <el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
  33. v-hasPermi="['system:role:export']">导出</el-button>
  34. </el-col>
  35. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  36. </el-row>
  37. <el-table v-loading="loading" :data="roleList">
  38. <el-table-column label="角色编号" prop="id" width="120" />
  39. <el-table-column label="角色名称" prop="name" :show-overflow-tooltip="true" width="150" />
  40. <el-table-column label="角色标识" prop="code" :show-overflow-tooltip="true" width="150" />
  41. <el-table-column label="角色类型" prop="type" width="80">
  42. <template slot-scope="scope">
  43. <dict-tag :type="DICT_TYPE.SYSTEM_ROLE_TYPE" :value="scope.row.type"/>
  44. </template>
  45. </el-table-column>
  46. <el-table-column label="显示顺序" prop="sort" width="100" />
  47. <el-table-column label="状态" align="center" width="100">
  48. <template slot-scope="scope">
  49. <el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
  50. </template>
  51. </el-table-column>
  52. <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  53. <template slot-scope="scope">
  54. <span>{{ parseTime(scope.row.createTime) }}</span>
  55. </template>
  56. </el-table-column>
  57. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  58. <template slot-scope="scope">
  59. <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  60. v-hasPermi="['system:role:update']">修改</el-button>
  61. <el-button size="mini" type="text" icon="el-icon-circle-check" @click="handleMenu(scope.row)"
  62. v-hasPermi="['system:permission:assign-role-menu']">菜单权限</el-button>
  63. <el-button size="mini" type="text" icon="el-icon-circle-check" @click="handleDataScope(scope.row)"
  64. v-hasPermi="['system:permission:assign-role-data-scope']">数据权限</el-button>
  65. <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  66. v-hasPermi="['system:role:delete']">删除</el-button>
  67. </template>
  68. </el-table-column>
  69. </el-table>
  70. <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  71. @pagination="getList"/>
  72. <!-- 添加或修改角色配置对话框 -->
  73. <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  74. <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  75. <el-form-item label="角色名称" prop="name">
  76. <el-input v-model="form.name" placeholder="请输入角色名称" />
  77. </el-form-item>
  78. <el-form-item label="角色标识" prop="code">
  79. <el-input v-model="form.code" placeholder="请输入角色标识" />
  80. </el-form-item>
  81. <el-form-item label="角色顺序" prop="sort">
  82. <el-input-number v-model="form.sort" controls-position="right" :min="0" />
  83. </el-form-item>
  84. <el-form-item label="备注">
  85. <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
  86. </el-form-item>
  87. </el-form>
  88. <div slot="footer" class="dialog-footer">
  89. <el-button type="primary" @click="submitForm">确 定</el-button>
  90. <el-button @click="cancel">取 消</el-button>
  91. </div>
  92. </el-dialog>
  93. <!-- 分配角色的数据权限对话框 -->
  94. <el-dialog title="分配数据权限" :visible.sync="openDataScope" width="500px" append-to-body>
  95. <el-form :model="form" label-width="80px">
  96. <el-form-item label="角色名称">
  97. <el-input v-model="form.name" :disabled="true" />
  98. </el-form-item>
  99. <el-form-item label="角色标识">
  100. <el-input v-model="form.code" :disabled="true" />
  101. </el-form-item>
  102. <el-form-item label="权限范围">
  103. <el-select v-model="form.dataScope">
  104. <el-option
  105. v-for="item in dataScopeDictDatas"
  106. :key="parseInt(item.value)"
  107. :label="item.label"
  108. :value="parseInt(item.value)"
  109. ></el-option>
  110. </el-select>
  111. </el-form-item>
  112. <el-form-item label="数据权限" v-show="form.dataScope === SysDataScopeEnum.DEPT_CUSTOM">
  113. <el-checkbox :checked="!form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动(选中父节点,自动选择子节点)</el-checkbox>
  114. <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox>
  115. <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
  116. <el-tree
  117. class="tree-border"
  118. :data="deptOptions"
  119. show-checkbox
  120. default-expand-all
  121. ref="dept"
  122. node-key="id"
  123. :check-strictly="form.deptCheckStrictly"
  124. empty-text="加载中,请稍后"
  125. :props="defaultProps"
  126. ></el-tree>
  127. </el-form-item>
  128. </el-form>
  129. <div slot="footer" class="dialog-footer">
  130. <el-button type="primary" @click="submitDataScope">确 定</el-button>
  131. <el-button @click="cancelDataScope">取 消</el-button>
  132. </div>
  133. </el-dialog>
  134. <!-- 分配角色的菜单权限对话框 -->
  135. <el-dialog :title="title" :visible.sync="openMenu" width="500px" append-to-body>
  136. <el-form :model="form" label-width="80px">
  137. <el-form-item label="角色名称">
  138. <el-input v-model="form.name" :disabled="true" />
  139. </el-form-item>
  140. <el-form-item label="角色标识">
  141. <el-input v-model="form.code" :disabled="true" />
  142. </el-form-item>
  143. <el-form-item label="菜单权限">
  144. <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox>
  145. <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
  146. <el-tree
  147. class="tree-border"
  148. :data="menuOptions"
  149. show-checkbox
  150. ref="menu"
  151. node-key="id"
  152. :check-strictly="form.menuCheckStrictly"
  153. empty-text="加载中,请稍后"
  154. :props="defaultProps"
  155. ></el-tree>
  156. </el-form-item>
  157. </el-form>
  158. <div slot="footer" class="dialog-footer">
  159. <el-button type="primary" @click="submitMenu">确 定</el-button>
  160. <el-button @click="cancelMenu">取 消</el-button>
  161. </div>
  162. </el-dialog>
  163. </div>
  164. </template>
  165. <script>
  166. import {
  167. addRole,
  168. changeRoleStatus,
  169. dataScope,
  170. delRole,
  171. exportRole,
  172. getRole,
  173. listRole,
  174. updateRole
  175. } from "@/api/system/role";
  176. import {listSimpleMenus} from "@/api/system/menu";
  177. import {assignRoleMenu, listRoleMenus, assignRoleDataScope} from "@/api/system/permission";
  178. import {listSimpleDepts} from "@/api/system/dept";
  179. import {CommonStatusEnum, SystemDataScopeEnum} from "@/utils/constants";
  180. import {DICT_TYPE, getDictDatas} from "@/utils/dict";
  181. export default {
  182. name: "Role",
  183. data() {
  184. return {
  185. // 遮罩层
  186. loading: true,
  187. // 导出遮罩层
  188. exportLoading: false,
  189. // 显示搜索条件
  190. showSearch: true,
  191. // 总条数
  192. total: 0,
  193. // 角色表格数据
  194. roleList: [],
  195. // 弹出层标题
  196. title: "",
  197. // 是否显示弹出层
  198. open: false,
  199. // 是否显示弹出层(数据权限)
  200. openDataScope: false,
  201. // 是否显示弹出层(菜单权限)
  202. openMenu: false,
  203. menuExpand: false,
  204. menuNodeAll: false,
  205. deptExpand: true,
  206. deptNodeAll: false,
  207. // 日期范围
  208. dateRange: [],
  209. // 菜单列表
  210. menuOptions: [],
  211. // 部门列表
  212. deptOptions: [], // 部门属性结构
  213. depts: [], // 部门列表
  214. // 查询参数
  215. queryParams: {
  216. pageNo: 1,
  217. pageSize: 10,
  218. name: undefined,
  219. code: undefined,
  220. status: undefined
  221. },
  222. // 表单参数
  223. form: {},
  224. defaultProps: {
  225. label: "name",
  226. children: "children"
  227. },
  228. // 表单校验
  229. rules: {
  230. name: [
  231. { required: true, message: "角色名称不能为空", trigger: "blur" }
  232. ],
  233. code: [
  234. { required: true, message: "角色标识不能为空", trigger: "blur" }
  235. ],
  236. sort: [
  237. { required: true, message: "角色顺序不能为空", trigger: "blur" }
  238. ]
  239. },
  240. // 枚举
  241. SysCommonStatusEnum: CommonStatusEnum,
  242. SysDataScopeEnum: SystemDataScopeEnum,
  243. // 数据字典
  244. roleTypeDictDatas: getDictDatas(DICT_TYPE.SYSTEM_ROLE_TYPE),
  245. statusDictDatas: getDictDatas(DICT_TYPE.COMMON_STATUS),
  246. dataScopeDictDatas: getDictDatas(DICT_TYPE.SYSTEM_DATA_SCOPE)
  247. };
  248. },
  249. created() {
  250. this.getList();
  251. },
  252. methods: {
  253. /** 查询角色列表 */
  254. getList() {
  255. this.loading = true;
  256. listRole(this.addDateRange(this.queryParams, [
  257. this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined,
  258. this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined,
  259. ])).then(
  260. response => {
  261. this.roleList = response.data.list;
  262. this.total = response.data.total;
  263. this.loading = false;
  264. }
  265. );
  266. },
  267. // 角色状态修改
  268. handleStatusChange(row) {
  269. // 此时,row 已经变成目标状态了,所以可以直接提交请求和提示
  270. let text = row.status === CommonStatusEnum.ENABLE ? "启用" : "停用";
  271. this.$modal.confirm('确认要"' + text + '""' + row.name + '"角色吗?').then(function() {
  272. return changeRoleStatus(row.id, row.status);
  273. }).then(() => {
  274. this.$modal.msgSuccess(text + "成功");
  275. }).catch(function() {
  276. // 异常时,需要将 row.status 状态重置回之前的
  277. row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE
  278. : CommonStatusEnum.ENABLE;
  279. });
  280. },
  281. // 取消按钮
  282. cancel() {
  283. this.open = false;
  284. this.reset();
  285. },
  286. // 取消按钮(数据权限)
  287. cancelDataScope() {
  288. this.openDataScope = false;
  289. this.reset();
  290. },
  291. // 取消按钮(菜单权限)
  292. cancelMenu() {
  293. this.openMenu = false;
  294. this.reset();
  295. },
  296. // 表单重置
  297. reset() {
  298. if (this.$refs.menu !== undefined) {
  299. this.$refs.menu.setCheckedKeys([]);
  300. }
  301. this.menuExpand = false;
  302. this.menuNodeAll = false;
  303. this.deptExpand = true;
  304. this.deptNodeAll = false;
  305. this.form = {
  306. id: undefined,
  307. name: undefined,
  308. code: undefined,
  309. sort: 0,
  310. deptIds: [],
  311. menuIds: [],
  312. dataScope: undefined,
  313. deptCheckStrictly: false,
  314. menuCheckStrictly: true,
  315. remark: undefined
  316. };
  317. this.resetForm("form");
  318. },
  319. /** 搜索按钮操作 */
  320. handleQuery() {
  321. this.queryParams.pageNo = 1;
  322. this.getList();
  323. },
  324. /** 重置按钮操作 */
  325. resetQuery() {
  326. this.dateRange = [];
  327. this.resetForm("queryForm");
  328. this.handleQuery();
  329. },
  330. // 树权限(展开/折叠)
  331. handleCheckedTreeExpand(value, type) {
  332. if (type === 'menu') {
  333. let treeList = this.menuOptions;
  334. for (let i = 0; i < treeList.length; i++) {
  335. this.$refs.menu.store.nodesMap[treeList[i].id].expanded = value;
  336. }
  337. } else if (type === 'dept') {
  338. let treeList = this.deptOptions;
  339. for (let i = 0; i < treeList.length; i++) {
  340. this.$refs.dept.store.nodesMap[treeList[i].id].expanded = value;
  341. }
  342. }
  343. },
  344. // 树权限(全选/全不选)
  345. handleCheckedTreeNodeAll(value, type) {
  346. if (type === 'menu') {
  347. this.$refs.menu.setCheckedNodes(value ? this.menuOptions: []);
  348. } else if (type === 'dept') {
  349. // this.$refs.dept.setCheckedNodes(value ? this.deptOptions: []);
  350. this.$refs.dept.setCheckedNodes(value ? this.depts: []);
  351. }
  352. },
  353. // 树权限(父子联动)
  354. handleCheckedTreeConnect(value, type) {
  355. if (type === 'menu') {
  356. this.form.menuCheckStrictly = value;
  357. } else if (type === 'dept') {
  358. this.form.deptCheckStrictly = !value;
  359. }
  360. },
  361. /** 新增按钮操作 */
  362. handleAdd() {
  363. this.reset();
  364. this.open = true;
  365. this.title = "添加角色";
  366. },
  367. /** 修改按钮操作 */
  368. handleUpdate(row) {
  369. this.reset();
  370. const id = row.id
  371. getRole(id).then(response => {
  372. this.form = response.data;
  373. this.open = true;
  374. this.title = "修改角色";
  375. });
  376. },
  377. /** 分配菜单权限操作 */
  378. handleMenu(row) {
  379. this.reset();
  380. const id = row.id
  381. // 处理了 form 的角色 name 和 code 的展示
  382. this.form.id = id;
  383. this.form.name = row.name;
  384. this.form.code = row.code;
  385. // 打开弹窗
  386. this.openMenu = true;
  387. // 获得菜单列表
  388. listSimpleMenus().then(response => {
  389. // 处理 menuOptions 参数
  390. this.menuOptions = [];
  391. this.menuOptions.push(...this.handleTree(response.data, "id"));
  392. });
  393. // 获得角色拥有的菜单集合
  394. listRoleMenus(id).then(response => {
  395. // 设置为严格,避免设置父节点自动选中子节点,解决半选中问题
  396. this.form.menuCheckStrictly = true
  397. // 设置选中
  398. this.$refs.menu.setCheckedKeys(response.data);
  399. // 设置为非严格,继续使用半选中
  400. this.form.menuCheckStrictly = false
  401. })
  402. },
  403. /** 分配数据权限操作 */
  404. handleDataScope(row) {
  405. this.reset();
  406. // 处理了 form 的角色 name 和 code 的展示
  407. this.form.id = row.id;
  408. this.form.name = row.name;
  409. this.form.code = row.code;
  410. // 打开弹窗
  411. this.openDataScope = true;
  412. // 获得部门列表
  413. listSimpleDepts().then(response => {
  414. // 处理 deptOptions 参数
  415. this.deptOptions = [];
  416. this.deptOptions.push(...this.handleTree(response.data, "id"));
  417. this.depts = response.data;
  418. // this.deptIds = response.data.map(x => x.id);
  419. // 获得角色拥有的数据权限
  420. getRole(row.id).then(response => {
  421. this.form.dataScope = response.data.dataScope;
  422. this.$refs.dept.setCheckedKeys(response.data.dataScopeDeptIds, false);
  423. });
  424. });
  425. },
  426. /** 提交按钮 */
  427. submitForm: function() {
  428. this.$refs["form"].validate(valid => {
  429. if (valid) {
  430. if (this.form.id !== undefined) {
  431. updateRole(this.form).then(response => {
  432. this.$modal.msgSuccess("修改成功");
  433. this.open = false;
  434. this.getList();
  435. });
  436. } else {
  437. addRole(this.form).then(response => {
  438. this.$modal.msgSuccess("新增成功");
  439. this.open = false;
  440. this.getList();
  441. });
  442. }
  443. }
  444. });
  445. },
  446. /** 提交按钮(数据权限) */
  447. submitDataScope: function() {
  448. if (this.form.id !== undefined) {
  449. assignRoleDataScope({
  450. roleId: this.form.id,
  451. dataScope: this.form.dataScope,
  452. dataScopeDeptIds: this.form.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM ? [] :
  453. this.$refs.dept.getCheckedKeys()
  454. }).then(response => {
  455. this.$modal.msgSuccess("修改成功");
  456. this.openDataScope = false;
  457. this.getList();
  458. });
  459. }
  460. },
  461. /** 提交按钮(菜单权限) */
  462. submitMenu: function() {
  463. if (this.form.id !== undefined) {
  464. assignRoleMenu({
  465. roleId: this.form.id,
  466. menuIds: [...this.$refs.menu.getCheckedKeys(), ...this.$refs.menu.getHalfCheckedKeys()]
  467. }).then(response => {
  468. this.$modal.msgSuccess("修改成功");
  469. this.openMenu = false;
  470. this.getList();
  471. });
  472. }
  473. },
  474. /** 删除按钮操作 */
  475. handleDelete(row) {
  476. const ids = row.id || this.ids;
  477. this.$modal.confirm('是否确认删除角色编号为"' + ids + '"的数据项?').then(function() {
  478. return delRole(ids);
  479. }).then(() => {
  480. this.getList();
  481. this.$modal.msgSuccess("删除成功");
  482. }).catch(() => {});
  483. },
  484. /** 导出按钮操作 */
  485. handleExport() {
  486. const queryParams = this.queryParams;
  487. this.$modal.confirm('是否确认导出所有角色数据项?').then(function() {
  488. this.exportLoading = true;
  489. return exportRole(queryParams);
  490. }).then(response => {
  491. this.$download.excel(response, '角色数据.xls');
  492. this.exportLoading = false;
  493. }).catch(() => {});
  494. }
  495. }
  496. };
  497. </script>