Skip to content
Closed
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
4 changes: 2 additions & 2 deletions apps/common/auth/constants/permission_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class PermissionConstants(Enum):
HOMEPAGE_READ = (
Permission(group=Group.HOMEPAGE, sub_group=Group.HOMEPAGE, operate=Operate.READ, bit_index=0),
PermissionMeta(
role_list=[RoleConstants.USER],
role_list=[RoleConstants.WORKSPACE_MANAGE, RoleConstants.USER],
category=Category.WORKSPACE,
scope=[PermissionScopeConstants.WORKSPACE],
),
Expand All @@ -84,7 +84,7 @@ class PermissionConstants(Enum):
HOMEPAGE_EXPORT = (
Permission(group=Group.HOMEPAGE, sub_group=Group.HOMEPAGE, operate=Operate.EXPORT, bit_index=1),
PermissionMeta(
role_list=[RoleConstants.USER],
role_list=[RoleConstants.WORKSPACE_MANAGE, RoleConstants.USER],
category=Category.WORKSPACE,
scope=[PermissionScopeConstants.WORKSPACE],
),
Expand Down
11 changes: 8 additions & 3 deletions apps/users/serializers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,10 +700,11 @@ def get_user_list(self, user_id, workspace_id, nick_name):

return list(users)

def get_user_members(self, workspace_id):
def get_user_members(self, workspace_id, nick_name=None):
"""
获取工作空间成员列表
:param workspace_id: 工作空间ID
:param nick_name: 昵称模糊查询
:return: 成员列表
"""
role_model = DatabaseModelManage.get_model("role_model")
Expand All @@ -713,6 +714,8 @@ def get_user_members(self, workspace_id):
user_role_relations = user_role_relation_model.objects.filter(
workspace_id=workspace_id, role__type="USER"
).select_related("role", "user")
if nick_name:
user_role_relations = user_role_relations.filter(user__nick_name__contains=nick_name)
user_dict = {}
for relation in user_role_relations:
user_id = relation.user.id
Expand All @@ -726,9 +729,11 @@ def get_user_members(self, workspace_id):
user_dict[user_id]["roles"].append(relation.role.role_name)

# 将字典值转换为列表形式
return list(user_dict.values())
return list(user_dict.values())[:200]
user_list = User.objects.exclude(role=RoleConstants.ADMIN.name)
return [{"id": user.id, "nick_name": user.nick_name, "roles": [RoleConstants.USER.name]} for user in user_list]
if nick_name:
user_list = user_list.filter(nick_name__contains=nick_name)
return [{"id": user.id, "nick_name": user.nick_name, "roles": [RoleConstants.USER.name]} for user in user_list[:200]]

class BatchDelete(serializers.Serializer):
ids = serializers.ListField(required=True, label=_("User IDs"))
Expand Down
3 changes: 2 additions & 1 deletion apps/users/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ class WorkspaceUserMemberView(APIView):
RoleConstants.EXTENDS_USER,
)
def get(self, request: Request, workspace_id):
return result.success(UserManageSerializer().get_user_members(workspace_id))
nick_name = request.query_params.get("nick_name", None)
return result.success(UserManageSerializer().get_user_members(workspace_id, nick_name))


class UserManage(APIView):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import RoleApi from '@/api/admin/system/role'
import type { RoleItem, RolePermission, RolePermissionModule } from '@/api/types'
import type { RoleItem, RolePermission, RolePermissionModule, RolePermissionModuleGroup } from '@/api/types'
import { MsgSuccess } from '@/utils/message'

interface PermissionTableRow {
Expand All @@ -19,6 +19,7 @@ const props = defineProps<{ currentRole: RoleItem }>()
/* 权限数据加载与表格展示 */
const loading = ref(false)
const permissionData = ref<PermissionTableRow[]>([])
const showCategory = ref(true)
const disabled = computed(() => props.currentRole.internal)
const permissionTableKey = computed(() => `${props.currentRole.id}:${disabled.value ? 'readonly' : 'editable'}`)

Expand All @@ -32,8 +33,29 @@ function loadPermissions() {
}

function transformPermissions(modules: RolePermissionModule[]) {
// 后端返回 分类(category) → 分组(group) → 叶子(feature) → 权限 的结构,
// 分类 → “分类”列,分组 → “模块名称”列,叶子 → “操作对象”列,向下展平为表格行
// 后端对普通用户角色(USER)不再返回“分类”层,直接返回 分组 → 叶子 → 权限。
// 分类层的子节点是分组(Group,含 children),扁平结构的子节点是叶子(Feature,含 permission)。
const firstChild = modules[0]?.children?.[0]
const categorized = Boolean(firstChild && Array.isArray(firstChild.children))
showCategory.value = categorized

if (!categorized) {
// 无“分类”层:分组 → “模块名称”列,叶子 → “操作对象”列
const flatModules = modules as unknown as RolePermissionModuleGroup[]
return flatModules.flatMap((group) =>
group.children.map((feature) => ({
id: `${group.id}:${feature.id}`,
categoryId: group.id,
category: group.name,
moduleId: group.id,
module: group.name,
name: feature.name,
permissions: feature.permission,
})),
)
}

// 含“分类”层:分类 → “分类”列,分组 → “模块名称”列,叶子 → “操作对象”列
return modules.flatMap((category) =>
category.children.flatMap((group) =>
group.children.map((feature) => ({
Expand All @@ -50,12 +72,14 @@ function transformPermissions(modules: RolePermissionModule[]) {
}

function permissionTableSpan({ row, rowIndex, columnIndex }: { row: PermissionTableRow; rowIndex: number; columnIndex: number }) {
if (columnIndex === 0) {
// “分类”列存在时,列0为“分类”、列1为“模块名称”;“分类”列隐藏时,列0即为“模块名称”
const moduleColumnIndex = showCategory.value ? 1 : 0
if (showCategory.value && columnIndex === 0) {
// “分类”列按分类纵向合并
const firstRowIndex = permissionData.value.findIndex(({ categoryId }) => categoryId === row.categoryId)
return rowIndex === firstRowIndex ? [permissionData.value.filter(({ categoryId }) => categoryId === row.categoryId).length, 1] : [0, 0]
}
if (columnIndex === 1) {
if (columnIndex === moduleColumnIndex) {
// “模块名称”列按分组纵向合并
const firstRowIndex = permissionData.value.findIndex(({ moduleId }) => moduleId === row.moduleId)
return rowIndex === firstRowIndex ? [permissionData.value.filter(({ moduleId }) => moduleId === row.moduleId).length, 1] : [0, 0]
Expand All @@ -77,7 +101,10 @@ function handlePermissionChange(value: boolean, permission: RolePermission, row:
/* 行选择与全表选择 */
function getPermissionState(permissions: RolePermission[]) {
const checkedCount = permissions.filter(({ enable }) => enable).length
return { checked: permissions.length > 0 && checkedCount === permissions.length, indeterminate: checkedCount > 0 && checkedCount < permissions.length }
return {
checked: permissions.length > 0 && checkedCount === permissions.length,
indeterminate: checkedCount > 0 && checkedCount < permissions.length,
}
}

function handleRowChange(value: boolean, row: PermissionTableRow) {
Expand Down Expand Up @@ -118,23 +145,32 @@ watch(() => props.currentRole.id, loadPermissions, { immediate: true })
:data="permissionData"
v-loading="loading"
>
<el-table-column prop="category" label="分类" width="120" />
<el-table-column v-if="showCategory" prop="category" label="分类" width="120" />
<el-table-column prop="module" label="模块名称" width="150" />
<el-table-column prop="name" label="操作对象" width="150" />
<el-table-column label="权限">
<template #default="{ row }">
<div class="flex-wrap">
<template v-for="permission in row.permissions" :key="permission.id">
<el-checkbox v-model="permission.enable" :disabled="disabled" class="w-30" @change="(value: boolean) => handlePermissionChange(value, permission, row)">{{
permission.name
}}</el-checkbox>
<el-checkbox
v-model="permission.enable"
:disabled="disabled"
class="w-30"
@change="(value: boolean) => handlePermissionChange(value, permission, row)"
>{{ permission.name }}</el-checkbox
>
</template>
</div>
</template>
</el-table-column>
<el-table-column class-name="permission-checkbox-column" label-class-name="permission-checkbox-column" :width="60">
<template #header>
<el-checkbox :model-value="allPermissionState.checked" :indeterminate="allPermissionState.indeterminate" :disabled="disabled" @change="handleCheckAll" />
<el-checkbox
:model-value="allPermissionState.checked"
:indeterminate="allPermissionState.indeterminate"
:disabled="disabled"
@change="handleCheckAll"
/>
</template>

<template #default="{ row }">
Expand Down
Loading