Skip to content

Commit d976510

Browse files
feat: resource_permission
1 parent a3b55f6 commit d976510

8 files changed

Lines changed: 227 additions & 77 deletions

File tree

‎ui/src/api/admin/system/common.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const getWorkspaceMembers = (workspaceId: string, query?: RequestParams) => {
1616
return get<CommonUserOption[]>(`/workspace/${workspaceId}/user_member`, query)
1717
}
1818

19+
20+
1921
export default {
2022
getDefaultPassword,
2123
getAllUsers,

‎ui/src/api/admin/system/resource-authorization.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import type {
88

99
/** 系统管理资源授权 */
1010
const prefix = (workspaceId: string) => `/workspace/${workspaceId}/user_resource_permission`
11-
1211
/** 获取指定空间、指定用户、指定资源类型的权限列表。 */
1312
const getUserResourcePermissions = (
1413
workspaceId: string,
@@ -35,6 +34,7 @@ const putUserResourcePermissions = (
3534
)
3635
}
3736

37+
3838
export default {
3939
getUserResourcePermissions,
4040
putUserResourcePermissions,

‎ui/src/components/global/mk-list-item/index.vue‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<script setup lang="ts" generic="T extends Record<string, unknown> = Record<string, unknown>">
1+
<script setup lang="ts" generic="T extends object = Record<string, unknown>">
22
import { computed } from 'vue'
33
44
defineOptions({ name: 'MkListItem' })

‎ui/src/components/mk-search-list/index.vue‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<script setup lang="ts" generic="T extends Record<string, unknown>">
1+
<script setup lang="ts" generic="T extends object">
22
import type { ScrollbarDirection, ScrollbarInstance } from 'element-plus'
33
import { computed, nextTick, ref, watch } from 'vue'
44

‎ui/src/views/VIEW_README.md‎

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -81,31 +81,31 @@ Dialog。新增或重命名文件时,应同步更新所有导入和页面功
8181

8282
## 页面功能登记
8383

84-
| 页面 | 功能说明 |
85-
| --------------------------------------------------------------- | ---------------------------------- | --- |
86-
| `application/ApplicationView.vue` | 工作空间智能体目录与智能体卡片页面 | |
87-
| `workflow/ApplicationWorkflowView.vue` | 智能体工作流页面头部与全屏画布 |
88-
| `chat/ChatView.vue` | Chat 入口的对话页面 |
89-
| `error/NotFoundView.vue` | Admin 未匹配路由和全局 404 页面 |
90-
| `home/HomeView.vue` | Workspace 首页 |
91-
| `knowledge/KnowledgeDetailView.vue` | 知识库详情页面 |
92-
| `knowledge/DocumentDetailView.vue` | 知识库文档详情页面 |
93-
| `login/LoginView.vue` | Admin 登录页面 |
94-
| `login/ForgotPasswordView.vue` | 忘记密码页面 |
95-
| `model/ModelView.vue` | 工作空间模型目录与模型卡片页面 |
96-
| `system/SystemView.vue` | System 模块通用占位页面 |
97-
| `system/identity/groups/UserGroupListView.vue` | 用户组列表页面 |
98-
| `system/identity/resource-authorization/ResourceAuthorizationView.vue` | 工作空间成员的分类资源授权页面 |
99-
| `system/identity/roles/RoleListView.vue` | 角色列表页面 |
100-
| `system/identity/users/UserListView.vue` | 用户列表页面 |
101-
| `system/identity/workspaces/WorkspaceListView.vue` | 工作空间列表页面 |
102-
| `system/chat/user-groups/GroupsListView.vue` | 对话用户组及组成员管理页面 |
103-
| `system/chat/users/UserListView.vue` | 对话用户列表及用户导入管理页面 |
104-
| `system/settings/AppearanceSettingsView.vue` | 系统外观设置和登录外观预览页面 |
105-
| `system/settings/authentication/AuthenticationSettingsView.vue` | 系统登录及认证源配置页面 |
106-
| `system/settings/email/EmailSettingsView.vue` | 系统邮件 SMTP 服务配置页面 |
107-
| `system/operate-logs/OperateLogListView.vue` | 系统操作日志查询与清理页面 |
108-
| `tool/ToolView.vue` | 工作空间工具目录与工具卡片页面 |
84+
| 页面 | 功能说明 |
85+
| ---------------------------------------------------------------------- | -------------------------------------- | --- |
86+
| `application/ApplicationView.vue` | 工作空间智能体目录与智能体卡片页面 | |
87+
| `workflow/ApplicationWorkflowView.vue` | 智能体工作流页面头部与全屏画布 |
88+
| `chat/ChatView.vue` | Chat 入口的对话页面 |
89+
| `error/NotFoundView.vue` | Admin 未匹配路由和全局 404 页面 |
90+
| `home/HomeView.vue` | Workspace 首页 |
91+
| `knowledge/KnowledgeDetailView.vue` | 知识库详情页面 |
92+
| `knowledge/DocumentDetailView.vue` | 知识库文档详情页面 |
93+
| `login/LoginView.vue` | Admin 登录页面 |
94+
| `login/ForgotPasswordView.vue` | 忘记密码页面 |
95+
| `model/ModelView.vue` | 工作空间模型目录与模型卡片页面 |
96+
| `system/SystemView.vue` | System 模块通用占位页面 |
97+
| `system/identity/groups/UserGroupListView.vue` | 用户组列表页面 |
98+
| `system/identity/resource-authorization/ResourceAuthorizationView.vue` | 工作空间用户组与用户的分类资源授权页面 |
99+
| `system/identity/roles/RoleListView.vue` | 角色列表页面 |
100+
| `system/identity/users/UserListView.vue` | 用户列表页面 |
101+
| `system/identity/workspaces/WorkspaceListView.vue` | 工作空间列表页面 |
102+
| `system/chat/user-groups/GroupsListView.vue` | 对话用户组及组成员管理页面 |
103+
| `system/chat/users/UserListView.vue` | 对话用户列表及用户导入管理页面 |
104+
| `system/settings/AppearanceSettingsView.vue` | 系统外观设置和登录外观预览页面 |
105+
| `system/settings/authentication/AuthenticationSettingsView.vue` | 系统登录及认证源配置页面 |
106+
| `system/settings/email/EmailSettingsView.vue` | 系统邮件 SMTP 服务配置页面 |
107+
| `system/operate-logs/OperateLogListView.vue` | 系统操作日志查询与清理页面 |
108+
| `tool/ToolView.vue` | 工作空间工具目录与工具卡片页面 |
109109

110110
## 备注要求
111111

‎ui/src/views/system/identity/resource-authorization/ResourceAuthorizationView.vue‎

Lines changed: 116 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,40 @@
11
<script setup lang="ts">
22
import { onMounted, ref } from 'vue'
3+
import { useRoute } from 'vue-router'
34
import WorkspaceApi from '@/api/admin/system/workspace'
45
import CommonSystemApi from '@/api/admin/system/common'
6+
import UserGroupsApi from '@/api/admin/system/user-groups'
57
import ResourceAuthorizationApi from '@/api/admin/system/resource-authorization'
68
import { RESOURCE_TYPE, RESOURCE_PERMISSION } from '@/api/enums'
79
import type {
810
ResourceAuthorizationType,
911
ResourcePermissionItem,
1012
ResourcePermissionPayload,
1113
CommonUserOption,
14+
OptionItem,
15+
SystemUserGroup,
1216
WorkspaceItem,
1317
} from '@/api/types'
1418
import { useStore } from '@/stores'
1519
import { MsgSuccess } from '@/utils/message'
1620
import WorkspaceDropdown from '@/components/business/workspace-dropdown/index.vue'
17-
import MkSearchList from '@/components/mk-search-list/index.vue'
1821
import PermissionTable from './components/PermissionTable.vue'
22+
import UserAuthorizationList from './components/UserAuthorizationList.vue'
23+
import UserGroupAuthorizationList from './components/UserGroupAuthorizationList.vue'
24+
import { RESOURCE_AUTHORIZATION_LABELS } from './constants'
1925
2026
const { auth } = useStore()
27+
const route = useRoute()
2128
22-
const resourceType = ref<ResourceAuthorizationType>(RESOURCE_TYPE.APPLICATION)
29+
const resourceType = ref<ResourceAuthorizationType>(
30+
(route.meta.resource as ResourceAuthorizationType) ?? RESOURCE_TYPE.APPLICATION,
31+
)
32+
const resourceTypeOptions: OptionItem<ResourceAuthorizationType>[] = [
33+
RESOURCE_TYPE.APPLICATION,
34+
RESOURCE_TYPE.KNOWLEDGE,
35+
RESOURCE_TYPE.TOOL,
36+
RESOURCE_TYPE.MODEL,
37+
].map((value) => ({ label: RESOURCE_AUTHORIZATION_LABELS[value], value }))
2338
2439
/* 选择工作空间列表 */
2540
const loadingView = ref(false)
@@ -28,7 +43,10 @@ const workspaceOptions = ref<WorkspaceItem[]>([])
2843
2944
function handleWorkspaceSelect(workspace: WorkspaceItem) {
3045
selectedWorkspaceId.value = workspace.id ?? 'default'
31-
loadWorkspaceMembers()
46+
loadingView.value = true
47+
loadAuthorizationTargets().finally(() => {
48+
loadingView.value = false
49+
})
3250
}
3351
3452
function loadWorkspaceOptions() {
@@ -37,44 +55,68 @@ function loadWorkspaceOptions() {
3755
})
3856
}
3957
40-
/* 选择用户(普通用户) */
41-
58+
/* 授权对象 */
59+
const targetType = ref<'user-group' | 'user'>('user-group')
60+
const userGroups = ref<SystemUserGroup[]>([])
4261
const workspaceMembers = ref<CommonUserOption[]>([])
43-
const selectedMemberId = ref('')
62+
const selectedUserGroupId = ref('')
63+
const selectedUserId = ref('')
4464
45-
function loadWorkspaceMembers() {
46-
return CommonSystemApi.getWorkspaceMembers(selectedWorkspaceId.value).then((members) => {
47-
workspaceMembers.value = members
48-
selectedMemberId.value = members[0]?.id ?? ''
65+
function loadAuthorizationTargets() {
66+
return Promise.all([
67+
UserGroupsApi.getSystemUserGroups(selectedWorkspaceId.value),
68+
CommonSystemApi.getWorkspaceMembers(selectedWorkspaceId.value),
69+
]).then(([groups, users]) => {
70+
userGroups.value = groups
71+
workspaceMembers.value = users
72+
selectedUserGroupId.value = groups[0]?.id ?? ''
73+
selectedUserId.value = users[0]?.id ?? ''
4974
return loadResourcePermissions()
5075
})
5176
}
5277
53-
function handleMemberSelect(member: CommonUserOption) {
54-
selectedMemberId.value = member.id
78+
function handleTargetTypeChange() {
5579
loadResourcePermissions()
5680
}
5781
58-
function getMemberRoleText(member: CommonUserOption) {
59-
return member.roles?.join(',') ?? ''
82+
function handleUserGroupSelect(userGroup: SystemUserGroup) {
83+
selectedUserGroupId.value = userGroup.id
84+
loadResourcePermissions()
85+
}
86+
87+
function handleUserSelect(user: CommonUserOption) {
88+
selectedUserId.value = user.id
89+
loadResourcePermissions()
6090
}
6191
6292
/* 资源权限 */
6393
const loadingPermissions = ref(false)
6494
const resourcePermissions = ref<ResourcePermissionItem[]>([])
6595
6696
function loadResourcePermissions() {
67-
if (!selectedMemberId.value) {
97+
const targetId =
98+
targetType.value === 'user-group' ? selectedUserGroupId.value : selectedUserId.value
99+
100+
if (!targetId) {
68101
resourcePermissions.value = []
69102
return Promise.resolve()
70103
}
71104
72105
loadingPermissions.value = true
73-
return ResourceAuthorizationApi.getUserResourcePermissions(
74-
selectedWorkspaceId.value,
75-
selectedMemberId.value,
76-
resourceType.value,
77-
)
106+
const permissionRequest =
107+
targetType.value === 'user-group'
108+
? ResourceAuthorizationApi.getUserResourcePermissions(
109+
selectedWorkspaceId.value,
110+
targetId,
111+
resourceType.value,
112+
)
113+
: ResourceAuthorizationApi.getUserResourcePermissions(
114+
selectedWorkspaceId.value,
115+
targetId,
116+
resourceType.value,
117+
)
118+
119+
return permissionRequest
78120
.then((permissions) => {
79121
resourcePermissions.value = buildResourceTree(permissions)
80122
})
@@ -114,15 +156,28 @@ function buildResourceTree(resourceItems: ResourcePermissionItem[]) {
114156
/* 保存权限 */
115157
116158
function handlePermissionsSubmit(permissions: ResourcePermissionPayload[]) {
117-
if (!selectedMemberId.value) return
159+
const targetId =
160+
targetType.value === 'user-group' ? selectedUserGroupId.value : selectedUserId.value
161+
162+
if (!targetId) return
118163
119164
loadingPermissions.value = true
120-
ResourceAuthorizationApi.putUserResourcePermissions(
121-
selectedWorkspaceId.value,
122-
selectedMemberId.value,
123-
resourceType.value,
124-
permissions,
125-
)
165+
const permissionRequest =
166+
targetType.value === 'user-group'
167+
? ResourceAuthorizationApi.putUserResourcePermissions(
168+
selectedWorkspaceId.value,
169+
targetId,
170+
resourceType.value,
171+
permissions,
172+
)
173+
: ResourceAuthorizationApi.putUserResourcePermissions(
174+
selectedWorkspaceId.value,
175+
targetId,
176+
resourceType.value,
177+
permissions,
178+
)
179+
180+
permissionRequest
126181
.then(() => {
127182
MsgSuccess('提交成功')
128183
return loadResourcePermissions()
@@ -135,7 +190,7 @@ function handlePermissionsSubmit(permissions: ResourcePermissionPayload[]) {
135190
onMounted(() => {
136191
loadingView.value = true
137192
Promise.all(
138-
auth.isEE ? [loadWorkspaceOptions(), loadWorkspaceMembers()] : [loadWorkspaceMembers()],
193+
auth.isEE ? [loadWorkspaceOptions(), loadAuthorizationTargets()] : [loadAuthorizationTargets()],
139194
).finally(() => {
140195
loadingView.value = false
141196
})
@@ -154,33 +209,45 @@ onMounted(() => {
154209

155210
<template #aside="{ Header }">
156211
<component :is="Header">
157-
<h4>按用户</h4>
212+
<el-tabs class="w-full" v-model="targetType" @tab-change="handleTargetTypeChange">
213+
<el-tab-pane label="按用户组" name="user-group" />
214+
<el-tab-pane label="按用户" name="user" />
215+
</el-tabs>
158216
</component>
159-
<MkSearchList
160-
:data="workspaceMembers"
161-
:default-active="selectedMemberId"
162-
:props="{ label: 'nick_name', value: 'id' }"
163-
@click="handleMemberSelect"
164-
>
165-
<template #default="{ row }">
166-
<div class="flex min-w-0 flex-1 items-center gap-2">
167-
<span class="min-w-0 truncate" :title="row.nick_name">{{ row.nick_name }}</span>
168-
<span
169-
v-if="(auth.isEE || auth.isPE) && row.roles?.length"
170-
class="min-w-0 truncate text-N600"
171-
:title="getMemberRoleText(row)"
172-
>
173-
({{ getMemberRoleText(row) }})
174-
</span>
175-
</div>
176-
</template>
177-
</MkSearchList>
217+
218+
<UserGroupAuthorizationList
219+
v-if="targetType === 'user-group'"
220+
:active-id="selectedUserGroupId"
221+
:user-groups="userGroups"
222+
@select="handleUserGroupSelect"
223+
/>
224+
<UserAuthorizationList
225+
v-else
226+
:active-id="selectedUserId"
227+
:users="workspaceMembers"
228+
@select="handleUserSelect"
229+
/>
178230
</template>
179231

180232
<template #default="{ Header }">
181233
<component :is="Header">
182-
<h4>资源权限配置</h4>
234+
<div class="w-full">
235+
<h4 class="mb-4">资源权限配置</h4>
236+
<el-tabs
237+
v-model="resourceType"
238+
class="w-full"
239+
@tab-change="loadResourcePermissions"
240+
>
241+
<el-tab-pane
242+
v-for="resourceTypeOption in resourceTypeOptions"
243+
:key="resourceTypeOption.value"
244+
:label="resourceTypeOption.label"
245+
:name="resourceTypeOption.value"
246+
/>
247+
</el-tabs>
248+
</div>
183249
</component>
250+
184251
<PermissionTable
185252
v-loading="loadingPermissions"
186253
:data="resourcePermissions"
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<script setup lang="ts">
2+
import type { CommonUserOption } from '@/api/types'
3+
import { useStore } from '@/stores'
4+
import MkSearchList from '@/components/mk-search-list/index.vue'
5+
6+
defineOptions({ name: 'UserAuthorizationList' })
7+
8+
defineProps<{
9+
activeId: string
10+
users: CommonUserOption[]
11+
}>()
12+
const emit = defineEmits<{
13+
select: [user: CommonUserOption]
14+
}>()
15+
16+
const { auth } = useStore()
17+
18+
function getUserRoleText(user: CommonUserOption) {
19+
return user.roles?.join(',') ?? ''
20+
}
21+
22+
function handleUserSelect(user: CommonUserOption) {
23+
emit('select', user)
24+
}
25+
</script>
26+
27+
<template>
28+
<MkSearchList
29+
:data="users"
30+
:default-active="activeId"
31+
:props="{ label: 'nick_name', value: 'id' }"
32+
@click="handleUserSelect"
33+
>
34+
<template #default="{ row }">
35+
<div class="flex min-w-0 flex-1 items-center gap-2">
36+
<span class="min-w-0 truncate" :title="row.nick_name">{{ row.nick_name }}</span>
37+
<span
38+
v-if="(auth.isEE || auth.isPE) && row.roles?.length"
39+
class="min-w-0 truncate text-N600"
40+
:title="getUserRoleText(row)"
41+
>
42+
({{ getUserRoleText(row) }})
43+
</span>
44+
</div>
45+
</template>
46+
</MkSearchList>
47+
</template>

0 commit comments

Comments
 (0)