feat: Folder authorization frontend

v3.2
zhangzhanwei 2025-10-14 16:38:49 +08:00 committed by zhanweizhang7
parent 89749a3006
commit 94560b8394
19 changed files with 191 additions and 18 deletions

View File

@ -12,12 +12,11 @@ const getResourceAuthorization: (
workspace_id: string,
user_id: string,
resource: string,
page: pageRequest,
params?: any,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (workspace_id, user_id, resource, page, params, loading) => {
) => Promise<Result<any>> = (workspace_id, user_id, resource, params, loading) => {
return get(
`${prefix}/${workspace_id}/user_resource_permission/user/${user_id}/resource/${resource}/${page.current_page}/${page.page_size}`,
`${prefix}/${workspace_id}/user_resource_permission/user/${user_id}/resource/${resource}`,
params,
loading,
)

View File

@ -48,7 +48,7 @@
</div>
<div
v-if="canOperation"
v-if="canOperation && permissionPrecise.folderManage(data.id)"
@click.stop
v-show="hoverNodeId === data.id"
@mouseenter.stop="handleMouseEnter(data)"
@ -56,30 +56,37 @@
class="mr-16"
>
<el-dropdown trigger="click" :teleported="false">
<el-button text class="w-full" v-if="MoreFilledPermission(node)">
<el-button text class="w-full" v-if="permissionPrecise.folderManage(data.id)">
<AppIcon iconName="app-more"></AppIcon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@click.stop="openCreateFolder(data)"
v-if="node.level !== 3 && permissionPrecise.folderCreate()"
v-if="node.level !== 3 && permissionPrecise.folderManage(data.id)"
>
<AppIcon iconName="app-add-folder" class="color-secondary"></AppIcon>
{{ $t('components.folder.addChildFolder') }}
</el-dropdown-item>
<el-dropdown-item
@click.stop="openEditFolder(data)"
v-if="permissionPrecise.folderEdit()"
v-if="permissionPrecise.folderManage(data.id)"
>
<AppIcon iconName="app-edit" class="color-secondary"></AppIcon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item
@click.stop="openAuthorization(data)"
v-if="permissionPrecise.folderManage(data.id)"
>
<AppIcon iconName="app-resource-authorization" class="color-secondary"></AppIcon>
{{ $t('views.system.resourceAuthorization.title') }}
</el-dropdown-item>
<el-dropdown-item
divided
@click.stop="deleteFolder(data)"
:disabled="!data.parent_id"
v-if="permissionPrecise.folderDelete()"
v-if="permissionPrecise.folderManage(data.id)"
>
<AppIcon iconName="app-delete" class="color-secondary"></AppIcon>
{{ $t('common.delete') }}
@ -94,6 +101,12 @@
</el-scrollbar>
</div>
<CreateFolderDialog ref="CreateFolderDialogRef" @refresh="refreshFolder" :title="title" />
<ResourceAuthorizationDrawer
:type="props.source"
:is-folder="true"
:is-root-folder="!currentNode?.parent_id"
ref="ResourceAuthorizationDrawerRef"
/>
</div>
</template>
@ -102,6 +115,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
import type { TreeInstance } from 'element-plus'
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
import ResourceAuthorizationDrawer from '@/components/resource-authorization-drawer/index.vue'
import { t } from '@/locales'
import { i18n_name } from '@/utils/common'
import folderApi from '@/api/folder'
@ -255,6 +269,13 @@ function openEditFolder(row: Tree) {
CreateFolderDialogRef.value.open(props.source, row.id, row)
}
const currentNode = ref<Tree | null>(null)
const ResourceAuthorizationDrawerRef = ref()
function openAuthorization(data: any) {
currentNode.value = data
ResourceAuthorizationDrawerRef.value.open(data.id)
}
function refreshFolder() {
emit('refreshTree')
}

View File

@ -112,7 +112,7 @@
v-model="row.permission"
@change="(val: any) => permissionsHandle(val, row)"
>
<template v-for="(item, index) in permissionOptions" :key="index">
<template v-for="(item, index) in getFolderPermissionOptions()" :key="index">
<el-radio :value="item.value" class="mr-16">{{ item.label }}</el-radio>
</template>
</el-radio-group>
@ -157,6 +157,8 @@ import useStore from '@/stores'
const { user } = useStore()
const props = defineProps<{
type: string
isFolder?: boolean
isRootFolder?: boolean
}>()
const apiType = computed(() => {
@ -166,6 +168,24 @@ const apiType = computed(() => {
return 'workspace'
}
})
const permissionOptionMap = computed(() => {
return {
rootFolder: getPermissionOptions(true, true),
folder: getPermissionOptions(true, false),
}
})
const getFolderPermissionOptions = () => {
if (props.isRootFolder) {
return permissionOptionMap.value.rootFolder
}
if (props.isFolder) {
return permissionOptionMap.value.folder
}
return getPermissionOptions(false, false)
}
const permissionOptions = computed(() => {
return getPermissionOptions()
})
@ -281,7 +301,12 @@ const getPermissionList = () => {
loading,
)
.then((res: any) => {
permissionData.value = res.data.records || []
permissionData.value = res.data.records.map((item: any) => {
if (props.isRootFolder && item.permission === 'NOT_AUTH') {
return {...item, permission: 'VIEW'}
}
return item
}) || []
paginationConfig.total = res.data.total || 0
})
}

View File

@ -13,6 +13,8 @@ const systemManage = {
'OR'
),
folderEdit: () => false,
folderRead: () => false,
folderManage: () => false,
export: () =>
hasPermission(
[

View File

@ -23,6 +23,26 @@ const workspace = {
],
'OR'
),
folderRead: (folder_id: string) =>
hasPermission(
[
new ComplexPermission([RoleConst.USER],[PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(folder_id)],[],'AND'),
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
PermissionConst.APPLICATION_FOLDER_READ.getApplicationWorkspaceResourcePermission(folder_id),
PermissionConst.APPLICATION_FOLDER_READ.getWorkspacePermissionWorkspaceManageRole,
],
'OR'
),
folderManage: (folder_id: string) =>
hasPermission(
[
new ComplexPermission([RoleConst.USER],[PermissionConst.APPLICATION.getApplicationWorkspaceResourcePermission(folder_id)],[],'AND'),
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
PermissionConst.APPLICATION_FOLDER_EDIT.getApplicationWorkspaceResourcePermission(folder_id),
PermissionConst.APPLICATION_EDIT.getWorkspacePermissionWorkspaceManageRole,
],
'OR'
),
edit: (source_id:string) =>
hasPermission(
[

View File

@ -159,6 +159,8 @@ const systemManage = {
PermissionConst.RESOURCE_KNOWLEDGE_AUTH
],'OR'
),
folderRead: () => false,
folderManage: () => false,
folderCreate: () => false,
folderEdit: () => false,
folderDelete: () => false,

View File

@ -185,6 +185,8 @@ const share = {
chat_user_edit: () =>false,
auth: () => false,
folderRead: () => false,
folderManage: () => false,
folderCreate: () => false,
folderEdit: () => false,
folderDelete: () => false,

View File

@ -33,6 +33,8 @@ const workspaceShare = {
problem_edit: () => false,
chat_user_edit: () =>false,
folderRead: () => false,
folderManage: () => false,
folderCreate: () => false,
folderEdit: () => false,
folderDelete: () => false,

View File

@ -20,6 +20,8 @@ const workspace = {
],
'OR',
),
folderRead: () => true,
folderManage: () => true,
folderCreate: () =>
hasPermission(
[

View File

@ -21,7 +21,10 @@ const systemManage = {
hasPermission([RoleConst.ADMIN, PermissionConst.RESOURCE_MODEL_DELETE], 'OR'),
auth: () =>
hasPermission([RoleConst.ADMIN, PermissionConst.RESOURCE_MODEL_AUTH], 'OR'),
hasPermission([RoleConst.ADMIN, PermissionConst.RESOURCE_MODEL_AUTH], 'OR'),
folderRead: () => false,
folderManage: () => false,
folderCreate: () => false,
folderEdit: () => false,
folderDelete: () => false,

View File

@ -36,6 +36,8 @@ const share = {
'OR',
),
auth: () => false,
folderRead: () => false,
folderManage: () => false,
folderCreate: () => false,
folderEdit: () => false,
folderDelete: () => false,

View File

@ -20,6 +20,8 @@ const workspace = {
],
'OR'
),
folderRead: () => true,
folderManage: () => true,
folderCreate: () =>
hasPermission(
[

View File

@ -73,6 +73,9 @@ const systemManage = {
],
'OR',
),
folderRead: () => false,
folderManage: () => false,
folderCreate: () => false,
folderEdit: () => false,
folderDelete: () => false,

View File

@ -77,6 +77,9 @@ const share = {
),
auth: () => false,
folderRead: () => false,
folderManage: () => false,
folderCreate: () => false,
folderEdit: () => false,
folderDelete: () => false,

View File

@ -40,6 +40,8 @@ const workspace = {
],
'OR'
),
folderRead: () => true,
folderManage: () => true,
folderCreate: () =>
hasPermission(
[

View File

@ -94,6 +94,13 @@ const PermissionConst = {
ROLE_ADD_MEMBER: new Permission('ROLE:READ+ADD_MEMBER'),
ROLE_REMOVE_MEMBER: new Permission('ROLE:READ+REMOVE_MEMBER'),
APPLICATION_FOLDER_READ: new Permission('APPLICATION_FOLDER:READ'),
APPLICATION_FOLDER_EDIT: new Permission('APPLICATION_FOLDER:READ+EDIT'),
KNOWLEDGE_FOLDER_READ: new Permission('APPLICATION_FOLDER:READ'),
KNOWLEDGE_FOLDER_EDIT: new Permission('APPLICATION_FOLDER:READ+EDIT'),
TOOL_FOLDER_READ: new Permission('APPLICATION_FOLDER:READ'),
TOOL_FOLDER_EDIT: new Permission('APPLICATION_FOLDER:READ+EDIT'),
KNOWLEDGE_READ: new Permission('KNOWLEDGE:READ'),
KNOWLEDGE_CREATE: new Permission('KNOWLEDGE:READ+CREATE'),
KNOWLEDGE_SYNC: new Permission('KNOWLEDGE:READ+SYNC'),

View File

@ -67,8 +67,10 @@
<el-table-column prop="name" :label="$t('common.name')">
<template #default="{ row }">
<el-space :size="8">
<!--  文件夹 icon -->
<AppIcon v-if="row.resource_type === 'folder'" iconName="app-folder" style="font-size: 20px"></AppIcon>
<!--  知识库 icon -->
<KnowledgeIcon :size="20" v-if="isKnowledge" :type="row.icon" />
<KnowledgeIcon :size="20" v-else-if="isKnowledge" :type="row.icon" />
<!--  应用/工具 自定义 icon -->
<el-avatar
v-else-if="isAppIcon(row?.icon) && !isModel"
@ -100,7 +102,7 @@
v-model="row.permission"
@change="(val: any) => submitPermissions(val, row)"
>
<template v-for="(item, index) in permissionOptions" :key="index">
<template v-for="(item, index) in getRowPermissionOptions(row)" :key="index">
<el-radio :value="item.value" class="mr-16">{{ item.label }}</el-radio>
</template>
</el-radio-group>
@ -154,6 +156,28 @@ const props = defineProps<{
}>()
const emit = defineEmits(['submitPermissions'])
const permissionOptionMap = computed(() => {
return {
rootFolder: getPermissionOptions(true, true),
folder: getPermissionOptions(true, false),
resource: getPermissionOptions(false, false),
}
})
const getRowPermissionOptions = (row: any) => {
const isFolder = row.resource_type === 'folder'
const isRoot = isFolder && row.folder_id === null
if (isRoot) {
return permissionOptionMap.value.rootFolder
}
if (isFolder) {
return permissionOptionMap.value.folder
}
return permissionOptionMap.value.resource
}
const permissionOptions = computed(() => {
return getPermissionOptions()
})

View File

@ -23,7 +23,15 @@ const permissionOptions = [
]
const getPermissionOptions=()=>{
const getPermissionOptions = (isFodler = false, isRootFolder = false) => {
if (isFodler && isRootFolder) {
return permissionOptions.filter(
item => item.value === AuthorizationEnum.VIEW || item.value === AuthorizationEnum.MANAGE
)
}
if (isFodler) {
return permissionOptions
}
if (hasPermission([EditionConst.IS_EE, EditionConst.IS_PE], 'OR')) {
return [...permissionOptions,{
label: t('views.system.resourceAuthorization.setting.role'),

View File

@ -63,7 +63,7 @@
</div>
</div>
<PermissionTable
:data="permissionData"
:data="treeData"
:type="activeData.type"
ref="PermissionTableRef"
:getData="getPermissionList"
@ -164,15 +164,59 @@ const getPermissionList = () => {
workspaceId,
currentUser.value,
(route.meta?.resource as string) || 'APPLICATION',
PermissionTableRef.value.paginationConfig,
params,
rLoading,
).then((res) => {
permissionData.value = res.data.records || []
PermissionTableRef.value.paginationConfig.total = res.data.total || 0
const resourceType = (route.meta?.resource as string) || 'APPLICATION'
if (resourceType === 'MODEL') {
permissionData.value = res.data || []
} else {
permissionData.value = res.data.map((item: any) => {
if (!item.folder_id && item.permission === 'NOT_AUTH') {
return {...item, permission: 'VIEW'}
}
return item}) || []
}
})
}
const toTree = (nodeList: any, pField: any) => {
if (!nodeList || nodeList.length === 0) return []
const list = JSON.parse(JSON.stringify(nodeList))
if (!pField) {
pField = 'parentId'
}
const nodeMap = Object.fromEntries(list.map((item: any) => [item.id, item]))
for (let index = 0; index < nodeList.length; index++) {
const element = list[index];
if (!element.children) {
element.children = []
}
if (element[pField]) {
const pNode = nodeMap[element[pField]]
if (pNode) {
if (!pNode.children) {
pNode.children = []
}
pNode.children.push(element)
}
}
}
return list.filter((item: any) => !item[pField])
}
const treeData = computed(() => {
const resourceType = (route.meta?.resource as string) || 'APPLICATION'
if (resourceType === 'MODEL') {
return permissionData.value
}
return toTree(permissionData.value, 'folder_id')
})
function clickMemberHandle(item: any) {
currentUser.value = item.id
currentType.value = item.type