permission.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <template>
  2. <el-dialog :title="title" v-model="isShowDialog" width="1100px">
  3. <div class="mb-4 tr">
  4. <el-dropdown>
  5. <el-button plain class="mr-3">
  6. 操作<el-icon>
  7. <ele-ArrowDown />
  8. </el-icon>
  9. </el-button>
  10. <template #dropdown>
  11. <el-dropdown-menu>
  12. <el-dropdown-item @click.native="checkAll(true)">全部勾选</el-dropdown-item>
  13. <el-dropdown-item @click.native="checkAll(false)">取消全选</el-dropdown-item>
  14. <el-dropdown-item @click.native="expand(true)">展开所有</el-dropdown-item>
  15. <el-dropdown-item @click.native="expand(false)">折叠所有</el-dropdown-item>
  16. </el-dropdown-menu>
  17. </template>
  18. </el-dropdown>
  19. <el-button :disabled="step <= 1" @click="prev">上一步</el-button>
  20. <el-button :disabled="step >= 4" @click="next">下一步</el-button>
  21. <el-button type="primary" :loading="btnLoading" @click="submit">确定</el-button>
  22. <el-button @click="cancel">取消</el-button>
  23. </div>
  24. <el-steps :active="step" simple finish-status="success">
  25. <el-step name title="菜单权限" />
  26. <el-step title="按钮权限" />
  27. <el-step title="列表权限" />
  28. <el-step title="接口权限" />
  29. </el-steps>
  30. <div class="scroll-part mt-3">
  31. <el-tree ref="treeRef" :data="treeData" show-checkbox default-expand-all node-key="id" highlight-current :props="defaultProps" check-on-click-node :expand-on-click-node="false" />
  32. </div>
  33. </el-dialog>
  34. </template>
  35. <script lang="ts" setup>
  36. import { ref } from 'vue';
  37. import api from '/@/api/system';
  38. import { ElMessage } from 'element-plus';
  39. const isShowDialog = ref(false);
  40. const btnLoading = ref(false);
  41. const step = ref(1);
  42. const treeRef = ref();
  43. const title = ref('角色权限设置');
  44. const roleId = ref(0);
  45. const treeData = ref([]);
  46. const menuData = ref([]);
  47. const buttonData = ref([]);
  48. const listData = ref([]);
  49. const apiData = ref([]);
  50. const menuIds = ref<number[]>([]);
  51. // 菜单id的半选节点
  52. const menuIdsHalf = ref<number[]>([]);
  53. const buttonIds = ref<number[]>([]);
  54. // 按钮id的半选节点
  55. const buttonIdsHalf = ref<number[]>([]);
  56. const columnIds = ref<number[]>([]);
  57. const apiIds = ref<number[]>([]);
  58. const typeList = ['menu', 'button', 'column', 'api'];
  59. let idsList = [menuIds, buttonIds, columnIds, apiIds];
  60. const treeDataList = [menuData, buttonData, listData, apiData];
  61. const defaultProps = {
  62. children: 'children',
  63. label: 'title',
  64. };
  65. // 返回的所有id都是 5_0 7_1 这种形式,_0代表半选 _1代表已选
  66. const getIds = (idsArr: string[], isHalf = false) => {
  67. const ids: number[] = [];
  68. const idsHalf: number[] = [];
  69. (idsArr || []).forEach((idStr) => {
  70. const [id, tag] = idStr.split('_');
  71. if (tag === '1') {
  72. ids.push(Number(id));
  73. } else {
  74. idsHalf.push(Number(id));
  75. }
  76. });
  77. return isHalf ? idsHalf : ids;
  78. };
  79. // 设置id 5_0 7_1 这种形式,_0代表半选 _1代表已选
  80. const setIds = (idsArr: number[], isHalf = false) => {
  81. return idsArr.map((id) => `${id}_${isHalf ? 0 : 1}`);
  82. };
  83. const openDialog = async (row: any) => {
  84. title.value = '角色权限设置 - ' + row.name;
  85. roleId.value = row.id;
  86. isShowDialog.value = true;
  87. step.value = 1;
  88. const ids = await api.role.getRoleIds(row.id);
  89. // idsList = ids.menuIds || [];
  90. menuIds.value = getIds(ids.menuIds);
  91. buttonIds.value = getIds(ids.buttonIds);
  92. columnIds.value = getIds(ids.columnIds);
  93. apiIds.value = getIds(ids.apiIds);
  94. // 设置选中
  95. treeRef.value.setCheckedKeys(menuIds.value);
  96. const res = await api.role.auth.getList(typeList[step.value - 1]);
  97. treeData.value = res;
  98. menuData.value = res;
  99. };
  100. const cancel = () => {
  101. isShowDialog.value = false;
  102. };
  103. const expand = (expand: boolean) => {
  104. const nodes = treeRef.value.store.nodesMap;
  105. for (let i in nodes) {
  106. nodes[i].expanded = expand;
  107. }
  108. };
  109. const prev = async () => {
  110. stepChange();
  111. // 获取选中id
  112. treeData.value = treeDataList[step.value - 2].value;
  113. treeRef.value.setCheckedKeys(idsList[step.value - 2].value);
  114. step.value = step.value - 1;
  115. };
  116. const next = async () => {
  117. stepChange();
  118. const treeDataRes = await api.role.auth.getList(typeList[step.value], menuIds.value.concat(menuIdsHalf.value));
  119. // 最外层是菜单,如果菜单下没有按钮,列表或者接口,就不显示这个菜单
  120. // 菜单id和其他id可能会重复,所以最外层的菜单id变一下,避免重复
  121. const itemsType = typeList[step.value]
  122. const treeDateFilter = (treeDataRes || []).filter((item: any) => {
  123. if (item.children?.length) {
  124. item.id += '_memu';
  125. if (itemsType === 'api') {
  126. item.children.forEach((i: any) => {
  127. i.title = i.method + '-' + i.title + (i.remark ? `(${i.remark})` : '')
  128. });
  129. }
  130. return true;
  131. }
  132. return false;
  133. });
  134. treeData.value = treeDateFilter;
  135. treeDataList[step.value].value = treeDateFilter;
  136. treeRef.value.setCheckedKeys(idsList[step.value].value);
  137. step.value = step.value + 1;
  138. };
  139. // 切换时候赋值
  140. const stepChange = () => {
  141. if (step.value === 1) {
  142. // 包含被选中节点和半选中节点
  143. idsList[step.value - 1].value = treeRef.value.getCheckedKeys(false);
  144. menuIdsHalf.value = treeRef.value.getHalfCheckedKeys();
  145. } else if (step.value === 2) {
  146. // 包含被选中节点和半选中节点
  147. idsList[step.value - 1].value = treeRef.value.getCheckedKeys(false).filter((id: any) => typeof id === 'number');
  148. // 按钮的半选节点过滤菜单id, 菜单id是字符串类型。 id_menu 这种形式,把这种过滤掉
  149. buttonIdsHalf.value = treeRef.value.getHalfCheckedKeys().filter((id: any) => typeof id === 'number');
  150. } else {
  151. // 只返回叶子节点
  152. idsList[step.value - 1].value = treeRef.value.getCheckedKeys(true);
  153. }
  154. };
  155. // 全选取消全选
  156. const checkAll = (all: boolean) => {
  157. if (!all) {
  158. treeRef.value.setCheckedKeys([]);
  159. } else {
  160. const ids = deepTree(treeDataList[step.value - 1].value, []);
  161. treeRef.value.setCheckedKeys(ids);
  162. }
  163. };
  164. const submit = async () => {
  165. stepChange();
  166. // 5_0 7_1 这种形式,_0代表半选 _1代表已选
  167. const data = {
  168. menuIds: setIds(menuIds.value).concat(setIds(menuIdsHalf.value, true)),
  169. buttonIds: setIds(buttonIds.value).concat(setIds(buttonIdsHalf.value, true)),
  170. columnIds: setIds(columnIds.value),
  171. apiIds: setIds(apiIds.value),
  172. roleId: roleId.value,
  173. };
  174. btnLoading.value = true;
  175. api.role.auth
  176. .set(data)
  177. .then(() => {
  178. ElMessage.success('权限设置成功');
  179. })
  180. .finally(() => {
  181. btnLoading.value = false;
  182. isShowDialog.value = false;
  183. });
  184. };
  185. function deepTree(tree: any[], arr: number[]) {
  186. for (let item of tree) {
  187. arr.push(item.id);
  188. if (item.children?.length) deepTree(item.children, arr);
  189. }
  190. return arr;
  191. }
  192. // openDialog({ name: '超级管理员', id: 3 });
  193. defineExpose({ openDialog });
  194. </script>
  195. <style scoped lang="scss">
  196. .scroll-part {
  197. height: calc(80vh - 200px);
  198. overflow-x: hidden;
  199. overflow-y: auto;
  200. }
  201. </style>