feat: system

This commit is contained in:
wangdan-fit2cloud 2025-06-17 16:11:07 +08:00
parent 3aa804f666
commit bdedf49a71
9 changed files with 134 additions and 60 deletions

View File

@ -12,13 +12,12 @@ Object.defineProperty(prefix, 'value', {
}, },
}) })
/** /**
* *
* @query * @query
*/ */
const getUserList: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => { const getUserList: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => {
return get(`${prefix}/user_list`, undefined, loading) return get(`${prefix.value}/user_list`, undefined, loading)
} }
/** /**
@ -29,7 +28,7 @@ const getResourceAuthorization: (
user_id: string, user_id: string,
loading?: Ref<boolean>, loading?: Ref<boolean>,
) => Promise<Result<any>> = (user_id, loading) => { ) => Promise<Result<any>> = (user_id, loading) => {
return get(`${prefix}/user_resource_permission/user/${user_id}`, undefined, loading) return get(`${prefix.value}/user_resource_permission/user/${user_id}`, undefined, loading)
} }
/** /**
@ -55,10 +54,10 @@ const putResourceAuthorization: (
body: any, body: any,
loading?: Ref<boolean>, loading?: Ref<boolean>,
) => Promise<Result<any>> = (user_id, body, loading) => { ) => Promise<Result<any>> = (user_id, body, loading) => {
return put(`${prefix}/user_resource_permission/user/${user_id}`, body, loading) return put(`${prefix.value}/user_resource_permission/user/${user_id}`, body, loading)
} }
const getUserMember: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => { const getUserMember: (loading?: Ref<boolean>) => Promise<Result<any>> = (loading) => {
return get(`${prefix}/user_member`, undefined, loading) return get(`${prefix.value}/user_member`, undefined, loading)
} }
export default { export default {
getResourceAuthorization, getResourceAuthorization,

View File

@ -0,0 +1,30 @@
import { get, post, del } from '@/request/index'
import type { Ref } from 'vue'
import { Result } from '@/request/Result'
import type { RoleItem, RolePermissionItem, CreateOrUpdateParams, RoleMemberItem, CreateMemberParamsItem } from '@/api/type/role'
import { RoleTypeEnum } from '@/enums/system'
import type { pageRequest, PageList } from '@/api/type/common'
const prefix = '/workspace/role'
/**
*
*/
const getRoleList: (loading?: Ref<boolean>) => Promise<Result<{ internal_role: RoleItem[], custom_role: RoleItem[] }>> = (loading) => {
return get(`${prefix}`, undefined, loading)
}
/**
*
*/
const CreateMember: (
role_id: string,
data: { members: CreateMemberParamsItem[] },
loading?: Ref<boolean>,
) => Promise<Result<any>> = (role_id, data, loading) => {
return post(`${prefix}/${role_id}/add_member`, data, undefined, loading)
}
export default {
getRoleList,
CreateMember,
}

View File

@ -166,4 +166,3 @@ export const iconMap: any = {
// 动态加载的图标 // 动态加载的图标
...dynamicIcons, ...dynamicIcons,
} }
console.log(iconMap);

View File

@ -1,4 +1,4 @@
import { Role, ComplexPermission } from '@/utils/permission/type' import { PermissionConst, EditionConst, RoleConst } from '@/utils/permission/data'
const systemRouter = { const systemRouter = {
path: '/system', path: '/system',
name: 'system', name: 'system',
@ -42,6 +42,7 @@ const systemRouter = {
activeMenu: '/system', activeMenu: '/system',
parentPath: '/system', parentPath: '/system',
parentName: 'system', parentName: 'system',
permission: [EditionConst.IS_EE],
}, },
component: () => import('@/views/role/index.vue'), component: () => import('@/views/role/index.vue'),
}, },
@ -55,6 +56,7 @@ const systemRouter = {
activeMenu: '/system', activeMenu: '/system',
parentPath: '/system', parentPath: '/system',
parentName: 'system', parentName: 'system',
permission: [EditionConst.IS_EE],
}, },
component: () => import('@/views/workspace/index.vue'), component: () => import('@/views/workspace/index.vue'),
}, },
@ -68,6 +70,7 @@ const systemRouter = {
activeMenu: '/system', activeMenu: '/system',
parentPath: '/system', parentPath: '/system',
parentName: 'system', parentName: 'system',
permission: [EditionConst.IS_EE],
}, },
children: [ children: [
{ {
@ -115,6 +118,7 @@ const systemRouter = {
activeMenu: '/system', activeMenu: '/system',
parentPath: '/system', parentPath: '/system',
parentName: 'system', parentName: 'system',
permission: [EditionConst.IS_PE, EditionConst.IS_EE],
}, },
children: [ children: [
{ {
@ -172,7 +176,7 @@ const systemRouter = {
activeMenu: '/system', activeMenu: '/system',
parentPath: '/system', parentPath: '/system',
parentName: 'system', parentName: 'system',
//permission: new ComplexPermission(['ADMIN'], ['x-pack'], 'AND') permission: [EditionConst.IS_PE, EditionConst.IS_EE],
}, },
component: () => import('@/views/theme/index.vue'), component: () => import('@/views/theme/index.vue'),
}, },
@ -184,7 +188,7 @@ const systemRouter = {
activeMenu: '/system', activeMenu: '/system',
parentPath: '/system', parentPath: '/system',
parentName: 'system', parentName: 'system',
//permission: new ComplexPermission(['ADMIN'], ['x-pack'], 'AND') permission: [EditionConst.IS_PE, EditionConst.IS_EE],
}, },
component: () => import('@/views/authentication/index.vue'), component: () => import('@/views/authentication/index.vue'),
}, },

View File

@ -0,0 +1,28 @@
import { PermissionConst, EditionConst, RoleConst } from '@/utils/permission/data'
import { hasPermission } from '@/utils/permission/index'
import roleSystemApi from '@/api/system/role'
import roleWorkspaceApi from '@/api/workspace/role'
// 系统管理员 API
const systemApiMap = {
role: roleSystemApi,
} as any
// 企业版工作空间管理员 API
const workspaceApiMap = {
role: roleWorkspaceApi,
} as any
/** API
* loadPermissionApi('role')
*/
export function loadPermissionApi(type: string) {
if (hasPermission([EditionConst.IS_EE, RoleConst.WORKSPACE_MANAGE.getWorkspaceRole], 'AND')) {
// 加载企业版工作空间管理员 API
return workspaceApiMap[type]
} else {
// 加载系统管理员 API
return systemApiMap[type]
}
}

View File

@ -22,13 +22,13 @@
</el-avatar> </el-avatar>
<LogoIcon <LogoIcon
v-else-if="isApplication" v-if="isApplication"
height="28px" height="28px"
style="width: 28px; height: 28px; display: block" style="width: 28px; height: 28px; display: block"
class="mr-12" class="mr-12"
/> />
<el-avatar <el-avatar
v-if="row.isFolder" v-else-if="row.isFolder"
class="mr-12" class="mr-12"
shape="square" shape="square"
:size="20" :size="20"

View File

@ -33,6 +33,7 @@ import { t } from '@/locales'
import type { RoleItem } from '@/api/type/role' import type { RoleItem } from '@/api/type/role'
import { MsgSuccess } from '@/utils/message' import { MsgSuccess } from '@/utils/message'
import { RoleTypeEnum } from '@/enums/system' import { RoleTypeEnum } from '@/enums/system'
import { loadPermissionApi } from '@/utils/permission-api'
const props = defineProps<{ const props = defineProps<{
currentRole?: RoleItem currentRole?: RoleItem
@ -140,7 +141,7 @@ function handleAdd() {
if (props.currentRole?.type === RoleTypeEnum.ADMIN) { if (props.currentRole?.type === RoleTypeEnum.ADMIN) {
params = list.value.map((item) => ({ user_ids: item.user_ids, workspace_ids: ['None'] })) params = list.value.map((item) => ({ user_ids: item.user_ids, workspace_ids: ['None'] }))
} }
await RoleApi.CreateMember( await loadPermissionApi('role').CreateMember(
props.currentRole?.id as string, props.currentRole?.id as string,
{ members: params ?? list.value }, { members: params ?? list.value },
loading, loading,

View File

@ -5,23 +5,30 @@
<div class="flex h-full"> <div class="flex h-full">
<div class="role-left border-r p-16"> <div class="role-left border-r p-16">
<div class="p-8 pb-0"> <div class="p-8 pb-0">
<el-input v-model="filterText" :placeholder="$t('common.search')" prefix-icon="Search" <el-input
clearable/> v-model="filterText"
:placeholder="$t('common.search')"
prefix-icon="Search"
clearable
/>
</div> </div>
<div class="list-height-left mt-8"> <div class="list-height-left mt-8">
<el-scrollbar v-loading="loading"> <el-scrollbar v-loading="loading">
<div class="role-left_title color-secondary lighter"> <div class="role-left_title color-secondary lighter">
<span>{{ $t('views.role.internalRole') }}</span> <span>{{ $t('views.role.internalRole') }}</span>
</div> </div>
<common-list :data="filterInternalRole" @click="clickRole" <common-list
:default-active="currentRole?.id"> :data="filterInternalRole"
@click="clickRole"
:default-active="currentRole?.id"
>
<template #default="{ row }"> <template #default="{ row }">
<div class="flex-between"> <div class="flex-between">
<span class="mr-8">{{ row.role_name }}</span> <span class="mr-8">{{ row.role_name }}</span>
<el-dropdown :teleported="false"> <el-dropdown :teleported="false">
<el-button text> <el-button text>
<el-icon class="color-secondary"> <el-icon class="color-secondary">
<MoreFilled/> <MoreFilled />
</el-icon> </el-icon>
</el-button> </el-button>
<template #dropdown> <template #dropdown>
@ -44,32 +51,37 @@
</template> </template>
</common-list> </common-list>
<div class="role-left_divider"> <div class="role-left_divider">
<el-divider/> <el-divider />
</div> </div>
<div class="role-left_title"> <div class="role-left_title">
<span class="color-secondary lighter">{{ $t('views.role.customRole') }}</span> <span class="color-secondary lighter">{{ $t('views.role.customRole') }}</span>
<el-tooltip effect="dark" <el-tooltip
effect="dark"
:content="`${$t('common.create')}${$t('views.role.customRole')}`" :content="`${$t('common.create')}${$t('views.role.customRole')}`"
placement="top"> placement="top"
>
<el-button type="primary" text @click="createOrUpdateRole()"> <el-button type="primary" text @click="createOrUpdateRole()">
<AppIcon iconName="app-copy"></AppIcon> <AppIcon iconName="app-copy"></AppIcon>
</el-button> </el-button>
</el-tooltip> </el-tooltip>
</div> </div>
<common-list :data="filterCustomRole" @click="clickRole" <common-list
:default-active="currentRole?.id"> :data="filterCustomRole"
@click="clickRole"
:default-active="currentRole?.id"
>
<template #default="{ row }"> <template #default="{ row }">
<div class="flex-between"> <div class="flex-between">
<span> <span>
{{ row.role_name }} {{ row.role_name }}
<span class="color-input-placeholder ml-4">({{ <span class="color-input-placeholder ml-4"
roleTypeMap[row.type as RoleTypeEnum] >({{ roleTypeMap[row.type as RoleTypeEnum] }})</span
}})</span> >
</span> </span>
<el-dropdown :teleported="false"> <el-dropdown :teleported="false">
<el-button text> <el-button text>
<el-icon class="color-secondary"> <el-icon class="color-secondary">
<MoreFilled/> <MoreFilled />
</el-icon> </el-icon>
</el-button> </el-button>
<template #dropdown> <template #dropdown>
@ -101,46 +113,53 @@
<div class="flex align-center"> <div class="flex align-center">
<span> <span>
{{ currentRole?.role_name }} {{ currentRole?.role_name }}
<span v-if="currentRole?.type && !currentRole.internal" <span
class="color-input-placeholder ml-4">({{ v-if="currentRole?.type && !currentRole.internal"
roleTypeMap[currentRole?.type as class="color-input-placeholder ml-4"
RoleTypeEnum] >({{ roleTypeMap[currentRole?.type as RoleTypeEnum] }})
}})
</span> </span>
</span> </span>
<el-divider direction="vertical" class="mr-8 ml-8"/> <el-divider direction="vertical" class="mr-8 ml-8" />
<AppIcon iconName="app-wordspace" style="font-size: 16px" <AppIcon
class="color-input-placeholder"></AppIcon> iconName="app-wordspace"
style="font-size: 16px"
class="color-input-placeholder"
></AppIcon>
<span class="color-input-placeholder ml-4"> <span class="color-input-placeholder ml-4">
{{ currentRole?.user_count }} {{ currentRole?.user_count }}
</span> </span>
</div> </div>
<el-radio-group v-model="currentTab"> <el-radio-group v-model="currentTab">
<el-radio-button v-for="item in tabList" :key="item.value" :label="item.label" <el-radio-button
:value="item.value"/> v-for="item in tabList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-radio-group> </el-radio-group>
</div> </div>
<PermissionConfiguration v-if="currentTab === 'permission'" :currentRole="currentRole"/> <PermissionConfiguration v-if="currentTab === 'permission'" :currentRole="currentRole" />
<Member v-else :currentRole="currentRole"/> <Member v-else :currentRole="currentRole" />
</div> </div>
</div> </div>
</el-card> </el-card>
<CreateOrUpdateRoleDialog ref="createOrUpdateRoleDialogRef" @refresh="refresh"/> <CreateOrUpdateRoleDialog ref="createOrUpdateRoleDialogRef" @refresh="refresh" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {onMounted, ref, watch} from 'vue' import { onMounted, ref, watch } from 'vue'
import RoleApi from '@/api/system/role' import RoleApi from '@/api/system/role'
import {t} from '@/locales' import { t } from '@/locales'
import PermissionConfiguration from './component/PermissionConfiguration.vue' import PermissionConfiguration from './component/PermissionConfiguration.vue'
import Member from './component/Member.vue' import Member from './component/Member.vue'
import CreateOrUpdateRoleDialog from './component/CreateOrUpdateRoleDialog.vue' import CreateOrUpdateRoleDialog from './component/CreateOrUpdateRoleDialog.vue'
import type {RoleItem} from '@/api/type/role' import type { RoleItem } from '@/api/type/role'
import {RoleTypeEnum} from '@/enums/system' import { RoleTypeEnum } from '@/enums/system'
import {roleTypeMap} from './index' import { roleTypeMap } from './index'
import {MsgSuccess, MsgConfirm} from '@/utils/message' import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { loadPermissionApi } from '@/utils/permission-api'
const filterText = ref('') const filterText = ref('')
const loading = ref(false) const loading = ref(false)
@ -152,7 +171,7 @@ const currentRole = ref<RoleItem>()
async function getRole() { async function getRole() {
try { try {
const res = await RoleApi.getRoleList(loading) const res = await loadPermissionApi('role').getRoleList(loading)
internalRoleList.value = res.data.internal_role internalRoleList.value = res.data.internal_role
customRoleList.value = res.data.custom_role customRoleList.value = res.data.custom_role
filterInternalRole.value = filter(internalRoleList.value, filterText.value) filterInternalRole.value = filter(internalRoleList.value, filterText.value)
@ -168,7 +187,7 @@ onMounted(async () => {
}) })
async function refresh(role?: RoleItem) { async function refresh(role?: RoleItem) {
await getRole(); await getRole()
// //
currentRole.value = role ? role : currentRole.value currentRole.value = role ? role : currentRole.value
} }
@ -177,9 +196,7 @@ function filter(list: RoleItem[], filterText: string) {
if (!filterText.length) { if (!filterText.length) {
return list return list
} }
return list.filter((v: RoleItem) => return list.filter((v: RoleItem) => v.role_name.toLowerCase().includes(filterText.toLowerCase()))
v.role_name.toLowerCase().includes(filterText.toLowerCase()),
)
} }
watch(filterText, (val: string) => { watch(filterText, (val: string) => {
@ -194,7 +211,7 @@ function clickRole(item: RoleItem) {
const createOrUpdateRoleDialogRef = ref<InstanceType<typeof CreateOrUpdateRoleDialog>>() const createOrUpdateRoleDialogRef = ref<InstanceType<typeof CreateOrUpdateRoleDialog>>()
function createOrUpdateRole(item?: RoleItem) { function createOrUpdateRole(item?: RoleItem) {
createOrUpdateRoleDialogRef.value?.open(item); createOrUpdateRoleDialogRef.value?.open(item)
} }
function deleteRole(item: RoleItem) { function deleteRole(item: RoleItem) {
@ -210,14 +227,13 @@ function deleteRole(item: RoleItem) {
RoleApi.deleteRole(item.id, loading).then(async () => { RoleApi.deleteRole(item.id, loading).then(async () => {
MsgSuccess(t('common.deleteSuccess')) MsgSuccess(t('common.deleteSuccess'))
await getRole() await getRole()
currentRole.value = item.id === currentRole.value?.id ? internalRoleList.value[0] : currentRole.value currentRole.value =
item.id === currentRole.value?.id ? internalRoleList.value[0] : currentRole.value
}) })
}) })
.catch(() => { .catch(() => {})
})
} }
const currentTab = ref('permission') const currentTab = ref('permission')
const tabList = [ const tabList = [
{ {
@ -229,7 +245,6 @@ const tabList = [
label: t('views.role.member.title'), label: t('views.role.member.title'),
}, },
] ]
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -273,8 +288,6 @@ const tabList = [
margin: 4px 0; margin: 4px 0;
} }
} }
} }
.role-right { .role-right {

View File

@ -17,7 +17,7 @@ export default defineConfig(({ mode }) => {
const prefix = process.env.VITE_DYNAMIC_PREFIX || ENV.VITE_BASE_PATH const prefix = process.env.VITE_DYNAMIC_PREFIX || ENV.VITE_BASE_PATH
const proxyConf: Record<string, string | ProxyOptions> = {} const proxyConf: Record<string, string | ProxyOptions> = {}
proxyConf['/api'] = { proxyConf['/api'] = {
target: 'http://127.0.0.1:8080', target: 'http://43.166.1.146:8080',
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'), rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'),
} }