perf: org permission (#3556)

This commit is contained in:
Archer
2025-01-10 10:48:54 +08:00
committed by GitHub
parent 93cf5bb372
commit ed619edd47
23 changed files with 84 additions and 73 deletions

View File

@@ -2,6 +2,7 @@ import { i18nT } from '../../../../web/i18n/utils';
import type { ErrType } from '../errorCode'; import type { ErrType } from '../errorCode';
/* team: 500000 */ /* team: 500000 */
export enum TeamErrEnum { export enum TeamErrEnum {
notUser = 'notUser',
teamOverSize = 'teamOverSize', teamOverSize = 'teamOverSize',
unAuthTeam = 'unAuthTeam', unAuthTeam = 'unAuthTeam',
teamMemberOverSize = 'teamMemberOverSize', teamMemberOverSize = 'teamMemberOverSize',
@@ -27,6 +28,10 @@ export enum TeamErrEnum {
} }
const teamErr = [ const teamErr = [
{
statusText: TeamErrEnum.notUser,
message: i18nT('common:code_error.team_error.not_user')
},
{ {
statusText: TeamErrEnum.teamOverSize, statusText: TeamErrEnum.teamOverSize,
message: i18nT('common:code_error.team_error.over_size') message: i18nT('common:code_error.team_error.over_size')

View File

@@ -1,7 +1,9 @@
export const HUMAN_ICON = `/icon/human.svg`; export const HUMAN_ICON = `/icon/human.svg`;
export const LOGO_ICON = `/icon/logo.svg`; export const LOGO_ICON = `/icon/logo.svg`;
export const HUGGING_FACE_ICON = `/imgs/model/huggingface.svg`; export const HUGGING_FACE_ICON = `/imgs/model/huggingface.svg`;
export const DEFAULT_TEAM_AVATAR = `/imgs/avatar/defaultTeamAvatar.svg`; export const DEFAULT_TEAM_AVATAR = `/imgs/avatar/defaultTeamAvatar.svg`;
export const DEFAULT_ORG_AVATAR = '/imgs/avatar/defaultOrgAvatar.svg'; export const DEFAULT_ORG_AVATAR = '/imgs/avatar/defaultOrgAvatar.svg';
export const DEFAULT_USER_AVATAR = '/imgs/avatar/BlueAvatar.svg';
export const isProduction = process.env.NODE_ENV === 'production'; export const isProduction = process.env.NODE_ENV === 'production';

View File

@@ -153,7 +153,7 @@ export const getClbsAndGroupsWithInfo = async ({
}) })
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({ .populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({
path: 'tmb', path: 'tmb',
select: 'name userId', select: 'name userId role',
populate: { populate: {
path: 'user', path: 'user',
select: 'avatar' select: 'avatar'

View File

@@ -4,7 +4,6 @@ import type { ClientSession } from 'mongoose';
import { MongoOrgModel } from './orgSchema'; import { MongoOrgModel } from './orgSchema';
import { MongoOrgMemberModel } from './orgMemberSchema'; import { MongoOrgMemberModel } from './orgMemberSchema';
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant'; import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
import { getNanoid } from '@fastgpt/global/common/string/tools';
export const getOrgsByTmbId = async ({ teamId, tmbId }: { teamId: string; tmbId: string }) => export const getOrgsByTmbId = async ({ teamId, tmbId }: { teamId: string; tmbId: string }) =>
MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean(); MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean();
@@ -16,23 +15,29 @@ export const getOrgIdSetWithParentByTmbId = async ({
teamId: string; teamId: string;
tmbId: string; tmbId: string;
}) => { }) => {
const orgMembers = await MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId') const orgMembers = await MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean();
.populate<{ org: { path: string } }>('org', 'path')
.lean();
const orgIds = new Set<string>(); const orgIds = Array.from(new Set(orgMembers.map((item) => String(item.orgId))));
const orgs = await MongoOrgModel.find({ _id: { $in: orgIds } }, 'path').lean();
for (const orgMember of orgMembers) { const pathIdList = new Set<string>(
orgIds.add(String(orgMember.orgId)); orgs
.map((org) => {
const pathIdList = org.path.split('/').filter(Boolean);
return pathIdList;
})
.flat()
);
const parentOrgs = await MongoOrgModel.find(
{
teamId,
pathId: { $in: Array.from(pathIdList) }
},
'_id'
).lean();
const parentOrgIds = parentOrgs.map((item) => String(item._id));
// Add parent org return new Set([...orgIds, ...parentOrgIds]);
const parentIds = orgMember.org.path.split('/').filter(Boolean);
for (const parentId of parentIds) {
orgIds.add(parentId);
}
}
return orgIds;
}; };
export const getChildrenByOrg = async ({ export const getChildrenByOrg = async ({

View File

@@ -349,6 +349,8 @@ export const iconPaths = {
history: () => import('./icons/history.svg'), history: () => import('./icons/history.svg'),
infoRounded: () => import('./icons/infoRounded.svg'), infoRounded: () => import('./icons/infoRounded.svg'),
kbTest: () => import('./icons/kbTest.svg'), kbTest: () => import('./icons/kbTest.svg'),
key: () => import('./icons/key.svg'),
keyPrimary: () => import('./icons/keyPrimary.svg'),
menu: () => import('./icons/menu.svg'), menu: () => import('./icons/menu.svg'),
minus: () => import('./icons/minus.svg'), minus: () => import('./icons/minus.svg'),
'modal/AddClb': () => import('./icons/modal/AddClb.svg'), 'modal/AddClb': () => import('./icons/modal/AddClb.svg'),
@@ -360,7 +362,6 @@ export const iconPaths = {
'modal/selectSource': () => import('./icons/modal/selectSource.svg'), 'modal/selectSource': () => import('./icons/modal/selectSource.svg'),
'modal/setting': () => import('./icons/modal/setting.svg'), 'modal/setting': () => import('./icons/modal/setting.svg'),
'modal/teamPlans': () => import('./icons/modal/teamPlans.svg'), 'modal/teamPlans': () => import('./icons/modal/teamPlans.svg'),
'modal/key': () => import('./icons/modal/key.svg'),
'model/BAAI': () => import('./icons/model/BAAI.svg'), 'model/BAAI': () => import('./icons/model/BAAI.svg'),
'model/alicloud': () => import('./icons/model/alicloud.svg'), 'model/alicloud': () => import('./icons/model/alicloud.svg'),
'model/baichuan': () => import('./icons/model/baichuan.svg'), 'model/baichuan': () => import('./icons/model/baichuan.svg'),
@@ -412,7 +413,6 @@ export const iconPaths = {
'support/bill/shoppingCart': () => import('./icons/support/bill/shoppingCart.svg'), 'support/bill/shoppingCart': () => import('./icons/support/bill/shoppingCart.svg'),
'support/bill/wallet': () => import('./icons/support/bill/wallet.svg'), 'support/bill/wallet': () => import('./icons/support/bill/wallet.svg'),
'support/outlink/apikeyFill': () => import('./icons/support/outlink/apikeyFill.svg'), 'support/outlink/apikeyFill': () => import('./icons/support/outlink/apikeyFill.svg'),
'support/outlink/apikeyLight': () => import('./icons/support/outlink/apikeyLight.svg'),
'support/outlink/iframeLight': () => import('./icons/support/outlink/iframeLight.svg'), 'support/outlink/iframeLight': () => import('./icons/support/outlink/iframeLight.svg'),
'support/outlink/share': () => import('./icons/support/outlink/share.svg'), 'support/outlink/share': () => import('./icons/support/outlink/share.svg'),
'support/outlink/shareLight': () => import('./icons/support/outlink/shareLight.svg'), 'support/outlink/shareLight': () => import('./icons/support/outlink/shareLight.svg'),
@@ -420,7 +420,6 @@ export const iconPaths = {
'support/permission/privateLight': () => import('./icons/support/permission/privateLight.svg'), 'support/permission/privateLight': () => import('./icons/support/permission/privateLight.svg'),
'support/permission/publicLight': () => import('./icons/support/permission/publicLight.svg'), 'support/permission/publicLight': () => import('./icons/support/permission/publicLight.svg'),
'support/team/group': () => import('./icons/support/team/group.svg'), 'support/team/group': () => import('./icons/support/team/group.svg'),
'support/team/key': () => import('./icons/support/team/key.svg'),
'support/team/memberLight': () => import('./icons/support/team/memberLight.svg'), 'support/team/memberLight': () => import('./icons/support/team/memberLight.svg'),
'support/usage/usageRecordLight': () => import('./icons/support/usage/usageRecordLight.svg'), 'support/usage/usageRecordLight': () => import('./icons/support/usage/usageRecordLight.svg'),
'support/user/informLight': () => import('./icons/support/user/informLight.svg'), 'support/user/informLight': () => import('./icons/support/user/informLight.svg'),

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,8 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M12.2109 12.3945C14.7199 12.3945 16.75 10.3639 16.75 7.8636C16.75 5.36333 14.7199 3.33268 12.2109 3.33268C9.70193 3.33268 7.67177 5.36333 7.67177 7.8636C7.67177 8.28211 7.72817 8.68482 7.83285 9.06581C8.02882 9.77912 7.86557 10.6064 7.27775 11.1959L3.33532 15.1497L3.33531 16.666H5.02967L5.07424 16.6215L5.52315 18.2969C5.43021 18.3205 5.33398 18.3327 5.23648 18.3327H2.66864C2.11635 18.3327 1.66864 17.885 1.66864 17.3327L1.66865 14.943C1.66865 14.6342 1.79111 14.3379 2.00918 14.1192L6.09755 10.0191C6.23063 9.88562 6.27567 9.6891 6.22574 9.50736C6.08191 8.98387 6.00511 8.43269 6.00511 7.8636C6.00511 4.44077 8.78354 1.66602 12.2109 1.66602C15.6383 1.66602 18.4167 4.44077 18.4167 7.8636C18.4167 11.2864 15.6383 14.0612 12.2109 14.0612C11.651 14.0612 11.1084 13.9871 10.5923 13.8483C10.4113 13.7996 10.216 13.8449 10.0833 13.9774L8.90842 15.1498L7.72991 13.9713L8.90598 12.7977C9.49218 12.2127 10.3143 12.0475 11.0254 12.2389C11.4015 12.34 11.7985 12.3945 12.2109 12.3945Z" />
<path
d="M6.13784 16.7874H6.85411C7.26832 16.7874 7.60417 16.4516 7.60417 16.0374L7.60411 16.0283V15.3211H8.43437C8.84858 15.3211 9.18437 14.9853 9.18437 14.5711C9.18437 14.1569 8.84858 13.8211 8.43437 13.8211H6.96804C6.94888 13.8211 6.92989 13.8218 6.9111 13.8232C6.89229 13.8218 6.87328 13.8211 6.85411 13.8211C6.4399 13.8211 6.10411 14.1569 6.10411 14.5711V15.2874H5.38784C4.97363 15.2874 4.63784 15.6232 4.63784 16.0374V17.569C4.63784 17.9832 4.97363 18.319 5.38784 18.319C5.80206 18.319 6.13784 17.9832 6.13784 17.569V16.7874Z" />
<path
d="M14.4642 7.1898C14.4642 8.11027 13.718 8.85647 12.7975 8.85647C11.877 8.85647 11.1308 8.11027 11.1308 7.1898C11.1308 6.26932 11.877 5.52313 12.7975 5.52313C13.718 5.52313 14.4642 6.26932 14.4642 7.1898Z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -82,6 +82,7 @@
"code_error.team_error.group_name_duplicate": "Duplicate group name", "code_error.team_error.group_name_duplicate": "Duplicate group name",
"code_error.team_error.group_name_empty": "Group name cannot be empty", "code_error.team_error.group_name_empty": "Group name cannot be empty",
"code_error.team_error.group_not_exist": "Group does not exist", "code_error.team_error.group_not_exist": "Group does not exist",
"code_error.team_error.not_user": "The member cannot be found",
"code_error.team_error.org_member_duplicated": "Duplicate organization member", "code_error.team_error.org_member_duplicated": "Duplicate organization member",
"code_error.team_error.org_member_not_exist": "Organization member does not exist", "code_error.team_error.org_member_not_exist": "Organization member does not exist",
"code_error.team_error.org_not_exist": "Organization does not exist", "code_error.team_error.org_not_exist": "Organization does not exist",

View File

@@ -105,7 +105,7 @@
"team.group.role.admin": "administrator", "team.group.role.admin": "administrator",
"team.group.role.member": "member", "team.group.role.member": "member",
"team.group.role.owner": "owner", "team.group.role.owner": "owner",
"team.group.search_placeholder": "Search member/group/org name", "search_group_org_user": "Search member/group/org name",
"team.group.set_as_admin": "Set as administrator", "team.group.set_as_admin": "Set as administrator",
"team.group.toast.can_not_delete_owner": "Owner cannot be deleted, please transfer first", "team.group.toast.can_not_delete_owner": "Owner cannot be deleted, please transfer first",
"team.group.transfer_owner": "transfer owner", "team.group.transfer_owner": "transfer owner",

View File

@@ -86,6 +86,7 @@
"code_error.team_error.group_name_duplicate": "群组名称重复", "code_error.team_error.group_name_duplicate": "群组名称重复",
"code_error.team_error.group_name_empty": "群组名称不能为空", "code_error.team_error.group_name_empty": "群组名称不能为空",
"code_error.team_error.group_not_exist": "群组不存在", "code_error.team_error.group_not_exist": "群组不存在",
"code_error.team_error.not_user": "找不到该成员",
"code_error.team_error.org_member_duplicated": "重复的部门成员", "code_error.team_error.org_member_duplicated": "重复的部门成员",
"code_error.team_error.org_member_not_exist": "部门成员不存在", "code_error.team_error.org_member_not_exist": "部门成员不存在",
"code_error.team_error.org_not_exist": "部门不存在", "code_error.team_error.org_not_exist": "部门不存在",

View File

@@ -105,7 +105,7 @@
"team.group.role.admin": "管理员", "team.group.role.admin": "管理员",
"team.group.role.member": "成员", "team.group.role.member": "成员",
"team.group.role.owner": "所有者", "team.group.role.owner": "所有者",
"team.group.search_placeholder": "搜索成员/部门/群组名称", "search_group_org_user": "搜索成员/部门/群组名称",
"team.group.set_as_admin": "设为管理员", "team.group.set_as_admin": "设为管理员",
"team.group.toast.can_not_delete_owner": "不能删除所有者, 请先转让", "team.group.toast.can_not_delete_owner": "不能删除所有者, 请先转让",
"team.group.transfer_owner": "转让所有者", "team.group.transfer_owner": "转让所有者",

View File

@@ -82,6 +82,7 @@
"code_error.team_error.group_name_duplicate": "群組名稱重複", "code_error.team_error.group_name_duplicate": "群組名稱重複",
"code_error.team_error.group_name_empty": "群組名稱不能為空", "code_error.team_error.group_name_empty": "群組名稱不能為空",
"code_error.team_error.group_not_exist": "群組不存在", "code_error.team_error.group_not_exist": "群組不存在",
"code_error.team_error.not_user": "找不到該成員",
"code_error.team_error.org_member_duplicated": "重複的組織成員", "code_error.team_error.org_member_duplicated": "重複的組織成員",
"code_error.team_error.org_member_not_exist": "組織成員不存在", "code_error.team_error.org_member_not_exist": "組織成員不存在",
"code_error.team_error.org_not_exist": "組織不存在", "code_error.team_error.org_not_exist": "組織不存在",

View File

@@ -105,7 +105,7 @@
"team.group.role.admin": "管理員", "team.group.role.admin": "管理員",
"team.group.role.member": "成員", "team.group.role.member": "成員",
"team.group.role.owner": "擁有者", "team.group.role.owner": "擁有者",
"team.group.search_placeholder": "搜尋成員/部門/群組名稱", "search_group_org_user": "搜尋成員/部門/群組名稱",
"team.group.set_as_admin": "設為管理員", "team.group.set_as_admin": "設為管理員",
"team.group.toast.can_not_delete_owner": "無法刪除擁有者,請先轉移擁有權", "team.group.toast.can_not_delete_owner": "無法刪除擁有者,請先轉移擁有權",
"team.group.transfer_owner": "轉移擁有者", "team.group.transfer_owner": "轉移擁有者",

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1700983497588" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6628" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M698.483573 594.905936A287.506808 287.506808 0 1 1 984.611923 306.020671v1.181535a287.309885 287.309885 0 0 1-286.12835 287.70373z" fill="#FFFFFF" p-id="6629"></path><path d="M698.483573 39.387645A267.814561 267.814561 0 1 1 433.229005 308.777585v-1.575379A267.420716 267.420716 0 0 1 698.483573 39.387645m0-39.384494A307.199055 307.199055 0 1 0 1004.30417 308.580663v-1.378457A306.411365 306.411365 0 0 0 698.680495 0.003151z" fill="#007FB7" p-id="6630"></path><path d="M787.689452 236.310116m-78.768988 0a78.768988 78.768988 0 1 0 157.537977 0 78.768988 78.768988 0 1 0-157.537977 0Z" fill="#D1EBF2" p-id="6631"></path><path d="M787.689452 177.233375a59.076741 59.076741 0 1 1-59.076741 59.076741 59.076741 59.076741 0 0 1 59.076741-59.076741m0-39.384495a98.461236 98.461236 0 1 0 98.461236 98.461236 98.461236 98.461236 0 0 0-98.461236-98.461236z" fill="#007FB7" p-id="6632"></path><path d="M39.384062 974.57246v-113.033499l390.300338-392.466484 162.067194 108.701204-116.381181 58.682896v124.455002l-135.876505 5.316906v131.150366l-127.014993 4.923062-65.772106 99.248925L39.384062 974.57246z" fill="#D1EBF2" p-id="6633"></path><path d="M433.229005 494.475475l120.713474 80.935136-75.421306 38.006037-21.661472 10.830736v118.153482l-98.461235 3.741527-38.793727 2.166148v131.347288l-98.461236 3.741527h-19.692247l-11.224581 16.73841L137.845298 979.101677l-78.768988-19.692247v-89.796647l374.152695-374.152695m-5.119985-50.805998L19.691815 853.268218v136.467272L155.56832 1024l67.938253-102.399685 135.876505-5.316907v-131.150365l135.876505-5.316907v-131.347288L630.151476 580.333673l-203.027068-136.664195z" fill="#007FB7" p-id="6634"></path></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -250,7 +250,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
<MyModal <MyModal
isOpen={!!apiKey} isOpen={!!apiKey}
w={['400px', '600px']} w={['400px', '600px']}
iconSrc="/imgs/modal/key.svg" iconSrc="keyPrimary"
title={ title={
<Box> <Box>
<Box fontWeight={'bold'}>{t('common:support.openapi.New api key')}</Box> <Box fontWeight={'bold'}>{t('common:support.openapi.New api key')}</Box>
@@ -330,7 +330,7 @@ function EditKeyModal({
return ( return (
<MyModal <MyModal
isOpen={true} isOpen={true}
iconSrc="/imgs/modal/key.svg" iconSrc="keyPrimary"
title={isEdit ? t('publish:edit_api_key') : t('publish:create_api_key')} title={isEdit ? t('publish:edit_api_key') : t('publish:create_api_key')}
> >
<ModalBody> <ModalBody>

View File

@@ -44,7 +44,7 @@ const ConfigPerModal = ({
<> <>
<MyModal <MyModal
isOpen isOpen
iconSrc="/imgs/modal/key.svg" iconSrc="keyPrimary"
onClose={onClose} onClose={onClose}
title={t('common:permission.Permission config')} title={t('common:permission.Permission config')}
> >

View File

@@ -22,7 +22,11 @@ import { useTranslation } from 'next-i18next';
import { useMemo, useRef, useState } from 'react'; import { useMemo, useRef, useState } from 'react';
import PermissionSelect from './PermissionSelect'; import PermissionSelect from './PermissionSelect';
import PermissionTags from './PermissionTags'; import PermissionTags from './PermissionTags';
import { DEFAULT_ORG_AVATAR, DEFAULT_TEAM_AVATAR } from '@fastgpt/global/common/system/constants'; import {
DEFAULT_ORG_AVATAR,
DEFAULT_TEAM_AVATAR,
DEFAULT_USER_AVATAR
} from '@fastgpt/global/common/system/constants';
import Path from '@/components/common/folder/Path'; import Path from '@/components/common/folder/Path';
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant'; import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type'; import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
@@ -43,8 +47,7 @@ function MemberModal({
addPermissionOnly?: boolean; addPermissionOnly?: boolean;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, myGroups, loadAndGetOrgs } = const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, loadAndGetOrgs } = useUserStore();
useUserStore();
const collaboratorList = useContextSelector(CollaboratorContext, (v) => v.collaboratorList); const collaboratorList = useContextSelector(CollaboratorContext, (v) => v.collaboratorList);
@@ -112,10 +115,11 @@ function MemberModal({
const filterMembers = useMemo(() => { const filterMembers = useMemo(() => {
if (searchText) return members.filter((item) => item.memberName.includes(searchText)); if (searchText) return members.filter((item) => item.memberName.includes(searchText));
if (!searchText && filterClass !== 'member' && filterClass !== 'org') return []; if (!searchText && filterClass !== 'member' && filterClass !== 'org') return [];
if (filterClass === 'org') {
if (!currentOrg) return []; if (currentOrg && filterClass === 'org') {
return members.filter((item) => currentOrg.members.find((v) => v.tmbId === item.tmbId)); return members.filter((item) => currentOrg.members.find((v) => v.tmbId === item.tmbId));
} }
return members; return members;
}, [members, searchText, filterClass, currentOrg]); }, [members, searchText, filterClass, currentOrg]);
@@ -123,14 +127,15 @@ function MemberModal({
const filterGroups = useMemo(() => { const filterGroups = useMemo(() => {
if (searchText) return groups.filter((item) => item.name.includes(searchText)); if (searchText) return groups.filter((item) => item.name.includes(searchText));
if (!searchText && filterClass !== 'group') return []; if (!searchText && filterClass !== 'group') return [];
return groups.filter((item) => {
return !myGroups.find((i) => String(i._id) === String(item._id)); return groups;
}); }, [groups, searchText, filterClass]);
}, [groups, searchText, filterClass, myGroups]);
const permissionList = useContextSelector(CollaboratorContext, (v) => v.permissionList); const permissionList = useContextSelector(CollaboratorContext, (v) => v.permissionList);
const getPerLabelList = useContextSelector(CollaboratorContext, (v) => v.getPerLabelList); const getPerLabelList = useContextSelector(CollaboratorContext, (v) => v.getPerLabelList);
const [selectedPermission, setSelectedPermission] = useState<number>(); const [selectedPermission, setSelectedPermission] = useState<number | undefined>(
permissionList?.read?.value
);
const perLabel = useMemo(() => { const perLabel = useMemo(() => {
if (selectedPermission === undefined) return ''; if (selectedPermission === undefined) return '';
return getPerLabelList(selectedPermission!).join('、'); return getPerLabelList(selectedPermission!).join('、');
@@ -157,7 +162,7 @@ function MemberModal({
); );
const entryList = useRef([ const entryList = useRef([
{ label: t('user:team.group.members'), icon: '/imgs/avatar/BlueAvatar.svg', value: 'member' }, { label: t('user:team.group.members'), icon: DEFAULT_USER_AVATAR, value: 'member' },
{ label: t('user:team.org.org'), icon: DEFAULT_ORG_AVATAR, value: 'org' }, { label: t('user:team.org.org'), icon: DEFAULT_ORG_AVATAR, value: 'org' },
{ label: t('user:team.group.group'), icon: DEFAULT_TEAM_AVATAR, value: 'group' } { label: t('user:team.group.group'), icon: DEFAULT_TEAM_AVATAR, value: 'group' }
]); ]);
@@ -201,7 +206,7 @@ function MemberModal({
<MyModal <MyModal
isOpen isOpen
onClose={onClose} onClose={onClose}
iconSrc={addOnly ? 'modal/key' : 'modal/AddClb'} iconSrc={addOnly ? 'keyPrimary' : 'modal/AddClb'}
title={addOnly ? t('user:team.add_permission') : t('user:team.add_collaborator')} title={addOnly ? t('user:team.add_permission') : t('user:team.add_collaborator')}
minW="800px" minW="800px"
h={'100%'} h={'100%'}
@@ -225,7 +230,7 @@ function MemberModal({
p="4" p="4"
> >
<SearchInput <SearchInput
placeholder={t('user:team.group.search_placeholder')} placeholder={t('user:search_group_org_user')}
bgColor="myGray.50" bgColor="myGray.50"
onChange={(e) => setSearchText(e.target.value)} onChange={(e) => setSearchText(e.target.value)}
/> />
@@ -364,11 +369,9 @@ function MemberModal({
<HStack ml="2" w="full" gap="5px"> <HStack ml="2" w="full" gap="5px">
<Text>{org.name}</Text> <Text>{org.name}</Text>
{org.count && ( {org.count && (
<>
<Tag size="sm" my="auto"> <Tag size="sm" my="auto">
{org.count} {org.count}
</Tag> </Tag>
</>
)} )}
</HStack> </HStack>
<PermissionTags <PermissionTags

View File

@@ -94,7 +94,7 @@ const AccountContainer = ({
...(userInfo?.team?.permission.hasManagePer ...(userInfo?.team?.permission.hasManagePer
? [ ? [
{ {
icon: 'support/outlink/apikeyLight', icon: 'key',
label: t('account:api_key'), label: t('account:api_key'),
value: TabEnum.apikey value: TabEnum.apikey
} }

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react'; import React, { useMemo } from 'react';
import { import {
Box, Box,
Checkbox, Checkbox,
@@ -39,7 +39,6 @@ import CollaboratorContextProvider, {
CollaboratorContext CollaboratorContext
} from '@/components/support/permission/MemberManager/context'; } from '@/components/support/permission/MemberManager/context';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { CollaboratorItemType } from '@fastgpt/global/support/permission/collaborator'; import { CollaboratorItemType } from '@fastgpt/global/support/permission/collaborator';
@@ -121,6 +120,11 @@ function PermissionManage({
useRequest2(onDelOneCollaborator); useRequest2(onDelOneCollaborator);
const userManage = userInfo?.permission.hasManagePer; const userManage = userInfo?.permission.hasManagePer;
const hasDeletePer = (per: TeamPermission) => {
if (userInfo?.permission.isOwner) return true;
if (userManage && !per.hasManagePer) return true;
return false;
};
return ( return (
<> <>
@@ -128,7 +132,7 @@ function PermissionManage({
{Tabs} {Tabs}
<Box ml="auto"> <Box ml="auto">
{/* <SearchInput {/* <SearchInput
placeholder={t('user:team.group.search_placeholder')} placeholder={t('user:search_group_org_user')}
w="200px" w="200px"
value={searchKey} value={searchKey}
onChange={(e) => setSearchKey(e.target.value)} onChange={(e) => setSearchKey(e.target.value)}
@@ -236,10 +240,9 @@ function PermissionManage({
/> />
</Box> </Box>
</Td> </Td>
{userManage &&
!member.permission.isOwner &&
userInfo?.team.tmbId !== member.tmbId && (
<Td> <Td>
{hasDeletePer(member.permission) &&
userInfo?.team.tmbId !== member.tmbId && (
<Box mx="auto" w="fit-content"> <Box mx="auto" w="fit-content">
<MyIconButton <MyIconButton
icon="common/trash" icon="common/trash"
@@ -248,8 +251,8 @@ function PermissionManage({
} }
/> />
</Box> </Box>
</Td>
)} )}
</Td>
</Tr> </Tr>
))} ))}
</> </>
@@ -305,16 +308,16 @@ function PermissionManage({
/> />
</Box> </Box>
</Td> </Td>
{userInfo?.permission.isOwner && (
<Td> <Td>
{hasDeletePer(org.permission) && (
<Box mx="auto" w="fit-content"> <Box mx="auto" w="fit-content">
<MyIconButton <MyIconButton
icon="common/trash" icon="common/trash"
onClick={() => onDeleteMemberPermission({ orgId: org.orgId! })} onClick={() => onDeleteMemberPermission({ orgId: org.orgId! })}
/> />
</Box> </Box>
</Td>
)} )}
</Td>
</Tr> </Tr>
))} ))}
</> </>
@@ -385,16 +388,16 @@ function PermissionManage({
/> />
</Box> </Box>
</Td> </Td>
{userInfo?.permission.isOwner && (
<Td> <Td>
{hasDeletePer(group.permission) && (
<Box mx="auto" w="fit-content"> <Box mx="auto" w="fit-content">
<MyIconButton <MyIconButton
icon="common/trash" icon="common/trash"
onClick={() => onDeleteMemberPermission({ groupId: group.groupId! })} onClick={() => onDeleteMemberPermission({ groupId: group.groupId! })}
/> />
</Box> </Box>
</Td>
)} )}
</Td>
</Tr> </Tr>
))} ))}
</> </>

View File

@@ -66,7 +66,7 @@ const AppCard = ({ showSaveStatus, isSaved }: { showSaveStatus: boolean; isSaved
cursor={'pointer'} cursor={'pointer'}
onClick={onOpenInfoEdit} onClick={onOpenInfoEdit}
> >
<MyIcon name={'support/team/key'} w={'16px'} mr={2} /> <MyIcon name={'key'} w={'16px'} mr={2} />
<Box fontSize={'sm'}>{t('app:Role_setting')}</Box> <Box fontSize={'sm'}>{t('app:Role_setting')}</Box>
</MyBox> </MyBox>
<Box w={'full'} h={'1px'} bg={'myGray.200'} my={1} /> <Box w={'full'} h={'1px'} bg={'myGray.200'} my={1} />

View File

@@ -344,7 +344,7 @@ const ListItem = () => {
...(app.permission.hasManagePer ...(app.permission.hasManagePer
? [ ? [
{ {
icon: 'support/team/key', icon: 'key',
type: 'grayBg' as MenuItemType, type: 'grayBg' as MenuItemType,
label: t('common:permission.Permission'), label: t('common:permission.Permission'),
onClick: () => setEditPerAppIndex(index) onClick: () => setEditPerAppIndex(index)

View File

@@ -350,7 +350,7 @@ function List() {
...(dataset.permission.hasManagePer ...(dataset.permission.hasManagePer
? [ ? [
{ {
icon: 'support/team/key', icon: 'key',
label: t('common:permission.Permission'), label: t('common:permission.Permission'),
onClick: () => setEditPerDatasetIndex(index) onClick: () => setEditPerDatasetIndex(index)
} }