UnisKB/ui/src/views/system/resource-authorization/index.vue

481 lines
14 KiB
Vue
Raw Normal View History

2025-05-30 06:02:54 +00:00
<template>
2025-06-03 08:08:49 +00:00
<div class="resource-authorization p-16-24">
2025-06-20 09:02:42 +00:00
<div class="flex align-center mb-16">
2025-06-27 10:13:12 +00:00
<el-breadcrumb separator-icon="ArrowRight">
<el-breadcrumb-item>{{ t('views.resourceAuthorization.title') }}</el-breadcrumb-item>
<el-breadcrumb-item>
<h5 class="ml-4 color-text-primary">{{ activeData.label }}</h5>
</el-breadcrumb-item>
</el-breadcrumb>
2025-06-20 09:02:42 +00:00
<!-- 企业版: 工作空间下拉框-->
2025-06-27 14:22:52 +00:00
<el-divider
class="ml-24"
direction="vertical"
v-if="hasPermission(EditionConst.IS_EE, 'OR')"
/>
2025-06-20 10:10:32 +00:00
<WorkspaceDropdown
v-if="hasPermission(EditionConst.IS_EE, 'OR')"
:data="workspaceList"
:currentWorkspace="currentWorkspace"
@changeWorkspace="changeWorkspace"
/>
2025-06-20 09:02:42 +00:00
</div>
2025-06-03 08:08:49 +00:00
<el-card style="--el-card-padding: 0">
<div class="flex main-calc-height">
2025-06-26 09:00:11 +00:00
<div class="resource-authorization__left border-r p-16">
2025-06-03 08:08:49 +00:00
<div class="p-8">
<h4 class="mb-12">{{ $t('views.resourceAuthorization.member') }}</h4>
2025-06-20 09:02:42 +00:00
<el-input
v-model="filterText"
:placeholder="$t('common.search')"
prefix-icon="Search"
clearable
/>
2025-05-30 06:02:54 +00:00
</div>
<div class="list-height-left">
<el-scrollbar>
2025-06-20 09:02:42 +00:00
<common-list
:data="filterMember"
class="mt-8"
v-loading="loading"
@click="clickMemberHandle"
:default-active="currentUser"
>
2025-05-30 06:02:54 +00:00
<template #default="{ row }">
<div class="flex-between">
2025-06-20 09:02:42 +00:00
<div class="flex">
2025-06-03 08:08:49 +00:00
<span class="mr-8">{{ row.nick_name }}</span>
2025-06-20 09:02:42 +00:00
<TagGroup :tags="row.roles" />
2025-05-30 06:02:54 +00:00
</div>
</div>
</template>
</common-list>
</el-scrollbar>
</div>
</div>
2025-06-26 09:00:11 +00:00
<div class="permission-setting p-24 flex" v-loading="rLoading">
2025-06-03 08:08:49 +00:00
<div class="resource-authorization__table">
2025-06-27 10:17:27 +00:00
<h4 class="mb-16">{{ $t('views.resourceAuthorization.permissionSetting') }}</h4>
2025-06-27 09:58:39 +00:00
<!-- <el-tabs
2025-06-20 13:13:31 +00:00
v-model="activeName"
@tab-change="handleTabChange"
class="resource-authorization__tabs"
>
2025-06-20 09:02:42 +00:00
<el-tab-pane
v-for="(item, index) in settingTags"
:key="item.value"
:label="item.label"
:name="item.value"
2025-06-27 09:58:39 +00:00
> -->
<PermissionSetting
:data="activeData.data"
:type="activeData.type"
:tableHeight="tableHeight"
:manage="isManage(currentType)"
@refreshData="refreshData"
v-model:isRole="activeData.isRole"
></PermissionSetting>
<!-- </el-tab-pane> -->
<!-- </el-tabs> -->
2025-05-30 06:02:54 +00:00
</div>
<div class="submit-button">
2025-07-02 08:01:22 +00:00
<el-button
type="primary"
@click="submitPermissions"
v-if="
hasPermission(
permissionObj[(route.meta?.resource as string) || 'APPLICATION'],
'OR',
)
"
>{{ $t('common.save') }}</el-button
>
2025-05-30 06:02:54 +00:00
</div>
</div>
</div>
</el-card>
</div>
</template>
2025-05-28 11:03:28 +00:00
<script lang="ts" setup>
2025-06-20 10:10:32 +00:00
import { onMounted, ref, reactive, watch, computed } from 'vue'
2025-06-27 09:58:39 +00:00
import { useRoute } from 'vue-router'
2025-06-20 19:14:59 +00:00
import AuthorizationApi from '@/api/system/resource-authorization'
2025-06-03 08:08:49 +00:00
import PermissionSetting from './component/PermissionSetting.vue'
2025-05-30 06:02:54 +00:00
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { AuthorizationEnum } from '@/enums/system'
import { t } from '@/locales'
import useStore from '@/stores'
import { cloneDeep } from 'lodash'
2025-06-30 05:38:04 +00:00
import { EditionConst, RoleConst, PermissionConst } from '@/utils/permission/data'
2025-06-20 09:02:42 +00:00
import { hasPermission } from '@/utils/permission/index'
2025-06-20 10:10:32 +00:00
import type { WorkspaceItem } from '@/api/type/workspace'
2025-06-30 05:38:04 +00:00
import { ComplexPermission } from '@/utils/permission/type'
2025-07-02 08:01:22 +00:00
import { loadPermissionApi } from '@/utils/dynamics-api/permission-api.ts'
2025-06-30 05:38:04 +00:00
2025-06-27 09:58:39 +00:00
const route = useRoute()
2025-06-25 08:59:42 +00:00
const { user } = useStore()
2025-05-30 06:02:54 +00:00
const loading = ref(false)
const rLoading = ref(false)
2025-06-03 08:08:49 +00:00
const memberList = ref<any[]>([]) // 全部成员
const filterMember = ref<any[]>([]) // 搜索过滤后列表
2025-06-04 05:05:39 +00:00
const currentUser = ref<string>('')
const currentType = ref<string>('')
2025-05-30 06:02:54 +00:00
const filterText = ref('')
const tableHeight = ref(0)
2025-06-25 08:59:42 +00:00
2025-07-02 08:01:22 +00:00
const permissionObj = ref<any>({
APPLICATION: new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[
PermissionConst.APPLICATION_WORKSPACE_USER_RESOURCE_PERMISSION_EDIT,
PermissionConst.APPLICATION_WORKSPACE_USER_RESOURCE_PERMISSION_EDIT
.getWorkspacePermissionWorkspaceManageRole,
],
[],
'OR',
),
KNOWLEDGE: new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[
PermissionConst.KNOWLEDGE_WORKSPACE_USER_RESOURCE_PERMISSION_EDIT,
PermissionConst.KNOWLEDGE_WORKSPACE_USER_RESOURCE_PERMISSION_EDIT
.getWorkspacePermissionWorkspaceManageRole,
],
[],
'OR',
),
TOOL: new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[
PermissionConst.TOOL_WORKSPACE_USER_RESOURCE_PERMISSION_EDIT,
PermissionConst.TOOL_WORKSPACE_USER_RESOURCE_PERMISSION_EDIT
.getWorkspacePermissionWorkspaceManageRole,
],
[],
'OR',
),
MODEL: new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[
PermissionConst.MODEL_WORKSPACE_USER_RESOURCE_PERMISSION_EDIT,
PermissionConst.MODEL_WORKSPACE_USER_RESOURCE_PERMISSION_EDIT
.getWorkspacePermissionWorkspaceManageRole,
],
[],
'OR',
),
2025-06-30 05:38:04 +00:00
})
2025-07-02 08:01:22 +00:00
console.log(route.meta.resource || 'APPLICATION')
2025-05-30 06:02:54 +00:00
const settingTags = reactive([
{
label: t('views.knowledge.title'),
2025-06-27 09:58:39 +00:00
type: AuthorizationEnum.KNOWLEDGE,
2025-05-30 06:02:54 +00:00
data: [] as any,
2025-06-23 09:49:43 +00:00
isRole: false,
2025-05-30 06:02:54 +00:00
},
{
label: t('views.application.title'),
2025-06-27 09:58:39 +00:00
type: AuthorizationEnum.APPLICATION,
2025-05-30 06:02:54 +00:00
data: [] as any,
2025-06-23 09:49:43 +00:00
isRole: false,
2025-05-30 06:02:54 +00:00
},
{
label: t('views.tool.title'),
2025-06-27 09:58:39 +00:00
type: AuthorizationEnum.TOOL,
data: [] as any,
isRole: false,
},
{
label: t('views.model.title'),
2025-06-27 09:58:39 +00:00
type: AuthorizationEnum.MODEL,
data: [] as any,
isRole: false,
},
2025-05-30 06:02:54 +00:00
])
2025-06-27 09:58:39 +00:00
// 当前激活的数据类型(应用/知识库/模型/工具)
const activeData = computed(() => {
var lastIndex = route.path.lastIndexOf('/')
const currentPathType = route.path.substring(lastIndex + 1).toUpperCase()
return settingTags.filter((item) => {
return item.type === currentPathType
})[0]
})
2025-06-04 05:05:39 +00:00
watch(filterText, (val: any) => {
2025-05-30 06:02:54 +00:00
if (val) {
2025-06-04 05:05:39 +00:00
filterMember.value = memberList.value.filter((v: any) =>
v.nick_name.toLowerCase().includes(val.toLowerCase()),
2025-05-30 06:02:54 +00:00
)
} else {
filterMember.value = memberList.value
}
})
2025-06-06 04:31:30 +00:00
function isManage(type: string) {
2025-05-30 06:02:54 +00:00
return type === 'manage'
}
2025-05-28 11:03:28 +00:00
const flotTree = (tree: Array<any>, result: Array<any>) => {
2025-06-20 09:02:42 +00:00
tree.forEach((tItem) => {
result.push(tItem)
if (tItem.children) {
flotTree(tItem.children, result)
}
})
return result
}
2025-05-30 06:02:54 +00:00
function submitPermissions() {
const user_resource_permission_list = settingTags
.map((item: any, index: number) => {
return flotTree(item.data, [])
.filter((v: any) => !v.isFolder)
.map((v: any) => {
return {
target_id: v.id,
auth_target_type: item.value,
permission: v.permission,
auth_type: item.isRole ? 'ROLE' : 'RESOURCE_PERMISSION_GROUP',
}
})
})
.reduce((pre: any, next: any) => [...pre, ...next], [])
2025-06-23 09:49:43 +00:00
2025-06-20 09:02:42 +00:00
AuthorizationApi.putResourceAuthorization(
2025-06-20 10:10:32 +00:00
currentWorkspaceId.value || 'default',
2025-06-20 09:02:42 +00:00
currentUser.value,
2025-06-27 14:22:52 +00:00
(route.meta?.resource as string) || 'APPLICATION',
2025-06-20 09:02:42 +00:00
{ user_resource_permission_list: user_resource_permission_list },
rLoading,
).then(() => {
2025-06-06 02:59:40 +00:00
MsgSuccess(t('common.submitSuccess'))
getWholeTree(currentUser.value)
2025-06-06 02:59:40 +00:00
})
2025-05-30 06:02:54 +00:00
}
2025-06-03 08:08:49 +00:00
function clickMemberHandle(item: any) {
currentUser.value = item.id
currentType.value = item.type
2025-06-16 09:07:00 +00:00
getWholeTree(item.id)
2025-06-03 08:08:49 +00:00
}
function getMember(id?: string) {
2025-06-20 10:10:32 +00:00
AuthorizationApi.getUserMember(currentWorkspaceId.value || 'default', loading).then((res) => {
2025-06-06 02:59:40 +00:00
memberList.value = res.data
filterMember.value = res.data
2025-07-07 08:59:24 +00:00
if (memberList.value.length > 0) {
const member = (id && memberList.value.find((p: any) => p.user_id === id)) || null
currentUser.value = member ? member.id : memberList.value?.[0]?.id
currentType.value = member ? member.type : memberList.value?.[0]?.type
getWholeTree(currentUser.value)
}
})
}
const dfsPermissionIndeterminateTrue = (arr: any = [], type: string) => {
return arr.every((item: any) => {
if (item.children?.length) {
item.permission[type] = dfsPermissionIndeterminateTrue(item.children, type)
}
return item.permission[type]
})
}
const dfsPermissionIndeterminate = (
arr: any = [],
type: string,
permissionHalf: any,
permissionHalfMap: any,
id: string,
) => {
arr.forEach((item: any) => {
if (item.isFolder) {
if (!permissionHalfMap[item.id]) {
permissionHalfMap[item.id] = cloneDeep(permissionHalf)
}
}
if (item.children?.length) {
dfsPermissionIndeterminate(item.children, type, permissionHalf, permissionHalfMap, item.id)
}
if (!item.isFolder) {
permissionHalfMap[id][type] = [...permissionHalfMap[id][type], item.permission[type]]
}
if (item.isFolder) {
item.permissionHalf[type] = permissionHalfMap[item.id][type].length
? new Set(permissionHalfMap[item.id][type]).size > 1
: false
if (item.children.some((ele: any) => ele.isFolder && ele.permissionHalf[type])) {
item.permissionHalf[type] = true
}
if (
item.children.some((ele: any) => ele.permission[type]) &&
item.children.some((ele: any) => !ele.permission[type])
) {
item.permissionHalf[type] = true
}
}
})
}
const dfsFolder = (arr: any[] = [], folderIdMap: any) => {
arr.forEach((ele) => {
if (ele.permission) return
if (ele.children?.length) {
if (folderIdMap[ele.id]) {
ele.children = [...ele.children, ...folderIdMap[ele.id]]
}
dfsFolder(ele.children, folderIdMap)
} else {
ele.children = folderIdMap[ele.id] || []
}
ele.isFolder = true
ele.permission = {
VIEW: false,
MANAGE: false,
ROLE: false,
}
ele.permissionHalf = {
VIEW: false,
MANAGE: false,
ROLE: false,
}
})
}
function getFolder() {
2025-06-20 10:10:32 +00:00
return AuthorizationApi.getSystemFolder(
currentWorkspaceId.value || 'default',
2025-06-27 09:58:39 +00:00
activeData.value.type,
2025-06-20 10:10:32 +00:00
{},
loading,
)
}
function getResourcePermissions(user_id: string) {
2025-06-20 10:10:32 +00:00
return AuthorizationApi.getResourceAuthorization(
currentWorkspaceId.value || 'default',
user_id,
2025-06-27 14:22:52 +00:00
(route.meta?.resource as string) || 'APPLICATION',
2025-06-20 10:10:32 +00:00
rLoading,
)
}
const getWholeTree = async (user_id: string) => {
const [parentRes, childrenRes] = await Promise.all([getFolder(), getResourcePermissions(user_id)])
if (!childrenRes.data || Object.keys(childrenRes.data).length > 0) {
settingTags.map((item: any) => {
let folderIdMap = []
const folderTree = cloneDeep((parentRes as unknown as any).data)
2025-06-27 09:58:39 +00:00
if (Object.keys(childrenRes.data).indexOf(item.type) !== -1) {
item.isRole =
2025-07-02 08:01:22 +00:00
childrenRes.data[item.type].length > 0 &&
hasPermission([EditionConst.IS_EE, EditionConst.IS_PE], 'OR')
2025-06-27 09:58:39 +00:00
? childrenRes.data[item.type][0].auth_type == 'ROLE'
: false
2025-06-27 09:58:39 +00:00
folderIdMap = getFolderIdMap(childrenRes.data[item.type])
dfsFolder(folderTree, folderIdMap)
const permissionHalf = {
VIEW: [],
MANAGE: [],
ROLE: [],
}
Object.keys(permissionHalf).forEach((ele) => {
dfsPermissionIndeterminateTrue(folderTree, ele)
dfsPermissionIndeterminate(folderTree, ele, cloneDeep(permissionHalf), {}, 'default')
})
item.data = folderTree
}
})
}
}
const refreshData = () => {
settingTags.map((item: any) => {
2025-06-27 09:58:39 +00:00
if (activeData.value.type === item.type) {
const permissionHalf = {
VIEW: [],
MANAGE: [],
ROLE: [],
}
Object.keys(permissionHalf).forEach((ele) => {
dfsPermissionIndeterminateTrue(item.data, ele)
dfsPermissionIndeterminate(item.data, ele, cloneDeep(permissionHalf), {}, 'default')
})
}
2025-06-06 02:59:40 +00:00
})
2025-06-03 08:08:49 +00:00
}
const getFolderIdMap = (arr: any = []) => {
return arr.reduce((pre: any, next: any) => {
if (pre[next.folder_id]) {
pre[next.folder_id].push(next)
} else {
pre[next.folder_id] = [next]
}
return pre
}, {})
}
2025-05-30 06:02:54 +00:00
2025-06-20 10:10:32 +00:00
const workspaceList = ref<WorkspaceItem[]>([])
const currentWorkspaceId = ref<string | undefined>('')
const currentWorkspace = computed(() => {
return workspaceList.value.find((w) => w.id == currentWorkspaceId.value)
})
async function getWorkspaceList() {
2025-07-02 01:56:42 +00:00
const res = await loadPermissionApi('workspace').getSystemWorkspaceList(loading)
2025-06-23 11:37:38 +00:00
workspaceList.value = res.data
currentWorkspaceId.value = 'default'
2025-06-20 10:10:32 +00:00
}
function changeWorkspace(item: WorkspaceItem) {
currentWorkspaceId.value = item.id
getMember()
}
2025-05-30 06:02:54 +00:00
onMounted(() => {
tableHeight.value = window.innerHeight - 330
window.onresize = () => {
return (() => {
tableHeight.value = window.innerHeight - 330
})()
}
2025-06-23 11:37:38 +00:00
if (user.isEE()) {
getWorkspaceList()
}
2025-06-03 08:08:49 +00:00
getMember()
2025-05-30 06:02:54 +00:00
})
2025-05-28 11:03:28 +00:00
</script>
2025-05-30 06:02:54 +00:00
<style lang="scss" scoped>
.resource-authorization {
2025-06-03 08:08:49 +00:00
.resource-authorization__left {
2025-05-30 06:02:54 +00:00
box-sizing: border-box;
width: var(--setting-left-width);
min-width: var(--setting-left-width);
}
.permission-setting {
2025-06-23 09:49:43 +00:00
flex: 1;
2025-06-23 03:12:37 +00:00
overflow: hidden;
2025-05-30 06:02:54 +00:00
box-sizing: border-box;
2025-06-03 08:08:49 +00:00
width: 100%;
2025-05-30 06:02:54 +00:00
flex-direction: column;
position: relative;
.submit-button {
position: absolute;
2025-06-26 09:00:11 +00:00
top: 24px;
2025-05-30 06:02:54 +00:00
right: 24px;
}
}
.list-height-left {
2025-06-03 08:08:49 +00:00
height: calc(100vh - 240px);
2025-05-30 06:02:54 +00:00
}
}
</style>