Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ui/src/api/API_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,13 @@ API 枚举与类型统一在 `src/api` 范围内管理,相关规则由本文
- 流式 POST 请求使用 `postStream` 返回原始 `Response`,由业务组件按具体协议解析数据块;
鉴权、语言请求头和错误状态仍由请求基础设施统一处理。
- 上传、下载和其他特殊请求在真实需求出现时独立设计,不提前塞入普通 JSON 请求客户端。

### 资源用户授权

`admin/workspace/resource-authorization.ts` 按指定资源查询和更新用户权限,使用
`resource_user_permission/resource/<target>/resource/<resource>`;与 System 用户视角的
`user_resource_permission` 区分。分页使用 `ParamsPage`,直接返回
`ResponsePage<ResourceUserPermission>`;提交 `ResourceUserPermissionPayload[]`。
`ResourceAuthorizationTargetType` 包含资源类型和三个 `_FOLDER` 类型,文件夹类型用于后端
鉴权。包含子资源时传 `include_children: true` 及经过管理权限筛选的 `folder_ids`,
普通资源或仅当前文件夹不传子文件夹 ID。loading、刷新及成功提示由抽屉负责。
14 changes: 13 additions & 1 deletion ui/src/api/admin/system/resource-authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,16 @@ const putUserResourcePermissions = (workspaceId: string, userId: string, resourc
return put<ResourcePermissionPayload[], ResourcePermissionPayload[]>(`${prefix(workspaceId)}/user/${userId}/resource/${resource}`, permissions)
}

export default { getUserResourcePermissions, putUserResourcePermissions }

/** 获取指定空间、指定用户组、指定资源类型的权限列表。 */
const getUserGroupResourcePermissions = (workspaceId: string, userId: string, resource: ResourceAuthorizationType, query?: Dict<unknown>) => {
return get<ResourcePermissionItem[]>(`${prefix(workspaceId)}/user-group/${userId}/resource/${resource}`, query)
}

/** 更新指定空间、指定用户组、指定资源类型的权限列表。 */
const putUserGroupResourcePermissions = (workspaceId: string, userId: string, resource: ResourceAuthorizationType, permissions: ResourcePermissionPayload[]) => {
return put<ResourcePermissionPayload[], ResourcePermissionPayload[]>(`${prefix(workspaceId)}/user-group/${userId}/resource/${resource}`, permissions)
}


export default { getUserResourcePermissions, putUserResourcePermissions, getUserGroupResourcePermissions, putUserGroupResourcePermissions }
29 changes: 29 additions & 0 deletions ui/src/api/admin/workspace/resource-authorization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { get, put } from '../core/request'
import type { ParamsPage, ResponsePage } from '../core/types'
import type { Dict, ResourceAuthorizationTargetType, ResourceUserPermission, ResourceUserPermissionPayload } from '@/api/types'

const prefix = (workspaceId: string, targetId: string, resource: ResourceAuthorizationTargetType) =>
`/workspace/${workspaceId}/resource_user_permission/resource/${targetId}/resource/${resource}`

/** 获取指定资源或文件夹的用户权限分页列表。 */
const getResourceAuthorization = (
workspaceId: string,
targetId: string,
resource: ResourceAuthorizationTargetType,
page: ParamsPage,
query?: Dict<unknown>,
) => {
return get<ResponsePage<ResourceUserPermission>>(`${prefix(workspaceId, targetId, resource)}/${page.currentPage}/${page.pageSize}`, query)
}

/** 更新资源的用户权限,可同时应用到有管理权限的子文件夹及资源。 */
const putResourceAuthorization = (
workspaceId: string,
targetId: string,
resource: ResourceAuthorizationTargetType,
permissions: ResourceUserPermissionPayload[],
) => {
return put<ResourceUserPermissionPayload[], ResourceUserPermissionPayload[]>(prefix(workspaceId, targetId, resource), permissions)
}

export default { getResourceAuthorization, putResourceAuthorization }
8 changes: 8 additions & 0 deletions ui/src/api/enums/resource-authorization.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
/** 后端资源授权的资源类型。 */
export const RESOURCE_TYPE = { APPLICATION: 'APPLICATION', KNOWLEDGE: 'KNOWLEDGE', MODEL: 'MODEL', TOOL: 'TOOL' } as const

/** 用户授权接口额外区分文件夹目标,以匹配后端鉴权入口。 */
export const RESOURCE_AUTHORIZATION_TARGET_TYPE = {
...RESOURCE_TYPE,
APPLICATION_FOLDER: 'APPLICATION_FOLDER',
KNOWLEDGE_FOLDER: 'KNOWLEDGE_FOLDER',
TOOL_FOLDER: 'TOOL_FOLDER',
} as const

/** 后端资源授权的权限值。 */
export const RESOURCE_PERMISSION = { MANAGE: 'MANAGE', NOT_AUTH: 'NOT_AUTH', ROLE: 'ROLE', VIEW: 'VIEW' } as const
20 changes: 19 additions & 1 deletion ui/src/api/types/resource-authorization.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
/** 系统资源授权 API 与页面共用的业务类型。 */

import { RESOURCE_TYPE, RESOURCE_PERMISSION } from '@/api/enums'
import { RESOURCE_TYPE, RESOURCE_PERMISSION, RESOURCE_AUTHORIZATION_TARGET_TYPE } from '@/api/enums'
import type { ToolType } from './tool'

export type ResourceType = (typeof RESOURCE_TYPE)[keyof typeof RESOURCE_TYPE]
export type ResourceAuthorizationType = ResourceType
export type ResourcePermission = (typeof RESOURCE_PERMISSION)[keyof typeof RESOURCE_PERMISSION]

export type ResourceAuthorizationTargetType = (typeof RESOURCE_AUTHORIZATION_TARGET_TYPE)[keyof typeof RESOURCE_AUTHORIZATION_TARGET_TYPE]

/** 指定资源下的用户及其权限,与用户视角的资源列表区分。 */
export interface ResourceUserPermission {
id: string
nick_name: string
username: string
role_name?: string[]
permission: ResourcePermission
}

export interface ResourceUserPermissionPayload {
user_id: string
permission: ResourcePermission
include_children?: boolean
folder_ids?: string[]
}

export interface ResourcePermissionItem {
auth_target_type: ResourceAuthorizationType
children?: ResourcePermissionItem[]
Expand Down
1 change: 1 addition & 0 deletions ui/src/api/types/system-user-groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface SystemUserGroup {

export interface SystemUserGroupMember {
id: string
roles: string[]
username: string
email: string
phone: string
Expand Down
18 changes: 18 additions & 0 deletions ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ src/components/
│ │ └── ModelParamsDialog.vue # 模型参数动态表单弹窗
│ ├── workspace-dropdown/
│ │ └── index.vue # 工作空间选择下拉框
│ ├── resource-authorization-drawer/ # 资源用户授权抽屉与权限配置弹窗
│ │ ├── index.vue
│ │ └── PermissionConfigDialog.vue
│ └── workspace-relation-tags/
│ └── index.vue # 标签及关联工作空间展示
├── global/ # 高频、稳定的基础组件,自动注册
Expand Down Expand Up @@ -1311,3 +1314,18 @@ import WorkspaceRelationTags from '@/components/business/workspace-relation-tags
5. 更新本文档中的目录树和组件说明。
6. 通过正常开发或构建流程刷新自动生成的组件声明。
7. 运行 ESLint、类型检查以及受影响入口的生产构建。

### ResourceAuthorizationDrawer

手动导入 `business/resource-authorization-drawer/index.vue`。调用方传入完整 `api`
(`typeof ResourceAuthorizationApi`)和 `type`(`ResourceAuthorizationTargetType`),不由抽屉
根据路由路径选择接口。当前后端的资源用户授权统一使用
`api/admin/workspace/resource-authorization.ts`。通过 `open(id, folder?, workspaceId?)` 打开;
System 场景显式传入资源所属工作空间,Workspace 场景可读取当前工作空间。
`isFolder`、`isRootFolder` 标记文件夹及根目录,传入文件夹类型也可识别文件夹。

支持姓名、用户名、权限及商业版本角色搜索,跨页选择和单人、批量授权;搜索后清空选择。
根目录隐藏“不授权”,返回的“不授权”按“查看”展示。包含子资源时必须传入完整文件夹子树,
抽屉根据目标工作空间筛选有管理权限的文件夹 ID;没有可管理文件夹时禁用该范围。
`PermissionConfigDialog` 只收集权限和范围,保存成功才关闭并刷新,失败保留配置。
保存成功触发 `refresh`,打开和关闭后统一重置临时数据,过期查询不写回。
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<script setup lang="ts">
import { ref } from 'vue'
import type { OptionItem, ResourcePermission } from '@/api/types'

const props = defineProps<{
options: (OptionItem<ResourcePermission> & { description: string })[]
isFolder: boolean
canIncludeChildren: boolean
loading: boolean
}>()
const emit = defineEmits<{ submit: [permission: ResourcePermission, includeChildren: boolean] }>()

/* 权限和生效范围 */
const visible = ref(false)
const permission = ref<ResourcePermission>()
const includeChildren = ref(false)
const scopeOnly = ref(false)

function resetData() {
permission.value = undefined
includeChildren.value = false
scopeOnly.value = false
}

function open(currentPermission?: ResourcePermission) {
resetData()
permission.value = currentPermission
scopeOnly.value = Boolean(currentPermission)
visible.value = true
}

function handleSubmit() {
if (!permission.value || props.loading) return
emit('submit', permission.value, props.isFolder && includeChildren.value)
}

function close() {
visible.value = false
}

defineExpose({ open, close })
</script>

<template>
<MkDialog v-model="visible" :title="scopeOnly ? '生效资源' : '配置权限'" width="500" :show-close="!loading" @closed="resetData">
<el-radio-group v-if="!scopeOnly" v-model="permission" class="vertical-radio-group" :disabled="loading">
<el-radio v-for="option in options" :key="option.value" :value="option.value">
<p>{{ option.label }}</p>
<p v-if="option.description" class="mt-1 text-N500">{{ option.description }}</p>
</el-radio>
</el-radio-group>
<template v-if="isFolder">
<template v-if="!scopeOnly">
<el-divider />
<h6 class="mb-2">生效资源</h6>
</template>
<el-radio-group v-model="includeChildren" class="vertical-radio-group" :disabled="loading">
<el-radio :value="false">仅当前文件夹</el-radio>
<el-radio :value="true" :disabled="!canIncludeChildren">包含所有有管理权限的子文件夹及资源</el-radio>
</el-radio-group>
</template>
<template #footer>
<el-button plain :disabled="loading" @click="close">取消</el-button>
<el-button type="primary" :loading="loading" :disabled="!permission" @click="handleSubmit">确认</el-button>
</template>
</MkDialog>
</template>
Loading
Loading