164 lines
4.6 KiB
JavaScript
164 lines
4.6 KiB
JavaScript
// Author: fruit.com.cn
|
|
|
|
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import {
|
|
createRoleApi,
|
|
deleteRoleApi,
|
|
listPermissionGroupsApi,
|
|
listRoleCategoriesApi,
|
|
listRolesApi,
|
|
updateRolePermissionsApi
|
|
} from '@/api/admin/role'
|
|
import { useUserStoreWithOut } from '@/store/modules/user'
|
|
import { useRbac } from '@/stores/rbac'
|
|
|
|
export function useRolePermissionEditor() {
|
|
const userStore = useUserStoreWithOut()
|
|
const { can } = useRbac()
|
|
|
|
const roleData = ref([])
|
|
const categories = ref([])
|
|
const permissionGroups = ref([])
|
|
const selectedRoleId = ref(null)
|
|
const dialogVisible = ref(false)
|
|
const newRoleName = ref('')
|
|
const newRoleCategoryId = ref(undefined)
|
|
const treeRef = ref()
|
|
const syncingTree = ref(false)
|
|
|
|
const permissionTree = computed(() =>
|
|
permissionGroups.value.map((group) => ({
|
|
id: `group-${group.name}`,
|
|
label: group.name,
|
|
children: group.items.map((item) => ({
|
|
id: item.code,
|
|
label: item.name,
|
|
permission: item.code
|
|
}))
|
|
}))
|
|
)
|
|
|
|
const selectedRoleRecord = computed(() =>
|
|
roleData.value.find((role) => role.id === selectedRoleId.value)
|
|
)
|
|
const selectedRole = computed(() => selectedRoleRecord.value?.name || '—')
|
|
const currentLevel = computed(() => {
|
|
const code = userStore.getUserInfo?.role
|
|
return roleData.value.find((role) => role.code === code)?.level ?? 1
|
|
})
|
|
const canEditSelectedRole = computed(
|
|
() =>
|
|
can('system:role') &&
|
|
!!selectedRoleRecord.value &&
|
|
selectedRoleRecord.value.level >= currentLevel.value
|
|
)
|
|
|
|
async function load() {
|
|
const [rolesRes, groupsRes, categoriesRes] = await Promise.all([
|
|
listRolesApi(),
|
|
listPermissionGroupsApi(),
|
|
listRoleCategoriesApi()
|
|
])
|
|
roleData.value = rolesRes.data || []
|
|
permissionGroups.value = groupsRes.data || []
|
|
categories.value = categoriesRes.data || []
|
|
if (!selectedRoleId.value && roleData.value.length) {
|
|
selectedRoleId.value = roleData.value[0].id
|
|
}
|
|
}
|
|
|
|
watch(
|
|
[selectedRoleId, permissionGroups],
|
|
async () => {
|
|
syncingTree.value = true
|
|
await nextTick()
|
|
treeRef.value?.setCheckedKeys(selectedRoleRecord.value?.permissions || [])
|
|
await nextTick()
|
|
syncingTree.value = false
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
function selectRole(row) {
|
|
selectedRoleId.value = row.id
|
|
}
|
|
|
|
function readCheckedCodes() {
|
|
const checked = treeRef.value?.getCheckedKeys() || []
|
|
return checked.filter((key) => typeof key === 'string' && !key.startsWith('group-'))
|
|
}
|
|
|
|
/** 勾选即保存:把整棵树的权限码提交给后端覆盖。 */
|
|
async function handleCheck() {
|
|
if (syncingTree.value || !canEditSelectedRole.value) return
|
|
const role = selectedRoleRecord.value
|
|
if (!role) return
|
|
const codes = readCheckedCodes()
|
|
await updateRolePermissionsApi(role.id, codes)
|
|
role.permissions = codes
|
|
ElMessage.success('权限已保存')
|
|
}
|
|
|
|
async function addRole() {
|
|
const name = newRoleName.value.trim()
|
|
if (!name) return ElMessage.warning('请输入角色名称')
|
|
if (roleData.value.some((role) => role.name === name)) {
|
|
return ElMessage.warning('角色名称已存在')
|
|
}
|
|
await createRoleApi({
|
|
name,
|
|
categoryId: newRoleCategoryId.value || categories.value[0]?.id || 0,
|
|
level: currentLevel.value + 1,
|
|
description: '自定义角色',
|
|
permissions: []
|
|
})
|
|
newRoleName.value = ''
|
|
newRoleCategoryId.value = undefined
|
|
dialogVisible.value = false
|
|
ElMessage.success('角色已创建')
|
|
await load()
|
|
const created = roleData.value.find((role) => role.name === name)
|
|
if (created) selectedRoleId.value = created.id
|
|
}
|
|
|
|
async function removeRole() {
|
|
const role = selectedRoleRecord.value
|
|
if (!role) return ElMessage.warning('请先选择角色')
|
|
if (role.builtin) return ElMessage.warning('内置角色不可删除')
|
|
try {
|
|
await ElMessageBox.confirm(`确定删除角色「${role.name}」吗?`, '删除确认', {
|
|
confirmButtonText: '确认删除',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
confirmButtonClass: 'el-button--danger'
|
|
})
|
|
} catch {
|
|
return
|
|
}
|
|
await deleteRoleApi(role.id)
|
|
ElMessage.success('角色已删除')
|
|
selectedRoleId.value = null
|
|
await load()
|
|
}
|
|
|
|
onMounted(load)
|
|
|
|
return {
|
|
roleData,
|
|
categories,
|
|
selectedRole,
|
|
selectedRoleRecord,
|
|
canEditSelectedRole,
|
|
permissionTree,
|
|
treeRef,
|
|
dialogVisible,
|
|
newRoleName,
|
|
newRoleCategoryId,
|
|
selectRole,
|
|
handleCheck,
|
|
addRole,
|
|
removeRole
|
|
}
|
|
}
|