feat: role
This commit is contained in:
parent
cfafc3f0ba
commit
e0dcc4b29d
43
ui/src/api/type/role.ts
Normal file
43
ui/src/api/type/role.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { RoleTypeEnum } from '@/enums/system'
|
||||
|
||||
interface RoleItem {
|
||||
id: string,
|
||||
role_name: string,
|
||||
type: RoleTypeEnum,
|
||||
create_user: string,
|
||||
internal: boolean,
|
||||
}
|
||||
|
||||
interface ChildrenPermissionItem {
|
||||
id: string
|
||||
name: string
|
||||
enable: boolean
|
||||
}
|
||||
|
||||
interface RolePermissionItem {
|
||||
id: string,
|
||||
name: string,
|
||||
children: {
|
||||
id: string,
|
||||
name: string,
|
||||
permission: ChildrenPermissionItem[],
|
||||
enable: boolean,
|
||||
}[]
|
||||
}
|
||||
|
||||
interface RoleTableDataItem {
|
||||
module: string
|
||||
name: string
|
||||
permission: ChildrenPermissionItem[]
|
||||
enable: boolean
|
||||
perChecked: string[]
|
||||
indeterminate: boolean
|
||||
}
|
||||
|
||||
interface CreateOrUpdateParams {
|
||||
role_id?: string,
|
||||
role_name: string,
|
||||
role_type?: RoleTypeEnum,
|
||||
}
|
||||
|
||||
export type { RoleItem, RolePermissionItem, RoleTableDataItem, CreateOrUpdateParams, ChildrenPermissionItem }
|
||||
67
ui/src/api/user/role.ts
Normal file
67
ui/src/api/user/role.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { get, post, del } from '@/request/index'
|
||||
import type { Ref } from 'vue'
|
||||
import { Result } from '@/request/Result'
|
||||
import type { RoleItem, RolePermissionItem, CreateOrUpdateParams } from '@/api/type/role'
|
||||
import { RoleTypeEnum } from '@/enums/system'
|
||||
|
||||
const prefix = '/system/role'
|
||||
/**
|
||||
* 获取角色列表
|
||||
*/
|
||||
const getRoleList: (loading?: Ref<boolean>) => Promise<Result<{ internal_role: RoleItem[], custom_role: RoleItem[] }>> = (loading) => {
|
||||
return get(`${prefix}`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型获取角色权限模版列表
|
||||
*/
|
||||
const getRoleTemplate: (role_type: RoleTypeEnum, loading?: Ref<boolean>) => Promise<Result<RolePermissionItem[]>> = (role_type, loading) => {
|
||||
return get(`${prefix}/template/${role_type}`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色权限选中
|
||||
*/
|
||||
const getRolePermissionList: (role_id: string, loading?: Ref<boolean>) => Promise<Result<RolePermissionItem[]>> = (role_id, loading) => {
|
||||
return get(`${prefix}/${role_id}/permission`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建或更新角色
|
||||
*/
|
||||
const CreateOrUpdateRole: (
|
||||
data: CreateOrUpdateParams,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (data, loading) => {
|
||||
return post(`${prefix}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
*/
|
||||
const deleteRole: (role_id: string, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
role_id,
|
||||
loading,
|
||||
) => {
|
||||
return del(`${prefix}/${role_id}`, undefined, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存角色权限
|
||||
*/
|
||||
const saveRolePermission: (
|
||||
role_id: string,
|
||||
data: { id: string, enable: boolean }[],
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (role_id, data, loading) => {
|
||||
return post(`${prefix}/${role_id}/permission`, data, undefined, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getRoleList,
|
||||
getRolePermissionList,
|
||||
getRoleTemplate,
|
||||
CreateOrUpdateRole,
|
||||
deleteRole,
|
||||
saveRolePermission
|
||||
}
|
||||
@ -5,3 +5,9 @@ export enum AuthorizationEnum {
|
||||
KNOWLEDGE = 'KNOWLEDGE',
|
||||
APPLICATION = 'APPLICATION',
|
||||
}
|
||||
|
||||
export enum RoleTypeEnum {
|
||||
ADMIN = 'ADMIN',
|
||||
USER = 'USER',
|
||||
WORKSPACE_MANAGE = 'WORKSPACE_MANAGE',
|
||||
}
|
||||
@ -66,9 +66,11 @@ export default {
|
||||
addParam: 'Add Parameter',
|
||||
},
|
||||
inputPlaceholder: 'Please input',
|
||||
selectPlaceholder: 'Please select',
|
||||
title: 'Title',
|
||||
content: 'Content',
|
||||
rename: 'Rename',
|
||||
renameSuccess: 'Successful',
|
||||
EditAvatarDialog: {
|
||||
title: 'App Logo',
|
||||
customizeUpload: 'Custom Upload',
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import notFound from './404'
|
||||
import application from './application'
|
||||
import role from './role'
|
||||
import applicationOverview from './application-overview'
|
||||
import knowledge from './knowledge'
|
||||
import system from './system'
|
||||
@ -32,5 +33,6 @@ export default {
|
||||
problem,
|
||||
log,
|
||||
login,
|
||||
operateLog
|
||||
operateLog,
|
||||
role
|
||||
}
|
||||
|
||||
22
ui/src/locales/lang/en-US/views/role.ts
Normal file
22
ui/src/locales/lang/en-US/views/role.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
title: 'Role management',
|
||||
internalRole: 'System built-in roles',
|
||||
customRole: 'Custom roles',
|
||||
systemAdmin: 'System admin',
|
||||
workspaceAdmin: 'Workspace admin',
|
||||
user: 'Regular user',
|
||||
roleName: 'Role name',
|
||||
inheritingRole: 'Inherited role',
|
||||
delete: {
|
||||
confirmTitle: 'Confirm to delete role:',
|
||||
confirmMessage: 'After deletion, all members under this role will be removed. Please proceed with caution.',
|
||||
},
|
||||
permission: {
|
||||
title: 'Permission configuration',
|
||||
operationTarget: 'Operation target',
|
||||
moduleName: 'Module name'
|
||||
},
|
||||
member: {
|
||||
title: 'Members'
|
||||
}
|
||||
};
|
||||
@ -70,9 +70,11 @@ export default {
|
||||
addParam: '添加参数',
|
||||
},
|
||||
inputPlaceholder: '请输入',
|
||||
selectPlaceholder: '请选择',
|
||||
title: '标题',
|
||||
content: '内容',
|
||||
rename: '重命名',
|
||||
renameSuccess: '重命名成功',
|
||||
EditAvatarDialog: {
|
||||
title: '应用头像',
|
||||
customizeUpload: '自定义上传',
|
||||
|
||||
@ -6,6 +6,7 @@ import document from './document'
|
||||
import system from './system'
|
||||
import userManage from './user-manage'
|
||||
import resourceAuthorization from './resource-authorization'
|
||||
import role from './role'
|
||||
import application from './application'
|
||||
import problem from './problem'
|
||||
import applicationOverview from './application-overview'
|
||||
@ -24,6 +25,7 @@ export default {
|
||||
system,
|
||||
userManage,
|
||||
resourceAuthorization,
|
||||
role,
|
||||
application,
|
||||
problem,
|
||||
applicationOverview,
|
||||
|
||||
22
ui/src/locales/lang/zh-CN/views/role.ts
Normal file
22
ui/src/locales/lang/zh-CN/views/role.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
title: '角色管理',
|
||||
internalRole: '系统内置角色',
|
||||
customRole: '自定义角色',
|
||||
systemAdmin: '系统管理员',
|
||||
workspaceAdmin: '工作空间管理员',
|
||||
user: '普通用户',
|
||||
roleName: '角色名称',
|
||||
inheritingRole: '继承角色',
|
||||
delete: {
|
||||
confirmTitle: '是否删除角色:',
|
||||
confirmMessage: '删除后,该角色下的成员都会被移除,请谨慎操作。',
|
||||
},
|
||||
permission: {
|
||||
title: '权限配置',
|
||||
operationTarget: '操作对象',
|
||||
moduleName: '模块名称'
|
||||
},
|
||||
member: {
|
||||
title: '成员'
|
||||
}
|
||||
}
|
||||
@ -66,9 +66,11 @@ export default {
|
||||
addParam: '新增參數',
|
||||
},
|
||||
inputPlaceholder: '請輸入',
|
||||
selectPlaceholder: '請選擇',
|
||||
title: '標題',
|
||||
content: '内容',
|
||||
rename: '重命名',
|
||||
renameSuccess: '重命名成功',
|
||||
EditAvatarDialog: {
|
||||
title: '應用頭像',
|
||||
customizeUpload: '自訂上傳',
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import notFound from './404'
|
||||
import application from './application'
|
||||
import role from './role'
|
||||
import applicationOverview from './application-overview'
|
||||
import knowledge from './knowledge'
|
||||
import system from './system'
|
||||
@ -32,5 +33,6 @@ export default {
|
||||
problem,
|
||||
log,
|
||||
login,
|
||||
operateLog
|
||||
operateLog,
|
||||
role
|
||||
}
|
||||
|
||||
22
ui/src/locales/lang/zh-Hant/views/role.ts
Normal file
22
ui/src/locales/lang/zh-Hant/views/role.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
title: '角色管理',
|
||||
internalRole: '系統內置角色',
|
||||
customRole: '自定義角色',
|
||||
systemAdmin: '系統管理員',
|
||||
workspaceAdmin: '工作空間管理員',
|
||||
user: '普通用戶',
|
||||
roleName: '角色名稱',
|
||||
inheritingRole: '繼承角色',
|
||||
delete: {
|
||||
confirmTitle: '是否刪除角色:',
|
||||
confirmMessage: '刪除後,該角色下的成員都會被移除,請謹慎操作。',
|
||||
},
|
||||
permission: {
|
||||
title: '權限配置',
|
||||
operationTarget: '操作對象',
|
||||
moduleName: '模塊名稱'
|
||||
},
|
||||
member: {
|
||||
title: '成員'
|
||||
}
|
||||
};
|
||||
@ -32,6 +32,19 @@ const systemRouter = {
|
||||
},
|
||||
component: () => import('@/views/resource-authorization/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system/role',
|
||||
name: 'role',
|
||||
meta: {
|
||||
icon: 'app-resource-authorization', // TODO
|
||||
iconActive: 'app-resource-authorization-active', // TODO
|
||||
title: 'views.role.title',
|
||||
activeMenu: '/system',
|
||||
parentPath: '/system',
|
||||
parentName: 'system',
|
||||
},
|
||||
component: () => import('@/views/role/index.vue'),
|
||||
},
|
||||
{
|
||||
path:'/system/setting',
|
||||
name: 'setting',
|
||||
|
||||
@ -385,6 +385,9 @@ h5 {
|
||||
.color-success {
|
||||
color: var(--el-color-success);
|
||||
}
|
||||
.color-input-placeholder {
|
||||
color: var(--app-input-color-placeholder);
|
||||
}
|
||||
.avatar-purple {
|
||||
background: #7f3bf5;
|
||||
}
|
||||
|
||||
82
ui/src/views/role/component/CreateOrUpdateRoleDialog.vue
Normal file
82
ui/src/views/role/component/CreateOrUpdateRoleDialog.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<el-dialog :title="`${!form.role_id ? $t('common.create') : $t('common.rename')}${$t('views.role.customRole')}`"
|
||||
v-model="dialogVisible" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true">
|
||||
<el-form label-position="top" ref="formRef" :rules="rules" :model="form" require-asterisk-position="right">
|
||||
<el-form-item :label="$t('views.role.roleName')" prop="role_name">
|
||||
<el-input v-model="form.role_name" maxlength="64"
|
||||
:placeholder="`${$t('common.inputPlaceholder')}${$t('views.role.roleName')}`" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!form.role_id" :label="$t('views.role.inheritingRole')" prop="role_type">
|
||||
<el-select v-model="form.role_type"
|
||||
:placeholder="`${$t('common.selectPlaceholder')}${$t('views.role.inheritingRole')}`">
|
||||
<el-option v-for="(label, value) in roleTypeMap" :key="value" :label="label" :value="value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit(formRef)" :loading="loading">
|
||||
{{ !form.role_id ? $t('common.create') : $t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import type { RoleItem, CreateOrUpdateParams } from '@/api/type/role'
|
||||
import RoleApi from '@/api/user/role'
|
||||
import { roleTypeMap } from '../index'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'refresh', currentRole: RoleItem): void;
|
||||
}>();
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const defaultForm = {
|
||||
role_name: ''
|
||||
}
|
||||
const form = ref<CreateOrUpdateParams>({
|
||||
...defaultForm,
|
||||
})
|
||||
function open(item?: RoleItem) {
|
||||
if (item) {
|
||||
form.value = {
|
||||
role_name: item.role_name,
|
||||
role_type: item.type,
|
||||
role_id: item.id,
|
||||
}
|
||||
} else {
|
||||
form.value = { ...defaultForm }
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const rules = reactive({
|
||||
role_name: [{ required: true, message: `${t('common.inputPlaceholder')}${t('views.role.roleName')}`, trigger: 'blur' }],
|
||||
role_type: [{ required: true, message: `${t('common.selectPlaceholder')}${t('views.role.inheritingRole')}`, trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
RoleApi.CreateOrUpdateRole(form.value, loading).then((res: any) => {
|
||||
MsgSuccess(!form.value.role_id ? t('common.createSuccess') : t('common.renameSuccess'))
|
||||
emit('refresh', res.data)
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
5
ui/src/views/role/component/Member.vue
Normal file
5
ui/src/views/role/component/Member.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
173
ui/src/views/role/component/PermissionConfiguration.vue
Normal file
173
ui/src/views/role/component/PermissionConfiguration.vue
Normal file
@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<el-scrollbar v-loading="loading">
|
||||
<div class="p-24 pt-0">
|
||||
<el-table :data="tableData" border :span-method="objectSpanMethod">
|
||||
<el-table-column prop="module" :width="130" :label="$t('views.role.permission.moduleName')" />
|
||||
<el-table-column prop="name" :width="150" :label="$t('views.role.permission.operationTarget')" />
|
||||
<el-table-column prop="permission" :label="$t('views.model.modelForm.permissionType.label')">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox-group v-model="row.perChecked" @change="handleCellChange($event, row)">
|
||||
<el-checkbox v-for="item in row.permission" :key="item.id" :value="item.id" :disabled="disabled">
|
||||
<div class="ellipsis" style="width: 96px">{{ item.name }}</div>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :width="40">
|
||||
<template #header>
|
||||
<el-checkbox :model-value="allChecked" :indeterminate="allIndeterminate" :disabled="disabled"
|
||||
@change="handleCheckAll" />
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row.enable" :indeterminate="row.indeterminate" :disabled="disabled"
|
||||
@change="(value: boolean) => handleRowChange(value, row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div v-if="!disabled" class="footer border-t">
|
||||
<el-button type="primary" style="width: 80px;" :loading="loading" @click="handleSave">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import type { RoleItem, RolePermissionItem, RoleTableDataItem, ChildrenPermissionItem } from '@/api/type/role'
|
||||
import RoleApi from '@/api/user/role'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const props = defineProps<{
|
||||
currentRole?: RoleItem
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
const tableData = ref<RoleTableDataItem[]>([])
|
||||
const disabled = computed(() => props.currentRole?.internal) // TODO 权限
|
||||
|
||||
function transformData(data: RolePermissionItem[]) {
|
||||
const transformedData: RoleTableDataItem[] = []
|
||||
data.forEach(module => {
|
||||
module.children.forEach(feature => {
|
||||
const perChecked = feature.permission
|
||||
.filter(p => p.enable)
|
||||
.map(p => p.id)
|
||||
|
||||
transformedData.push({
|
||||
module: module.name,
|
||||
name: feature.name,
|
||||
permission: feature.permission,
|
||||
enable: feature.enable,
|
||||
perChecked,
|
||||
indeterminate: perChecked.length > 0 && perChecked.length < feature.permission.length
|
||||
})
|
||||
})
|
||||
})
|
||||
return transformedData;
|
||||
};
|
||||
|
||||
async function getRolePermission() {
|
||||
if (!props.currentRole?.id) return
|
||||
try {
|
||||
tableData.value = [];
|
||||
const res = await RoleApi.getRolePermissionList(props.currentRole.id, loading)
|
||||
tableData.value = transformData(res.data);
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
function handleCellChange(checkedValues: string[], row: RoleTableDataItem) {
|
||||
row.enable = checkedValues.length === row.permission.length
|
||||
row.indeterminate = checkedValues.length > 0 && checkedValues.length < row.permission.length
|
||||
|
||||
row.permission.forEach(p => {
|
||||
p.enable = checkedValues.includes(p.id)
|
||||
})
|
||||
}
|
||||
|
||||
function handleRowChange(checked: boolean, row: RoleTableDataItem) {
|
||||
if (checked) {
|
||||
row.perChecked = row.permission.map(p => p.id)
|
||||
row.permission.forEach(p => p.enable = true)
|
||||
} else {
|
||||
row.perChecked = []
|
||||
row.permission.forEach(p => p.enable = false)
|
||||
}
|
||||
row.indeterminate = false
|
||||
}
|
||||
|
||||
const allChecked = computed(() => {
|
||||
return tableData.value.length > 0 && tableData.value.every(item => item.enable)
|
||||
})
|
||||
|
||||
const allIndeterminate = computed(() => {
|
||||
return !allChecked.value && tableData.value.some(item => item.enable)
|
||||
})
|
||||
|
||||
function handleCheckAll(checked: boolean) {
|
||||
tableData.value.forEach(item => {
|
||||
item.enable = checked
|
||||
item.perChecked = checked ? item.permission.map(p => p.id) : []
|
||||
item.indeterminate = false
|
||||
item.permission.forEach(p => p.enable = checked)
|
||||
})
|
||||
}
|
||||
|
||||
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }: any) => {
|
||||
if (columnIndex === 0) {
|
||||
const sameModuleRows = tableData.value.filter(item => item.module === row.module)
|
||||
const firstRowIndex = tableData.value.findIndex(item => item.module === row.module)
|
||||
if (rowIndex === firstRowIndex) {
|
||||
return {
|
||||
rowspan: sameModuleRows.length,
|
||||
colspan: 1
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
rowspan: 0,
|
||||
colspan: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.currentRole?.id, getRolePermission, { immediate: true })
|
||||
|
||||
async function handleSave() {
|
||||
try {
|
||||
const permissions: { id: string, enable: boolean }[] = [];
|
||||
tableData.value.forEach((e) => {
|
||||
e.permission?.forEach((ele: ChildrenPermissionItem) => {
|
||||
permissions.push({
|
||||
id: ele.id,
|
||||
enable: ele.enable,
|
||||
});
|
||||
});
|
||||
});
|
||||
await RoleApi.saveRolePermission(props.currentRole?.id as string, permissions, loading);
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-checkbox-group) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 24px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
8
ui/src/views/role/index.ts
Normal file
8
ui/src/views/role/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { RoleTypeEnum } from '@/enums/system'
|
||||
import { t } from '@/locales'
|
||||
|
||||
export const roleTypeMap: Record<RoleTypeEnum, string> = {
|
||||
[RoleTypeEnum.ADMIN]: t('views.role.systemAdmin'),
|
||||
[RoleTypeEnum.USER]: t('views.role.user'),
|
||||
[RoleTypeEnum.WORKSPACE_MANAGE]: t('views.role.workspaceAdmin')
|
||||
}
|
||||
266
ui/src/views/role/index.vue
Normal file
266
ui/src/views/role/index.vue
Normal file
@ -0,0 +1,266 @@
|
||||
<template>
|
||||
<div class="role">
|
||||
<h2 class="mb-16">{{ $t('views.role.title') }}</h2>
|
||||
<el-card style="--el-card-padding: 0" body-class="role-card">
|
||||
<div class="flex h-full">
|
||||
<div class="role-left border-r p-16">
|
||||
<div class="p-8 pb-0">
|
||||
<el-input v-model="filterText" :placeholder="$t('common.search')" prefix-icon="Search" clearable />
|
||||
</div>
|
||||
<div class="list-height-left mt-8">
|
||||
<el-scrollbar v-loading="loading">
|
||||
<div class="role-left_title color-secondary lighter">
|
||||
<span>{{ $t('views.role.internalRole') }}</span>
|
||||
</div>
|
||||
<common-list :data="filterInternalRole" @click="clickRole" :default-active="currentRole?.id">
|
||||
<template #default="{ row }">
|
||||
<div class="flex-between">
|
||||
<span class="mr-8">{{ row.role_name }}</span>
|
||||
<el-dropdown :teleported="false">
|
||||
<el-button text>
|
||||
<el-icon class="color-secondary">
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu style="min-width: 80px">
|
||||
<el-dropdown-item @click.stop="createOrUpdateRole(row)" class="p-8">
|
||||
{{ $t('common.rename') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="deleteRole(row)" class="border-t p-8">
|
||||
{{ $t('common.delete') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<template #empty>
|
||||
<span></span>
|
||||
</template>
|
||||
</common-list>
|
||||
<div class="role-left_divider">
|
||||
<el-divider />
|
||||
</div>
|
||||
<div class="role-left_title">
|
||||
<span class="color-secondary lighter">{{ $t('views.role.customRole') }}</span>
|
||||
<AppIcon iconName="app-wordspace" style="font-size: 16px" class="cursor color-primary"
|
||||
@click="createOrUpdateRole()">
|
||||
</AppIcon>
|
||||
</div>
|
||||
<common-list :data="filterCustomRole" @click="clickRole" :default-active="currentRole?.id">
|
||||
<template #default="{ row }">
|
||||
<div class="flex-between">
|
||||
<span>
|
||||
{{ row.role_name }}
|
||||
<span class="color-input-placeholder ml-4">({{ roleTypeMap[row.type as RoleTypeEnum] }})</span>
|
||||
</span>
|
||||
<el-dropdown :teleported="false">
|
||||
<el-button text>
|
||||
<el-icon class="color-secondary">
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu style="min-width: 80px">
|
||||
<el-dropdown-item @click.stop="createOrUpdateRole(row)" class="p-8"> {{ $t('common.rename') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="deleteRole(row)" class="border-t p-8"> {{ $t('common.delete')
|
||||
}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<template #empty>
|
||||
<span></span>
|
||||
</template>
|
||||
</common-list>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右边 -->
|
||||
<div class="role-right">
|
||||
<div class="flex-between mb-16 p-24 pb-0">
|
||||
<div class="flex align-center">
|
||||
<span>
|
||||
{{ currentRole?.role_name }}
|
||||
<span v-if="currentRole?.type" class="color-input-placeholder ml-4">({{ roleTypeMap[currentRole?.type as
|
||||
RoleTypeEnum] }})
|
||||
</span>
|
||||
</span>
|
||||
<el-divider direction="vertical" class="mr-8 ml-8" />
|
||||
<AppIcon iconName="app-wordspace" style="font-size: 16px" class="color-input-placeholder"></AppIcon>
|
||||
<span class="color-input-placeholder ml-4">
|
||||
数字
|
||||
</span>
|
||||
</div>
|
||||
<el-radio-group v-model="currentTab">
|
||||
<el-radio-button v-for="item in tabList" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<PermissionConfiguration v-if="currentTab === 'permission'" :currentRole="currentRole" />
|
||||
<Member v-else :currentRole="currentRole" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<CreateOrUpdateRoleDialog ref="createOrUpdateRoleDialogRef" @refresh="refresh" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import RoleApi from '@/api/user/role'
|
||||
import { t } from '@/locales'
|
||||
import PermissionConfiguration from './component/PermissionConfiguration.vue'
|
||||
import Member from './component/Member.vue'
|
||||
import CreateOrUpdateRoleDialog from './component/CreateOrUpdateRoleDialog.vue'
|
||||
import type { RoleItem } from '@/api/type/role'
|
||||
import { RoleTypeEnum } from '@/enums/system'
|
||||
import { roleTypeMap } from './index'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
|
||||
const filterText = ref('')
|
||||
const loading = ref(false)
|
||||
const internalRoleList = ref<RoleItem[]>([])
|
||||
const filterInternalRole = ref<RoleItem[]>([]) // 搜索过滤后列表
|
||||
const customRoleList = ref<RoleItem[]>([])
|
||||
const filterCustomRole = ref<RoleItem[]>([]) // 搜索过滤后列表
|
||||
const currentRole = ref<RoleItem>()
|
||||
|
||||
async function getRole() {
|
||||
try {
|
||||
const res = await RoleApi.getRoleList(loading)
|
||||
internalRoleList.value = res.data.internal_role
|
||||
customRoleList.value = res.data.custom_role
|
||||
filterInternalRole.value = filter(internalRoleList.value, filterText.value)
|
||||
filterCustomRole.value = filter(customRoleList.value, filterText.value)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getRole()
|
||||
currentRole.value = internalRoleList.value[0]
|
||||
})
|
||||
|
||||
async function refresh(role?: RoleItem) {
|
||||
await getRole();
|
||||
// 创建角色后选中新建的角色
|
||||
currentRole.value = role ? role : currentRole.value
|
||||
}
|
||||
|
||||
function filter(list: RoleItem[], filterText: string) {
|
||||
if (!filterText.length) {
|
||||
return list
|
||||
}
|
||||
return list.filter((v: RoleItem) =>
|
||||
v.role_name.toLowerCase().includes(filterText.toLowerCase()),
|
||||
)
|
||||
}
|
||||
|
||||
watch(filterText, (val: string) => {
|
||||
filterInternalRole.value = filter(internalRoleList.value, val)
|
||||
filterCustomRole.value = filter(customRoleList.value, val)
|
||||
})
|
||||
|
||||
function clickRole(item: RoleItem) {
|
||||
currentRole.value = item
|
||||
}
|
||||
|
||||
const createOrUpdateRoleDialogRef = ref<InstanceType<typeof CreateOrUpdateRoleDialog>>()
|
||||
function createOrUpdateRole(item?: RoleItem) {
|
||||
createOrUpdateRoleDialogRef.value?.open(item);
|
||||
}
|
||||
|
||||
function deleteRole(item: RoleItem) {
|
||||
MsgConfirm(
|
||||
`${t('views.role.delete.confirmTitle')}${item.role_name} ?`,
|
||||
t('views.role.delete.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
RoleApi.deleteRole(item.id, loading).then(async () => {
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
await getRole()
|
||||
currentRole.value = item.id === currentRole.value?.id ? internalRoleList.value[0] : currentRole.value
|
||||
})
|
||||
})
|
||||
.catch(() => { })
|
||||
}
|
||||
|
||||
|
||||
const currentTab = ref('permission')
|
||||
const tabList = [
|
||||
{
|
||||
value: 'permission',
|
||||
label: t('views.role.permission.title'),
|
||||
},
|
||||
{
|
||||
value: 'member',
|
||||
label: t('views.role.member.title'),
|
||||
},
|
||||
]
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.role {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 16px 24px;
|
||||
|
||||
:deep(.role-card) {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.role-left {
|
||||
box-sizing: border-box;
|
||||
width: var(--setting-left-width);
|
||||
min-width: var(--setting-left-width);
|
||||
|
||||
.role-left_title {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.list-height-left {
|
||||
height: calc(100vh - 213px);
|
||||
|
||||
:deep(.common-list li) {
|
||||
padding-right: 4px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.role-left_divider {
|
||||
padding: 0 8px;
|
||||
|
||||
:deep(.el-divider) {
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.role-right {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user