feat: sync org from wecom, pref: member list pagination (#3549)

* feat: sync org

* chore: fe

* chore: loading

* chore: type

* pref: team member list change to pagination. Edit a sort of list apis.

* feat: member update avatar

* chore: user avatar move to tmb

* chore: init scripts move user avatar

* chore: sourceMember

* fix: list api sourceMember

* fix: member sync

* fix: pagination

* chore: adjust code

* chore: move changeOwner to pro

* chore: init v4819 script

* chore: adjust code

* chore: UserBox
This commit is contained in:
Finley Ge
2025-01-13 11:22:20 +08:00
committed by archer
parent a8d456f448
commit ec0cef09a2
73 changed files with 883 additions and 757 deletions

View File

@@ -40,7 +40,7 @@ export type FastGPTConfigFileType = {
export type FastGPTFeConfigsType = { export type FastGPTFeConfigsType = {
show_emptyChat?: boolean; show_emptyChat?: boolean;
register_method?: ['email' | 'phone']; register_method?: ['email' | 'phone' | 'sync'];
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
find_password_method?: ['email' | 'phone']; find_password_method?: ['email' | 'phone'];
bind_notification_method?: ['email' | 'phone']; bind_notification_method?: ['email' | 'phone'];
@@ -76,7 +76,6 @@ export type FastGPTFeConfigsType = {
wecom?: { wecom?: {
corpid?: string; corpid?: string;
agentid?: string; agentid?: string;
secret?: string;
}; };
microsoft?: { microsoft?: {
clientId?: string; clientId?: string;

View File

@@ -12,8 +12,9 @@ import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/use
import { StoreEdgeItemType } from '../workflow/type/edge'; import { StoreEdgeItemType } from '../workflow/type/edge';
import { AppPermission } from '../../support/permission/app/controller'; import { AppPermission } from '../../support/permission/app/controller';
import { ParentIdType } from '../../common/parentFolder/type'; import { ParentIdType } from '../../common/parentFolder/type';
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant'; import { FlowNodeInputTypeEnum } from '../../core/workflow/node/constant';
import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type'; import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type';
import { SourceMemberType } from '../../support/user/type';
export type AppSchema = { export type AppSchema = {
_id: string; _id: string;
@@ -63,6 +64,7 @@ export type AppListItemType = {
permission: AppPermission; permission: AppPermission;
inheritPermission?: boolean; inheritPermission?: boolean;
private?: boolean; private?: boolean;
sourceMember: SourceMemberType;
}; };
export type AppDetailType = AppSchema & { export type AppDetailType = AppSchema & {

View File

@@ -1,5 +1,7 @@
import { TeamMemberStatusEnum } from 'support/user/team/constant';
import { StoreEdgeItemType } from '../workflow/type/edge'; import { StoreEdgeItemType } from '../workflow/type/edge';
import { AppChatConfigType, AppSchema } from './type'; import { AppChatConfigType, AppSchema } from './type';
import { SourceMemberType } from 'support/user/type';
export type AppVersionSchemaType = { export type AppVersionSchemaType = {
_id: string; _id: string;
@@ -20,4 +22,5 @@ export type VersionListItemType = {
time: Date; time: Date;
isPublish: boolean | undefined; isPublish: boolean | undefined;
tmbId: string; tmbId: string;
sourceMember: SourceMemberType;
}; };

View File

@@ -11,6 +11,7 @@ import {
import { DatasetPermission } from '../../support/permission/dataset/controller'; import { DatasetPermission } from '../../support/permission/dataset/controller';
import { Permission } from '../../support/permission/controller'; import { Permission } from '../../support/permission/controller';
import { APIFileServer, FeishuServer, YuqueServer } from './apiDataset'; import { APIFileServer, FeishuServer, YuqueServer } from './apiDataset';
import { SourceMemberType } from 'support/user/type';
export type DatasetSchemaType = { export type DatasetSchemaType = {
_id: string; _id: string;
@@ -165,6 +166,7 @@ export type DatasetListItemType = {
vectorModel: VectorModelItemType; vectorModel: VectorModelItemType;
inheritPermission: boolean; inheritPermission: boolean;
private?: boolean; private?: boolean;
sourceMember?: SourceMemberType;
}; };
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & { export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {

View File

@@ -89,6 +89,7 @@ export type NodeTemplateListItemType = {
hasTokenFee?: boolean; // 是否配置积分 hasTokenFee?: boolean; // 是否配置积分
instructions?: string; // 使用说明 instructions?: string; // 使用说明
courseUrl?: string; // 教程链接 courseUrl?: string; // 教程链接
sourceMember?: SourceMember;
}; };
export type NodeTemplateListType = { export type NodeTemplateListType = {

View File

@@ -12,6 +12,7 @@ export type CreateTeamProps = {
avatar?: string; avatar?: string;
defaultTeam?: boolean; defaultTeam?: boolean;
memberName?: string; memberName?: string;
memberAvatar?: string;
}; };
export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & { export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & {
name?: string; name?: string;

View File

@@ -5,8 +5,6 @@ export const OrgMemberCollectionName = 'team_org_members';
export const getOrgChildrenPath = (org: OrgSchemaType) => `${org.path}/${org.pathId}`; export const getOrgChildrenPath = (org: OrgSchemaType) => `${org.path}/${org.pathId}`;
// export enum OrgMemberRole { export enum SyncOrgSourceEnum {
// owner = 'owner', wecom = 'wecom'
// admin = 'admin', }
// member = 'member'
// }

View File

@@ -44,6 +44,7 @@ export type TeamMemberSchema = {
name: string; name: string;
role: `${TeamMemberRoleEnum}`; role: `${TeamMemberRoleEnum}`;
status: `${TeamMemberStatusEnum}`; status: `${TeamMemberStatusEnum}`;
avatar: string;
defaultTeam: boolean; defaultTeam: boolean;
}; };

View File

@@ -1,12 +1,12 @@
import { TeamPermission } from '../permission/user/controller'; import { TeamPermission } from '../permission/user/controller';
import { UserStatusEnum } from './constant'; import { UserStatusEnum } from './constant';
import { TeamMemberStatusEnum } from './team/constant';
import { TeamTmbItemType } from './team/type'; import { TeamTmbItemType } from './team/type';
export type UserModelSchema = { export type UserModelSchema = {
_id: string; _id: string;
username: string; username: string;
password: string; password: string;
avatar: string;
promotionRate: number; promotionRate: number;
inviterId?: string; inviterId?: string;
openaiKey: string; openaiKey: string;
@@ -22,7 +22,7 @@ export type UserModelSchema = {
export type UserType = { export type UserType = {
_id: string; _id: string;
username: string; username: string;
avatar: string; avatar: string; // it should be team member's avatar after 4.8.18
timezone: string; timezone: string;
promotionRate: UserModelSchema['promotionRate']; promotionRate: UserModelSchema['promotionRate'];
team: TeamTmbItemType; team: TeamTmbItemType;
@@ -30,3 +30,9 @@ export type UserType = {
notificationAccount?: string; notificationAccount?: string;
permission: TeamPermission; permission: TeamPermission;
}; };
export type SourceMemberType = {
name: string;
avatar: string;
status: `${TeamMemberStatusEnum}`;
};

View File

@@ -0,0 +1,21 @@
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { ApiRequestProps } from '../../type/next';
export function parsePaginationRequest(req: ApiRequestProps) {
const {
pageSize = 10,
pageNum = 1,
offset = 0
} = Object.keys(req.body).includes('pageSize')
? req.body
: Object.keys(req.query).includes('pageSize')
? req.query
: {};
if (!pageSize || (pageNum === undefined && offset === undefined)) {
throw new Error(CommonErrEnum.missingParams);
}
return {
pageSize: Number(pageSize),
offset: offset ? Number(offset) : (Number(pageNum) - 1) * Number(pageSize)
};
}

View File

@@ -2,7 +2,7 @@ import type { NextApiResponse, NextApiRequest } from 'next';
import NextCors from 'nextjs-cors'; import NextCors from 'nextjs-cors';
export async function withNextCors(req: NextApiRequest, res: NextApiResponse) { export async function withNextCors(req: NextApiRequest, res: NextApiResponse) {
const methods = ['GET', 'eHEAD', 'PUT', 'PATCH', 'POST', 'DELETE']; const methods = ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'];
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(','); const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',');
const origin = req.headers.origin; const origin = req.headers.origin;

View File

@@ -17,7 +17,6 @@ import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type'; import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type'; import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
import { UserModelSchema } from '@fastgpt/global/support/user/type';
import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type'; import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
import { getOrgIdSetWithParentByTmbId } from './org/controllers'; import { getOrgIdSetWithParentByTmbId } from './org/controllers';
@@ -151,13 +150,9 @@ export const getClbsAndGroupsWithInfo = async ({
$exists: true $exists: true
} }
}) })
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({ .populate<{ tmb: TeamMemberSchema }>({
path: 'tmb', path: 'tmb',
select: 'name userId role', select: 'name userId avatar'
populate: {
path: 'user',
select: 'avatar'
}
}) })
.lean(), .lean(),
MongoResourcePermission.find({ MongoResourcePermission.find({

View File

@@ -41,7 +41,7 @@ export async function getUserDetail({
return { return {
_id: user._id, _id: user._id,
username: user.username, username: user.username,
avatar: user.avatar, avatar: tmb.avatar,
timezone: user.timezone, timezone: user.timezone,
promotionRate: user.promotionRate, promotionRate: user.promotionRate,
team: tmb, team: tmb,

View File

@@ -3,7 +3,6 @@ const { Schema } = connectionMongo;
import { hashStr } from '@fastgpt/global/common/string/tools'; import { hashStr } from '@fastgpt/global/common/string/tools';
import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import type { UserModelSchema } from '@fastgpt/global/support/user/type';
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant'; import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils';
export const userCollectionName = 'users'; export const userCollectionName = 'users';
@@ -33,11 +32,6 @@ const UserSchema = new Schema({
type: Date, type: Date,
default: () => new Date() default: () => new Date()
}, },
avatar: {
type: String,
default: () => getRandomUserAvatar()
},
promotionRate: { promotionRate: {
type: Number, type: Number,
default: 15 default: 15
@@ -62,7 +56,10 @@ const UserSchema = new Schema({
ref: userCollectionName ref: userCollectionName
}, },
fastgpt_sem: Object, fastgpt_sem: Object,
sourceDomain: String sourceDomain: String,
/** @deprecated */
avatar: String
}); });
try { try {

View File

@@ -37,7 +37,7 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
teamAvatar: tmb.team.avatar, teamAvatar: tmb.team.avatar,
teamName: tmb.team.name, teamName: tmb.team.name,
memberName: tmb.name, memberName: tmb.name,
avatar: tmb.team.avatar, avatar: tmb.avatar,
balance: tmb.team.balance, balance: tmb.team.balance,
tmbId: String(tmb._id), tmbId: String(tmb._id),
teamDomain: tmb.team?.teamDomain, teamDomain: tmb.team?.teamDomain,

View File

@@ -7,6 +7,7 @@ import {
TeamMemberCollectionName, TeamMemberCollectionName,
TeamCollectionName TeamCollectionName
} from '@fastgpt/global/support/user/team/constant'; } from '@fastgpt/global/support/user/team/constant';
import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils';
const TeamMemberSchema = new Schema({ const TeamMemberSchema = new Schema({
teamId: { teamId: {
@@ -35,11 +36,14 @@ const TeamMemberSchema = new Schema({
type: Boolean, type: Boolean,
default: false default: false
}, },
avatar: {
type: String,
default: getRandomUserAvatar()
},
// Abandoned // Abandoned
role: { role: {
type: String type: String
// enum: Object.keys(TeamMemberRoleMap) // disable enum validation for old data
} }
}); });

View File

@@ -1,8 +1,13 @@
export type PaginationProps<T = {}> = T & { import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
offset: number;
pageSize: number; type PaginationProps<T = {}> = T & {
}; pageSize: number | string;
export type PaginationResponse<T = any> = { } & RequireOnlyOne<{
offset: number | string;
pageNum: number | string;
}>;
type PaginationResponse<T = {}> = {
total: number; total: number;
list: T[]; list: T[];
}; };

View File

@@ -21,7 +21,15 @@ import type { ButtonProps, MenuItemProps } from '@chakra-ui/react';
import MyIcon from '../Icon'; import MyIcon from '../Icon';
import { useRequest2 } from '../../../hooks/useRequest'; import { useRequest2 } from '../../../hooks/useRequest';
import MyDivider from '../MyDivider'; import MyDivider from '../MyDivider';
import { useScrollPagination } from '../../../hooks/useScrollPagination';
/** 选择组件 Props 类型
* value: 选中的值
* placeholder: 占位符
* list: 列表数据
* isLoading: 是否加载中
* ScrollData: 分页滚动数据控制器 [useScrollPagination] 的返回值
* */
export type SelectProps<T = any> = ButtonProps & { export type SelectProps<T = any> = ButtonProps & {
value?: T; value?: T;
placeholder?: string; placeholder?: string;
@@ -34,6 +42,7 @@ export type SelectProps<T = any> = ButtonProps & {
}[]; }[];
isLoading?: boolean; isLoading?: boolean;
onchange?: (val: T) => any | Promise<any>; onchange?: (val: T) => any | Promise<any>;
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
}; };
const MySelect = <T = any,>( const MySelect = <T = any,>(
@@ -44,6 +53,7 @@ const MySelect = <T = any,>(
list = [], list = [],
onchange, onchange,
isLoading = false, isLoading = false,
ScrollData,
...props ...props
}: SelectProps<T>, }: SelectProps<T>,
ref: ForwardedRef<{ ref: ForwardedRef<{
@@ -154,7 +164,8 @@ const MySelect = <T = any,>(
maxH={'40vh'} maxH={'40vh'}
overflowY={'auto'} overflowY={'auto'}
> >
{list.map((item, i) => ( {(() => {
const component = list.map((item, i) => (
<Box key={i}> <Box key={i}>
<MenuItem <MenuItem
{...menuItemStyles} {...menuItemStyles}
@@ -186,7 +197,12 @@ const MySelect = <T = any,>(
</MenuItem> </MenuItem>
{item.showBorder && <MyDivider my={2} />} {item.showBorder && <MyDivider my={2} />}
</Box> </Box>
))} ));
if (ScrollData) {
return <ScrollData>{component}</ScrollData>;
}
return component;
})()}
</MenuList> </MenuList>
</Menu> </Menu>
</Box> </Box>

View File

@@ -0,0 +1,23 @@
import { Box, HStack, type StackProps } from '@chakra-ui/react';
import { SourceMemberType } from '@fastgpt/global/support/user/type';
import React from 'react';
import Avatar from '../Avatar';
import { useTranslation } from 'next-i18next';
import Tag from '../Tag';
export type UserBoxProps = {
sourceMember: SourceMemberType;
avatarSize?: string;
} & StackProps;
function UserBox({ sourceMember, avatarSize = '1.25rem', ...props }: UserBoxProps) {
const { t } = useTranslation();
return (
<HStack space="1" {...props}>
<Avatar src={sourceMember.avatar} w={avatarSize} />
<Box>{sourceMember.name}</Box>
{sourceMember.status === 'leave' && <Tag color="gray">{t('account_team:leaved')}</Tag>}
</HStack>
);
}
export default React.memo(UserBox);

View File

@@ -4,7 +4,6 @@ import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useToast } from './useToast'; import { useToast } from './useToast';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { import {
useBoolean, useBoolean,
useLockFn, useLockFn,
@@ -14,19 +13,15 @@ import {
useThrottleEffect useThrottleEffect
} from 'ahooks'; } from 'ahooks';
import { PaginationProps, PaginationResponse } from '../common/fetch/type';
const thresholdVal = 200; const thresholdVal = 200;
type PagingData<T> = { export function usePagination<DataT, ResT = {}>(
pageNum: number; api: (data: PaginationProps<DataT>) => Promise<PaginationResponse<ResT>>,
pageSize: number; {
data: T[];
total?: number;
};
export function usePagination<ResT = any>({
api,
pageSize = 10, pageSize = 10,
params = {}, params,
defaultRequest = true, defaultRequest = true,
type = 'button', type = 'button',
onChange, onChange,
@@ -34,9 +29,8 @@ export function usePagination<ResT = any>({
scrollLoadType = 'bottom', scrollLoadType = 'bottom',
EmptyTip EmptyTip
}: { }: {
api: (data: any) => Promise<PagingData<ResT>>;
pageSize?: number; pageSize?: number;
params?: Record<string, any>; params?: DataT;
defaultRequest?: boolean; defaultRequest?: boolean;
type?: 'button' | 'scroll'; type?: 'button' | 'scroll';
onChange?: (pageNum: number) => void; onChange?: (pageNum: number) => void;
@@ -44,7 +38,8 @@ export function usePagination<ResT = any>({
throttleWait?: number; throttleWait?: number;
scrollLoadType?: 'top' | 'bottom'; scrollLoadType?: 'top' | 'bottom';
EmptyTip?: React.JSX.Element; EmptyTip?: React.JSX.Element;
}) { }
) {
const { toast } = useToast(); const { toast } = useToast();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -64,7 +59,7 @@ export function usePagination<ResT = any>({
setTrue(); setTrue();
try { try {
const res: PagingData<ResT> = await api({ const res = await api({
pageNum: num, pageNum: num,
pageSize, pageSize,
...params ...params
@@ -93,13 +88,13 @@ export function usePagination<ResT = any>({
); );
} }
setData((prevData) => (num === 1 ? res.data : [...res.data, ...prevData])); setData((prevData) => (num === 1 ? res.list : [...res.list, ...prevData]));
adjustScrollPosition(); adjustScrollPosition();
} else { } else {
setData((prevData) => (num === 1 ? res.data : [...prevData, ...res.data])); setData((prevData) => (num === 1 ? res.list : [...prevData, ...res.list]));
} }
} else { } else {
setData(res.data); setData(res.list);
} }
onChange?.(num); onChange?.(num);

View File

@@ -35,5 +35,9 @@
"user_team_invite_member": "邀请成员", "user_team_invite_member": "邀请成员",
"user_team_leave_team": "离开团队", "user_team_leave_team": "离开团队",
"user_team_leave_team_failed": "离开团队失败", "user_team_leave_team_failed": "离开团队失败",
"waiting": "待接受" "leaved": "已离职",
"waiting": "待接受",
"sync_immediately": "立即同步",
"sync_member_failed": "同步成员失败",
"sync_member_success": "同步成员成功"
} }

View File

@@ -54,7 +54,7 @@ const InputGuideConfig = ({
onChange: (e: ChatInputGuideConfigType) => void; onChange: (e: ChatInputGuideConfigType) => void;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { chatT, commonT } = useI18n(); const { chatT } = useI18n();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const { const {
isOpen: isOpenLexiconConfig, isOpen: isOpenLexiconConfig,
@@ -220,7 +220,7 @@ const LexiconConfigModal = ({ appId, onClose }: { appId: string; onClose: () =>
}); });
const { run: createNewData, loading: isCreating } = useRequest2( const { run: createNewData, loading: isCreating } = useRequest2(
(textList: string[]) => { async (textList: string[]) => {
if (textList.filter(Boolean).length === 0) { if (textList.filter(Boolean).length === 0) {
return Promise.resolve(); return Promise.resolve();
} }

View File

@@ -1,4 +1,4 @@
import { useUserStore } from '@/web/support/user/useUserStore'; import { getTeamMembers } from '@/web/support/user/team/api';
import { import {
Box, Box,
Flex, Flex,
@@ -15,6 +15,7 @@ import Icon from '@fastgpt/web/components/common/Icon';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import MyTag from '@fastgpt/web/components/common/Tag'; import MyTag from '@fastgpt/web/components/common/Tag';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import React, { useState } from 'react'; import React, { useState } from 'react';
@@ -31,13 +32,12 @@ export function ChangeOwnerModal({
onChangeOwner onChangeOwner
}: ChangeOwnerModalProps & { onClose: () => void }) { }: ChangeOwnerModalProps & { onClose: () => void }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { loadAndGetTeamMembers } = useUserStore();
const [inputValue, setInputValue] = React.useState(''); const [inputValue, setInputValue] = React.useState('');
const { data: teamMembers = [] } = useRequest2(loadAndGetTeamMembers, { const { data: teamMembers, ScrollData } = useScrollPagination(getTeamMembers, {
manual: false pageSize: 15
}); });
const memberList = teamMembers.filter((item) => { const memberList = teamMembers.filter((item) => {
return item.memberName.includes(inputValue); return item.memberName.includes(inputValue);
}); });
@@ -101,11 +101,6 @@ export function ChangeOwnerModal({
onOpenMemberListMenu(); onOpenMemberListMenu();
setSelectedMember(null); setSelectedMember(null);
}} }}
// onBlur={() => {
// setTimeout(() => {
// onCloseMemberListMenu();
// }, 10);
// }}
{...(selectedMember && { pl: '10' })} {...(selectedMember && { pl: '10' })}
/> />
</Flex> </Flex>
@@ -123,6 +118,7 @@ export function ChangeOwnerModal({
maxH={'300px'} maxH={'300px'}
overflow={'auto'} overflow={'auto'}
> >
<ScrollData>
{memberList.map((item) => ( {memberList.map((item) => (
<Box <Box
key={item.tmbId} key={item.tmbId}
@@ -143,6 +139,7 @@ export function ChangeOwnerModal({
</Flex> </Flex>
</Box> </Box>
))} ))}
</ScrollData>
</Flex> </Flex>
)} )}

View File

@@ -33,6 +33,10 @@ import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type
import { OrgType } from '@fastgpt/global/support/user/team/org/type'; import { OrgType } from '@fastgpt/global/support/user/team/org/type';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { CollaboratorContext } from './context'; import { CollaboratorContext } from './context';
import { getTeamMembers } from '@/web/support/user/team/api';
import { getGroupList } from '@/web/support/user/team/group/api';
import { getOrgList } from '@/web/support/user/team/org/api';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
const HoverBoxStyle = { const HoverBoxStyle = {
bgColor: 'myGray.50', bgColor: 'myGray.50',
@@ -47,22 +51,18 @@ function MemberModal({
addPermissionOnly?: boolean; addPermissionOnly?: boolean;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, loadAndGetOrgs } = useUserStore(); const { userInfo, myGroups } = useUserStore();
const collaboratorList = useContextSelector(CollaboratorContext, (v) => v.collaboratorList); const collaboratorList = useContextSelector(CollaboratorContext, (v) => v.collaboratorList);
const [searchText, setSearchText] = useState<string>(''); const [searchText, setSearchText] = useState<string>('');
const [filterClass, setFilterClass] = useState<'member' | 'org' | 'group'>(); const [filterClass, setFilterClass] = useState<'member' | 'org' | 'group'>();
const { data: members, ScrollData } = useScrollPagination(getTeamMembers, {
pageSize: 15
});
const { data: [members = [], groups = [], orgs = []] = [], loading: loadingMembersAndGroups } = const { data: [groups = [], orgs = []] = [], loading: loadingGroupsAndOrgs } = useRequest2(
useRequest2(
async () => { async () => {
if (!userInfo?.team?.teamId) return [[], []]; if (!userInfo?.team?.teamId) return [[], []];
return Promise.all([ return Promise.all([getGroupList(), getOrgList()]);
loadAndGetTeamMembers(true),
loadAndGetGroups(true),
loadAndGetOrgs(true)
]);
}, },
{ {
manual: false, manual: false,
@@ -71,6 +71,7 @@ function MemberModal({
); );
const [parentPath, setParentPath] = useState(''); const [parentPath, setParentPath] = useState('');
const paths = useMemo(() => { const paths = useMemo(() => {
const splitPath = parentPath.split('/').filter(Boolean); const splitPath = parentPath.split('/').filter(Boolean);
return splitPath return splitPath
@@ -212,7 +213,7 @@ function MemberModal({
h={'100%'} h={'100%'}
maxH={'90vh'} maxH={'90vh'}
isCentered isCentered
isLoading={loadingMembersAndGroups} isLoading={loadingGroupsAndOrgs}
> >
<ModalBody flex={'1'}> <ModalBody flex={'1'}>
<Grid <Grid
@@ -299,11 +300,9 @@ function MemberModal({
)} )}
<Flex flexDirection={'column'} gap={1} userSelect={'none'}> <Flex flexDirection={'column'} gap={1} userSelect={'none'}>
<ScrollData>
{filterMembers.map((member) => { {filterMembers.map((member) => {
const collaborator = collaboratorList?.find((v) => v.tmbId === member.tmbId);
const disabled = addOnly && collaborator !== undefined;
const onChange = () => { const onChange = () => {
if (disabled) return;
setSelectedMembers((state) => { setSelectedMembers((state) => {
if (state.includes(member.tmbId)) { if (state.includes(member.tmbId)) {
return state.filter((v) => v !== member.tmbId); return state.filter((v) => v !== member.tmbId);
@@ -311,6 +310,7 @@ function MemberModal({
return [...state, member.tmbId]; return [...state, member.tmbId];
}); });
}; };
const collaborator = collaboratorList?.find((v) => v.tmbId === member.tmbId);
return ( return (
<HStack <HStack
justifyContent="space-between" justifyContent="space-between"
@@ -323,25 +323,19 @@ function MemberModal({
onClick={onChange} onClick={onChange}
> >
<Checkbox <Checkbox
isDisabled={disabled} isChecked={selectedMemberIdList.includes(member.tmbId)}
isChecked={disabled || selectedMemberIdList.includes(member.tmbId)}
pointerEvents="none" pointerEvents="none"
/> />
<MyAvatar src={member.avatar} w="1.5rem" borderRadius={'50%'} /> <MyAvatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
<Box w="full" ml="2"> <Box w="full" ml="2">
{member.memberName} {member.memberName}
</Box> </Box>
<PermissionTags <PermissionTags permission={collaborator?.permission.value} />
permission={addOnly ? undefined : collaborator?.permission.value}
/>
</HStack> </HStack>
); );
})} })}
{filterOrgs.map((org) => { {filterOrgs.map((org) => {
const collaborator = collaboratorList?.find((v) => v.orgId === org._id);
const disabled = addOnly && collaborator !== undefined;
const onChange = () => { const onChange = () => {
if (disabled) return;
setSelectedOrgIdList((state) => { setSelectedOrgIdList((state) => {
if (state.includes(org._id)) { if (state.includes(org._id)) {
return state.filter((v) => v !== org._id); return state.filter((v) => v !== org._id);
@@ -349,6 +343,7 @@ function MemberModal({
return [...state, org._id]; return [...state, org._id];
}); });
}; };
const collaborator = collaboratorList?.find((v) => v.orgId === org._id);
return ( return (
<HStack <HStack
justifyContent="space-between" justifyContent="space-between"
@@ -361,22 +356,21 @@ function MemberModal({
onClick={onChange} onClick={onChange}
> >
<Checkbox <Checkbox
isDisabled={disabled} isChecked={selectedOrgIdList.includes(org._id)}
isChecked={disabled || selectedOrgIdList.includes(org._id)}
pointerEvents="none" pointerEvents="none"
/> />
<MyAvatar src={org.avatar} w="1.5rem" borderRadius={'50%'} /> <MyAvatar src={org.avatar} w="1.5rem" borderRadius={'50%'} />
<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 permission={collaborator?.permission.value} />
permission={addOnly ? undefined : collaborator?.permission.value}
/>
{org.count && ( {org.count && (
<MyIcon <MyIcon
name="core/chat/chevronRight" name="core/chat/chevronRight"
@@ -386,8 +380,7 @@ function MemberModal({
_hover={{ _hover={{
bgColor: 'myGray.200' bgColor: 'myGray.200'
}} }}
onClick={(e) => { onClick={() => {
e.stopPropagation();
setParentPath(getOrgChildrenPath(org)); setParentPath(getOrgChildrenPath(org));
}} }}
/> />
@@ -396,10 +389,7 @@ function MemberModal({
); );
})} })}
{filterGroups.map((group) => { {filterGroups.map((group) => {
const collaborator = collaboratorList?.find((v) => v.groupId === group._id);
const disabled = addOnly && collaborator !== undefined;
const onChange = () => { const onChange = () => {
if (disabled) return;
setSelectedGroupIdList((state) => { setSelectedGroupIdList((state) => {
if (state.includes(group._id)) { if (state.includes(group._id)) {
return state.filter((v) => v !== group._id); return state.filter((v) => v !== group._id);
@@ -407,6 +397,7 @@ function MemberModal({
return [...state, group._id]; return [...state, group._id];
}); });
}; };
const collaborator = collaboratorList?.find((v) => v.groupId === group._id);
return ( return (
<HStack <HStack
justifyContent="space-between" justifyContent="space-between"
@@ -419,20 +410,18 @@ function MemberModal({
onClick={onChange} onClick={onChange}
> >
<Checkbox <Checkbox
isDisabled={disabled} isChecked={selectedGroupIdList.includes(group._id)}
isChecked={disabled || selectedGroupIdList.includes(group._id)}
pointerEvents="none" pointerEvents="none"
/> />
<MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} /> <MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} />
<Box ml="2" w="full"> <Box ml="2" w="full">
{group.name === DefaultGroupName ? userInfo?.team.teamName : group.name} {group.name === DefaultGroupName ? userInfo?.team.teamName : group.name}
</Box> </Box>
<PermissionTags <PermissionTags permission={collaborator?.permission.value} />
permission={addOnly ? undefined : collaborator?.permission.value}
/>
</HStack> </HStack>
); );
})} })}
</ScrollData>
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>

View File

@@ -1,7 +1,7 @@
import { RequestPaging } from '@/types'; import { PaginationProps } from '@fastgpt/web/common/fetch/type';
export type GetAppChatLogsParams = RequestPaging & { export type GetAppChatLogsParams = PaginationProps<{
appId: string; appId: string;
dateStart: Date; dateStart: Date;
dateEnd: Date; dateEnd: Date;
}; }>;

View File

@@ -3,24 +3,24 @@ import {
DatasetCollectionTypeEnum, DatasetCollectionTypeEnum,
DatasetTypeEnum DatasetTypeEnum
} from '@fastgpt/global/core/dataset/constants'; } from '@fastgpt/global/core/dataset/constants';
import type { RequestPaging } from '@/types';
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants'; import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
import type { SearchTestItemType } from '@/types/core/dataset'; import type { SearchTestItemType } from '@/types/core/dataset';
import { UploadChunkItemType } from '@fastgpt/global/core/dataset/type'; import { UploadChunkItemType } from '@fastgpt/global/core/dataset/type';
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type'; import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type';
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant'; import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { PaginationProps } from '@fastgpt/web/common/fetch/type';
/* ===== dataset ===== */ /* ===== dataset ===== */
/* ======= collections =========== */ /* ======= collections =========== */
export type GetDatasetCollectionsProps = RequestPaging & { export type GetDatasetCollectionsProps = PaginationProps<{
datasetId: string; datasetId: string;
parentId?: string; parentId?: string;
searchText?: string; searchText?: string;
filterTags?: string[]; filterTags?: string[];
simple?: boolean; simple?: boolean;
selectFolder?: boolean; selectFolder?: boolean;
}; }>;
/* ==== data ===== */ /* ==== data ===== */

View File

@@ -1,4 +1,4 @@
import React, { useState, useCallback, useMemo, useEffect } from 'react'; import React, { useState, useMemo, useEffect } from 'react';
import { import {
Button, Button,
Table, Table,
@@ -25,7 +25,6 @@ import {
billStatusMap, billStatusMap,
billTypeMap billTypeMap
} from '@fastgpt/global/support/wallet/bill/constants'; } from '@fastgpt/global/support/wallet/bill/constants';
// import { usePagination } from '@/web/common/hooks/usePagination';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants'; import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants';
@@ -33,25 +32,23 @@ import MySelect from '@fastgpt/web/components/common/MySelect';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import { usePagination } from '@fastgpt/web/hooks/usePagination'; import { usePagination } from '@fastgpt/web/hooks/usePagination';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useI18n } from '@/web/context/I18n';
const BillTable = () => { const BillTable = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { commonT } = useI18n();
const { toast } = useToast(); const { toast } = useToast();
const [billType, setBillType] = useState<BillTypeEnum | ''>(''); const [billType, setBillType] = useState<BillTypeEnum | undefined>(undefined);
const [billDetail, setBillDetail] = useState<BillSchemaType>(); const [billDetail, setBillDetail] = useState<BillSchemaType>();
const billTypeList = useMemo( const billTypeList = useMemo(
() => () =>
[ [
{ label: t('account_bill:all'), value: '' }, { label: t('account_bill:all'), value: undefined },
...Object.entries(billTypeMap).map(([key, value]) => ({ ...Object.entries(billTypeMap).map(([key, value]) => ({
label: t(value.label as any), label: t(value.label as any),
value: key value: key
})) }))
] as { ] as {
label: string; label: string;
value: BillTypeEnum | ''; value: BillTypeEnum | undefined;
}[], }[],
[t] [t]
); );
@@ -62,8 +59,7 @@ const BillTable = () => {
Pagination, Pagination,
getData, getData,
total total
} = usePagination({ } = usePagination(getBills, {
api: getBills,
pageSize: 20, pageSize: 20,
params: { params: {
type: billType type: billType
@@ -110,7 +106,7 @@ const BillTable = () => {
<Tr> <Tr>
<Th>#</Th> <Th>#</Th>
<Th> <Th>
<MySelect<BillTypeEnum | ''> <MySelect
list={billTypeList} list={billTypeList}
value={billType} value={billType}
size={'sm'} size={'sm'}
@@ -181,7 +177,6 @@ export default BillTable;
function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) { function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { commonT } = useI18n();
return ( return (
<MyModal <MyModal
isOpen={true} isOpen={true}

View File

@@ -1,7 +1,7 @@
import { getInvoiceRecords } from '@/web/support/wallet/bill/invoice/api'; import { getInvoiceRecords } from '@/web/support/wallet/bill/invoice/api';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useEffect, useState } from 'react'; import { useState } from 'react';
import { import {
Box, Box,
Button, Button,
@@ -30,8 +30,7 @@ const InvoiceTable = () => {
isLoading, isLoading,
Pagination, Pagination,
total total
} = usePagination({ } = usePagination(getInvoiceRecords, {
api: getInvoiceRecords,
pageSize: 20 pageSize: 20
}); });

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useRef } from 'react'; import React, { useCallback, useMemo } from 'react';
import { import {
Box, Box,
Flex, Flex,
@@ -160,6 +160,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
color: 'myGray.900' color: 'myGray.900'
}; };
const isSyncMember = feConfigs.register_method?.includes('sync');
return ( return (
<Box> <Box>
{/* user info */} {/* user info */}
@@ -224,6 +225,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
<Box {...labelStyles}>{t('account_info:member_name')}:&nbsp;</Box> <Box {...labelStyles}>{t('account_info:member_name')}:&nbsp;</Box>
<Input <Input
flex={'1 0 0'} flex={'1 0 0'}
disabled={isSyncMember}
defaultValue={userInfo?.team?.memberName || 'Member'} defaultValue={userInfo?.team?.memberName || 'Member'}
title={t('account_info:click_modify_nickname')} title={t('account_info:click_modify_nickname')}
borderColor={'transparent'} borderColor={'transparent'}
@@ -590,11 +592,6 @@ const Other = ({ onOpenContact }: { onOpenContact: () => void }) => {
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
const { userInfo, updateUserInfo } = useUserStore();
const { reset } = useForm<UserUpdateParams>({
defaultValues: userInfo as UserType
});
return ( return (
<Box> <Box>

View File

@@ -1,13 +1,12 @@
import React from 'react'; import React from 'react';
import { Box, Button, Flex, useTheme } from '@chakra-ui/react'; import { Box, Button, Flex, useTheme } from '@chakra-ui/react';
import { getInforms, readInform } from '@/web/support/user/inform/api'; import { getInforms, readInform } from '@/web/support/user/inform/api';
import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time'; import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { usePagination } from '@fastgpt/web/hooks/usePagination'; import { usePagination } from '@fastgpt/web/hooks/usePagination';
import { useLoading } from '@fastgpt/web/hooks/useLoading'; import { useLoading } from '@fastgpt/web/hooks/useLoading';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import AccountContainer, { TabEnum } from './components/AccountContainer'; import AccountContainer from './components/AccountContainer';
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs'; import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
const InformTable = () => { const InformTable = () => {
@@ -23,8 +22,7 @@ const InformTable = () => {
Pagination, Pagination,
getData, getData,
pageNum pageNum
} = usePagination<UserInformSchema>({ } = usePagination(getInforms, {
api: getInforms,
pageSize: 20 pageSize: 20
}); });

View File

@@ -25,7 +25,7 @@ import { usePagination } from '@fastgpt/web/hooks/usePagination';
import { useLoading } from '@fastgpt/web/hooks/useLoading'; import { useLoading } from '@fastgpt/web/hooks/useLoading';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import AccountContainer, { TabEnum } from './components/AccountContainer'; import AccountContainer from './components/AccountContainer';
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs'; import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
const Promotion = () => { const Promotion = () => {
@@ -41,8 +41,7 @@ const Promotion = () => {
total, total,
pageSize, pageSize,
Pagination Pagination
} = usePagination({ } = usePagination(getPromotionRecords, {
api: getPromotionRecords,
pageSize: 20 pageSize: 20
}); });

View File

@@ -13,7 +13,6 @@ import {
Tr, Tr,
useDisclosure useDisclosure
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
@@ -30,6 +29,8 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { delLeaveTeam } from '@/web/support/user/team/api'; import { delLeaveTeam } from '@/web/support/user/team/api';
import { postSyncMembers } from '@/web/support/user/api';
import MyLoading from '@fastgpt/web/components/common/MyLoading';
const InviteModal = dynamic(() => import('./InviteModal')); const InviteModal = dynamic(() => import('./InviteModal'));
const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal')); const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal'));
@@ -40,8 +41,16 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
const { userInfo, teamPlanStatus } = useUserStore(); const { userInfo, teamPlanStatus } = useUserStore();
const { feConfigs, setNotSufficientModalType } = useSystemStore(); const { feConfigs, setNotSufficientModalType } = useSystemStore();
const { groups, refetchGroups, myTeams, refetchTeams, members, refetchMembers, onSwitchTeam } = const {
useContextSelector(TeamContext, (v) => v); groups,
refetchGroups,
myTeams,
refetchTeams,
members,
refetchMembers,
onSwitchTeam,
MemberScrollData
} = useContextSelector(TeamContext, (v) => v);
const { const {
isOpen: isOpenTeamTagsAsync, isOpen: isOpenTeamTagsAsync,
@@ -54,6 +63,8 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
type: 'delete' type: 'delete'
}); });
const isSyncMember = feConfigs.register_method?.includes('sync');
const { runAsync: onLeaveTeam } = useRequest2( const { runAsync: onLeaveTeam } = useRequest2(
async () => { async () => {
const defaultTeam = myTeams.find((item) => item.defaultTeam) || myTeams[0]; const defaultTeam = myTeams.find((item) => item.defaultTeam) || myTeams[0];
@@ -72,8 +83,17 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
content: t('account_team:confirm_leave_team') content: t('account_team:confirm_leave_team')
}); });
const { runAsync: onSyncMember, loading: isSyncing } = useRequest2(postSyncMembers, {
onSuccess() {
refetchMembers();
},
successToast: t('account_team:sync_member_success'),
errorToast: t('account_team:sync_member_failed')
});
return ( return (
<> <>
{isSyncing && <MyLoading />}
<Flex justify={'space-between'} align={'center'} pb={'1rem'}> <Flex justify={'space-between'} align={'center'} pb={'1rem'}>
{Tabs} {Tabs}
<HStack> <HStack>
@@ -91,7 +111,21 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:label_sync')} {t('account_team:label_sync')}
</Button> </Button>
)} )}
{userInfo?.team.permission.hasManagePer && ( {userInfo?.team.permission.hasManagePer && isSyncMember && (
<Button
variant={'primary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name="common/retryLight" w={'16px'} color={'white'} />}
onClick={() => {
onSyncMember();
}}
>
{t('account_team:sync_immediately')}
</Button>
)}
{userInfo?.team.permission.hasManagePer && !isSyncMember && (
<Button <Button
variant={'primary'} variant={'primary'}
size="md" size="md"
@@ -135,6 +169,8 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
<Box flex={'1 0 0'} overflow={'auto'}> <Box flex={'1 0 0'} overflow={'auto'}>
<TableContainer overflow={'unset'} fontSize={'sm'}> <TableContainer overflow={'unset'} fontSize={'sm'}>
{MemberScrollData && (
<MemberScrollData>
<Table overflow={'unset'}> <Table overflow={'unset'}>
<Thead> <Thead>
<Tr bgColor={'white !important'}> <Tr bgColor={'white !important'}>
@@ -142,9 +178,11 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:user_name')} {t('account_team:user_name')}
</Th> </Th>
<Th bgColor="myGray.100">{t('account_team:member_group')}</Th> <Th bgColor="myGray.100">{t('account_team:member_group')}</Th>
{!isSyncMember && (
<Th borderRightRadius="6px" bgColor="myGray.100"> <Th borderRightRadius="6px" bgColor="myGray.100">
{t('common:common.Action')} {t('common:common.Action')}
</Th> </Th>
)}
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
@@ -166,15 +204,17 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
<Td maxW={'300px'}> <Td maxW={'300px'}>
<GroupTags <GroupTags
names={groups names={groups
?.filter((group) => group.members.map((m) => m.tmbId).includes(item.tmbId)) ?.filter((group) =>
group.members.map((m) => m.tmbId).includes(item.tmbId)
)
.map((g) => g.name)} .map((g) => g.name)}
max={3} max={3}
/> />
</Td> </Td>
{!isSyncMember && (
<Td> <Td>
{userInfo?.team.permission.hasManagePer && userInfo?.team.permission.hasManagePer && item.role !==
item.role !== TeamMemberRoleEnum.owner && TeamMemberRoleEnum.owner && item.tmbId !== userInfo?.team.tmbId && (
item.tmbId !== userInfo?.team.tmbId && (
<Icon <Icon
name={'common/trash'} name={'common/trash'}
cursor={'pointer'} cursor={'pointer'}
@@ -198,13 +238,15 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
)(); )();
}} }}
/> />
)} )
</Td> </Td>
)}
</Tr> </Tr>
))} ))}
</Tbody> </Tbody>
</Table> </Table>
</MemberScrollData>
)}
<ConfirmRemoveMemberModal /> <ConfirmRemoveMemberModal />
</TableContainer> </TableContainer>
</Box> </Box>

View File

@@ -1,4 +1,3 @@
import { deleteOrg, deleteOrgMember } from '@/web/support/user/team/org/api';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import { import {
Box, Box,
@@ -27,7 +26,7 @@ import { useMemo, useState } from 'react';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import MemberTag from '@/components/support/user/team/Info/MemberTag'; import MemberTag from '@/components/support/user/team/Info/MemberTag';
import { TeamContext } from '../context'; import { TeamContext } from '../context';
import { getOrgList } from '@/web/support/user/team/org/api'; import { deleteOrg, deleteOrgMember, getOrgList } from '@/web/support/user/team/org/api';
import IconButton from './IconButton'; import IconButton from './IconButton';
import { defaultOrgForm, type OrgFormType } from './OrgInfoModal'; import { defaultOrgForm, type OrgFormType } from './OrgInfoModal';
@@ -37,6 +36,7 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
import Path from '@/components/common/folder/Path'; import Path from '@/components/common/folder/Path';
import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type'; import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant'; import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
import { useSystemStore } from '@/web/common/system/useSystemStore';
const OrgInfoModal = dynamic(() => import('./OrgInfoModal')); const OrgInfoModal = dynamic(() => import('./OrgInfoModal'));
const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal')); const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal'));
@@ -76,7 +76,9 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
const { userInfo, isTeamAdmin } = useUserStore(); const { userInfo, isTeamAdmin } = useUserStore();
const { members } = useContextSelector(TeamContext, (v) => v); const { members } = useContextSelector(TeamContext, (v) => v);
const { feConfigs } = useSystemStore();
const isSyncMember = feConfigs.register_method?.includes('sync');
const [parentPath, setParentPath] = useState(''); const [parentPath, setParentPath] = useState('');
const { const {
data: orgs = [], data: orgs = [],
@@ -174,9 +176,11 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
<Th bg="myGray.100" borderLeftRadius="6px"> <Th bg="myGray.100" borderLeftRadius="6px">
{t('common:Name')} {t('common:Name')}
</Th> </Th>
{!isSyncMember && (
<Th bg="myGray.100" borderRightRadius="6px"> <Th bg="myGray.100" borderRightRadius="6px">
{t('common:common.Action')} {t('common:common.Action')}
</Th> </Th>
)}
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
@@ -197,8 +201,8 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
/> />
</HStack> </HStack>
</Td> </Td>
{isTeamAdmin && !isSyncMember && (
<Td w={'6rem'}> <Td w={'6rem'}>
{isTeamAdmin && (
<MyMenu <MyMenu
trigger="hover" trigger="hover"
Button={<IconButton name="more" />} Button={<IconButton name="more" />}
@@ -225,8 +229,8 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
} }
]} ]}
/> />
)}
</Td> </Td>
)}
</Tr> </Tr>
))} ))}
{currentOrg?.members.map((member) => { {currentOrg?.members.map((member) => {
@@ -239,7 +243,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
<MemberTag name={memberInfo.memberName} avatar={memberInfo.avatar} /> <MemberTag name={memberInfo.memberName} avatar={memberInfo.avatar} />
</Td> </Td>
<Td w={'6rem'}> <Td w={'6rem'}>
{isTeamAdmin && ( {isTeamAdmin && !isSyncMember && (
<MyMenu <MyMenu
trigger={'hover'} trigger={'hover'}
Button={<IconButton name="more" />} Button={<IconButton name="more" />}
@@ -268,6 +272,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
</Table> </Table>
</TableContainer> </TableContainer>
{/* Slider */} {/* Slider */}
{!isSyncMember && (
<VStack w={'180px'} alignItems={'start'}> <VStack w={'180px'} alignItems={'start'}>
<HStack gap={'6px'}> <HStack gap={'6px'}>
<Avatar src={currentOrg?.avatar} w={'1rem'} h={'1rem'} rounded={'xs'} /> <Avatar src={currentOrg?.avatar} w={'1rem'} h={'1rem'} rounded={'xs'} />
@@ -319,6 +324,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
</VStack> </VStack>
)} )}
</VStack> </VStack>
)}
</Flex> </Flex>
{!!editOrg && ( {!!editOrg && (

View File

@@ -2,7 +2,7 @@ import React, { ReactNode, useState } from 'react';
import { createContext } from 'use-context-selector'; import { createContext } from 'use-context-selector';
import type { EditTeamFormDataType } from './EditInfoModal'; import type { EditTeamFormDataType } from './EditInfoModal';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { getTeamList, putSwitchTeam } from '@/web/support/user/team/api'; import { getTeamList, getTeamMembers, putSwitchTeam } from '@/web/support/user/team/api';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant'; import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import type { TeamTmbItemType, TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; import type { TeamTmbItemType, TeamMemberItemType } from '@fastgpt/global/support/user/team/type';
@@ -10,7 +10,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { getGroupList } from '@/web/support/user/team/group/api'; import { getGroupList } from '@/web/support/user/team/group/api';
import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type'; import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type';
import { OrgType } from '@fastgpt/global/support/user/team/org/type'; import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
const EditInfoModal = dynamic(() => import('./EditInfoModal')); const EditInfoModal = dynamic(() => import('./EditInfoModal'));
@@ -26,6 +26,7 @@ type TeamModalContextType = {
refetchTeams: () => void; refetchTeams: () => void;
refetchGroups: () => void; refetchGroups: () => void;
teamSize: number; teamSize: number;
MemberScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
}; };
export const TeamContext = createContext<TeamModalContextType>({ export const TeamContext = createContext<TeamModalContextType>({
@@ -49,13 +50,14 @@ export const TeamContext = createContext<TeamModalContextType>({
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
teamSize: 0 teamSize: 0,
MemberScrollData: undefined
}); });
export const TeamModalContextProvider = ({ children }: { children: ReactNode }) => { export const TeamModalContextProvider = ({ children }: { children: ReactNode }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [editTeamData, setEditTeamData] = useState<EditTeamFormDataType>(); const [editTeamData, setEditTeamData] = useState<EditTeamFormDataType>();
const { userInfo, initUserInfo, loadAndGetTeamMembers } = useUserStore(); const { userInfo, initUserInfo } = useUserStore();
const { const {
data: myTeams = [], data: myTeams = [],
@@ -69,18 +71,11 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode })
// member action // member action
const { const {
data: members = [], data: members = [],
runAsync: refetchMembers, isLoading: loadingMembers,
loading: loadingMembers refreshList: refetchMembers,
} = useRequest2( total: memberTotal,
() => { ScrollData: MemberScrollData
if (!userInfo?.team?.teamId) return Promise.resolve([]); } = useScrollPagination(getTeamMembers, {});
return loadAndGetTeamMembers(true);
},
{
manual: false,
refreshDeps: [userInfo?.team?.teamId]
}
);
const { runAsync: onSwitchTeam, loading: isSwitchingTeam } = useRequest2( const { runAsync: onSwitchTeam, loading: isSwitchingTeam } = useRequest2(
async (teamId: string) => { async (teamId: string) => {
@@ -115,7 +110,8 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode })
refetchMembers, refetchMembers,
groups, groups,
refetchGroups, refetchGroups,
teamSize: members.length teamSize: memberTotal,
MemberScrollData
}; };
return ( return (

View File

@@ -1,6 +1,6 @@
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs'; import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
import AccountContainer from '../components/AccountContainer'; import AccountContainer from '../components/AccountContainer';
import { Box, Flex, useDisclosure } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import Icon from '@fastgpt/web/components/common/Icon'; import Icon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import TeamSelector from '../components/TeamSelector'; import TeamSelector from '../components/TeamSelector';
@@ -13,11 +13,10 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { TeamContext, TeamModalContextProvider } from './components/context'; import { TeamContext, TeamModalContextProvider } from './components/context';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import MemberTable from './components/MemberTable';
const MemberTable = dynamic(() => import('./components/MemberTable'));
const PermissionManage = dynamic(() => import('./components/PermissionManage/index')); const PermissionManage = dynamic(() => import('./components/PermissionManage/index'));
const GroupManage = dynamic(() => import('./components/GroupManage/index')); const GroupManage = dynamic(() => import('./components/GroupManage/index'));
const OrgManage = dynamic(() => import('./components/OrgManage/index')); const OrgManage = dynamic(() => import('./components/OrgManage/index'));
export enum TeamTabEnum { export enum TeamTabEnum {
@@ -34,7 +33,7 @@ const Team = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { setEditTeamData, teamSize, isLoading } = useContextSelector(TeamContext, (v) => v); const { setEditTeamData, isLoading, teamSize } = useContextSelector(TeamContext, (v) => v);
const Tabs = useMemo( const Tabs = useMemo(
() => ( () => (

View File

@@ -23,15 +23,16 @@ import DateRangePicker, {
import { addDays } from 'date-fns'; import { addDays } from 'date-fns';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useQuery } from '@tanstack/react-query';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import MySelect from '@fastgpt/web/components/common/MySelect'; import MySelect from '@fastgpt/web/components/common/MySelect';
import { formatNumber } from '@fastgpt/global/common/math/tools'; import { formatNumber } from '@fastgpt/global/common/math/tools';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import AccountContainer, { TabEnum } from '../components/AccountContainer'; import AccountContainer from '../components/AccountContainer';
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs'; import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import { getTeamMembers } from '@/web/support/user/team/api';
const UsageDetail = dynamic(() => import('./UsageDetail')); const UsageDetail = dynamic(() => import('./UsageDetail'));
@@ -44,7 +45,7 @@ const UsageTable = () => {
}); });
const [usageSource, setUsageSource] = useState<UsageSourceEnum | ''>(''); const [usageSource, setUsageSource] = useState<UsageSourceEnum | ''>('');
const { isPc } = useSystem(); const { isPc } = useSystem();
const { userInfo, loadAndGetTeamMembers } = useUserStore(); const { userInfo } = useUserStore();
const [usageDetail, setUsageDetail] = useState<UsageItemType>(); const [usageDetail, setUsageDetail] = useState<UsageItemType>();
const sourceList = useMemo( const sourceList = useMemo(
@@ -63,10 +64,7 @@ const UsageTable = () => {
); );
const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId); const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId);
const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => { const { data: members, ScrollData } = useScrollPagination(getTeamMembers, {});
if (!userInfo?.team?.teamId) return [];
return loadAndGetTeamMembers();
});
const tmbList = useMemo( const tmbList = useMemo(
() => () =>
members.map((item) => ({ members.map((item) => ({
@@ -86,14 +84,13 @@ const UsageTable = () => {
isLoading, isLoading,
Pagination, Pagination,
getData getData
} = usePagination<UsageItemType>({ } = usePagination(getUserUsages, {
api: getUserUsages,
pageSize: isPc ? 20 : 10, pageSize: isPc ? 20 : 10,
params: { params: {
dateStart: dateRange.from || new Date(), dateStart: dateRange.from || new Date(),
dateEnd: addDays(dateRange.to || new Date(), 1), dateEnd: addDays(dateRange.to || new Date(), 1),
source: usageSource, source: usageSource as UsageSourceEnum,
teamMemberId: selectTmbId teamMemberId: selectTmbId ?? ''
}, },
defaultRequest: false defaultRequest: false
}); });
@@ -120,6 +117,7 @@ const UsageTable = () => {
<MySelect <MySelect
size={'sm'} size={'sm'}
minW={'100px'} minW={'100px'}
ScrollData={ScrollData}
list={tmbList} list={tmbList}
value={selectTmbId} value={selectTmbId}
onchange={setSelectTmbId} onchange={setSelectTmbId}

View File

@@ -5,6 +5,8 @@ import { jiebaSplit } from '@fastgpt/service/common/string/jieba';
import { MongoDatasetDataText } from '@fastgpt/service/core/dataset/data/dataTextSchema'; import { MongoDatasetDataText } from '@fastgpt/service/core/dataset/data/dataTextSchema';
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { NextApiRequest, NextApiResponse } from 'next'; import { NextApiRequest, NextApiResponse } from 'next';
/* /*
@@ -14,6 +16,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
2. 执行升级脚本,不要删除 MongoDatasetData 里的数据。 2. 执行升级脚本,不要删除 MongoDatasetData 里的数据。
3. 切换正式版镜像,让 MongoDatasetDataText 生效。 3. 切换正式版镜像,让 MongoDatasetDataText 生效。
4. 删除 MongoDatasetData 里的索引和多余字段。4819 再删 4. 删除 MongoDatasetData 里的索引和多余字段。4819 再删
5. 移动 User 表中的 avatar 字段到 TeamMember 表中。
*/ */
let success = 0; let success = 0;
async function handler(req: NextApiRequest, res: NextApiResponse) { async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -109,15 +112,26 @@ const initData = async (batchSize: number) => {
} }
}; };
const batchUpdateFields = async (batchSize = 2000) => { // const batchUpdateFields = async (batchSize = 2000) => {
// Update in batches // // Find documents that still have these fields
await MongoDatasetData.updateMany( // const documents = await MongoDatasetData.find({ initFullText: { $exists: true } }, '_id')
{ initFullText: { $exists: true } }, // .limit(batchSize)
{ // .lean();
$unset: {
initFullText: 1, // if (documents.length === 0) return;
fullTextToken: 1
} // // Update in batches
} // await MongoDatasetData.updateMany(
); // { _id: { $in: documents.map((doc) => doc._id) } },
}; // {
// $unset: {
// initFullText: 1
// // fullTextToken: 1
// }
// }
// );
// success += documents.length;
// console.log('Delete success:', success);
// await batchUpdateFields(batchSize);
// };

View File

@@ -0,0 +1,37 @@
import { NextAPI } from '@/service/middleware/entry';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { NextApiRequest, NextApiResponse } from 'next';
/*
简单版迁移:直接升级到最新镜像,会去除 MongoDatasetData 里的索引。直接执行这个脚本。
无缝迁移:
1. 移动 User 表中的 avatar 字段到 TeamMember 表中。
*/
async function handler(req: NextApiRequest, res: NextApiResponse) {
await authCert({ req, authRoot: true });
await moveUserAvatar();
return { success: true };
}
export default NextAPI(handler);
const moveUserAvatar = async () => {
try {
const users = await MongoUser.find({});
for await (const user of users) {
await MongoTeamMember.updateOne(
{
_id: user._id
},
{
avatar: (user as any).avatar // 删除 avatar 字段, 因为 Type 改了,所以这里不能直接写 user.avatar
}
);
}
console.log('Move avatar success:', users.length);
} catch (error) {
console.error(error);
}
};

View File

@@ -1,6 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
import type { PagingData } from '@/types';
import { AppLogsListItemType } from '@/types/app'; import { AppLogsListItemType } from '@/types/app';
import { Types } from '@fastgpt/service/common/mongo'; import { Types } from '@fastgpt/service/common/mongo';
import { addDays } from 'date-fns'; import { addDays } from 'date-fns';
@@ -10,19 +9,22 @@ import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchem
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils'; import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
import { PaginationResponse } from '@fastgpt/web/common/fetch/type';
async function handler( async function handler(
req: NextApiRequest, req: NextApiRequest,
_res: NextApiResponse _res: NextApiResponse
): Promise<PagingData<AppLogsListItemType>> { ): Promise<PaginationResponse<AppLogsListItemType>> {
const { const {
pageNum = 1,
pageSize = 20,
appId, appId,
dateStart = addDays(new Date(), -7), dateStart = addDays(new Date(), -7),
dateEnd = new Date() dateEnd = new Date()
} = req.body as GetAppChatLogsParams; } = req.body as GetAppChatLogsParams;
const { pageSize = 20, offset } = parsePaginationRequest(req);
if (!appId) { if (!appId) {
throw new Error('缺少参数'); throw new Error('缺少参数');
} }
@@ -39,7 +41,7 @@ async function handler(
} }
}; };
const [data, total] = await Promise.all([ const [list, total] = await Promise.all([
MongoChat.aggregate( MongoChat.aggregate(
[ [
{ $match: where }, { $match: where },
@@ -51,7 +53,7 @@ async function handler(
updateTime: -1 updateTime: -1
} }
}, },
{ $skip: (pageNum - 1) * pageSize }, { $skip: offset },
{ $limit: pageSize }, { $limit: pageSize },
{ {
$lookup: { $lookup: {
@@ -80,6 +82,14 @@ async function handler(
as: 'chatitems' as: 'chatitems'
} }
}, },
{
$lookup: {
from: TeamMemberCollectionName,
localField: 'tmbId',
foreignField: '_id',
as: 'member'
}
},
{ {
$addFields: { $addFields: {
userGoodFeedbackCount: { userGoodFeedbackCount: {
@@ -133,7 +143,12 @@ async function handler(
customFeedbacksCount: 1, customFeedbacksCount: 1,
markCount: 1, markCount: 1,
outLinkUid: 1, outLinkUid: 1,
tmbId: 1 tmbId: 1,
sourceMember: {
name: '$member.name',
avatar: '$member.avatar',
status: '$member.status'
}
} }
} }
], ],
@@ -145,9 +160,7 @@ async function handler(
]); ]);
return { return {
pageNum, list,
pageSize,
data,
total total
}; };
} }

View File

@@ -18,6 +18,7 @@ import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { concatPer } from '@fastgpt/service/support/permission/controller'; import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers'; import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
export type ListAppBody = { export type ListAppBody = {
parentId?: ParentIdType; parentId?: ParentIdType;
@@ -201,7 +202,15 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
}) })
.filter((app) => app.permission.hasReadPer); .filter((app) => app.permission.hasReadPer);
return formatApps.map((app) => ({ // get member info
const memberInfo = await MongoTeamMember.find(
{ _id: { $in: formatApps.map((app) => app.tmbId) } },
'_id name avatar status'
).lean();
return formatApps.map((app) => {
const member = memberInfo.find((item) => String(item._id) === String(app.tmbId))!;
return {
_id: app._id, _id: app._id,
tmbId: app.tmbId, tmbId: app.tmbId,
avatar: app.avatar, avatar: app.avatar,
@@ -212,8 +221,14 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
permission: app.permission, permission: app.permission,
pluginData: app.pluginData, pluginData: app.pluginData,
inheritPermission: app.inheritPermission ?? true, inheritPermission: app.inheritPermission ?? true,
private: app.privateApp private: app.privateApp,
})); sourceMember: {
name: member.name,
avatar: member.avatar,
status: member.status
}
};
});
} }
export default NextAPI(handler); export default NextAPI(handler);

View File

@@ -6,6 +6,8 @@ import { ApiRequestProps } from '@fastgpt/service/type/next';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { VersionListItemType } from '@fastgpt/global/core/app/version'; import { VersionListItemType } from '@fastgpt/global/core/app/version';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
export type versionListBody = PaginationProps<{ export type versionListBody = PaginationProps<{
appId: string; appId: string;
@@ -15,24 +17,44 @@ export type versionListResponse = PaginationResponse<VersionListItemType>;
async function handler( async function handler(
req: ApiRequestProps<versionListBody>, req: ApiRequestProps<versionListBody>,
res: NextApiResponse<any> _res: NextApiResponse<any>
): Promise<versionListResponse> { ): Promise<versionListResponse> {
const { offset, pageSize, appId } = req.body; const { appId } = req.body;
const { offset, pageSize } = parsePaginationRequest(req);
await authApp({ appId, req, per: WritePermissionVal, authToken: true }); await authApp({ appId, req, per: WritePermissionVal, authToken: true });
const [result, total] = await Promise.all([ const [result, total] = await Promise.all([
MongoAppVersion.find( (async () => {
{ const versions = await MongoAppVersion.find({
appId appId
}, })
'_id appId versionName time isPublish tmbId'
)
.sort({ .sort({
time: -1 time: -1
}) })
.skip(offset) .skip(offset)
.limit(pageSize), .limit(pageSize)
.lean();
const memberList = await MongoTeamMember.find(
{
_id: { $in: versions.map((item) => item.tmbId) }
},
'_id name avatar status'
).lean();
return versions.map((item) => {
const member = memberList.find((member) => String(member._id) === String(item.tmbId));
return {
...item,
sourceMember: {
name: member?.name || '',
avatar: member?.avatar || '',
status: member?.status || ''
}
};
});
})(),
MongoAppVersion.countDocuments({ appId }) MongoAppVersion.countDocuments({ appId })
]); ]);
@@ -43,7 +65,8 @@ async function handler(
versionName: item.versionName, versionName: item.versionName,
time: item.time, time: item.time,
isPublish: item.isPublish, isPublish: item.isPublish,
tmbId: item.tmbId tmbId: item.tmbId,
sourceMember: item.sourceMember
}; };
}); });

View File

@@ -12,7 +12,6 @@ describe('发布应用版本测试', () => {
nodes: [], nodes: [],
edges: [], edges: [],
chatConfig: {}, chatConfig: {},
type: AppTypeEnum.simple,
isPublish: false, isPublish: false,
versionName: '1' versionName: '1'
}; };

View File

@@ -7,6 +7,7 @@ import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { GetHistoriesProps } from '@/global/core/chat/api'; import { GetHistoriesProps } from '@/global/core/chat/api';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { addMonths } from 'date-fns'; import { addMonths } from 'date-fns';
export type getHistoriesQuery = {}; export type getHistoriesQuery = {};
@@ -17,9 +18,10 @@ export type getHistoriesResponse = {};
async function handler( async function handler(
req: ApiRequestProps<getHistoriesBody, getHistoriesQuery>, req: ApiRequestProps<getHistoriesBody, getHistoriesQuery>,
res: ApiResponseType<any> _res: ApiResponseType<any>
): Promise<PaginationResponse<getHistoriesResponse>> { ): Promise<PaginationResponse<getHistoriesResponse>> {
const { appId, shareId, outLinkUid, teamId, teamToken, offset, pageSize, source } = req.body; const { appId, shareId, outLinkUid, teamId, teamToken, source } = req.body;
const { offset, pageSize } = parsePaginationRequest(req);
const match = await (async () => { const match = await (async () => {
if (shareId && outLinkUid) { if (shareId && outLinkUid) {

View File

@@ -13,6 +13,7 @@ import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
import { GetChatTypeEnum } from '@/global/core/chat/constants'; import { GetChatTypeEnum } from '@/global/core/chat/constants';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { ChatItemType } from '@fastgpt/global/core/chat/type'; import { ChatItemType } from '@fastgpt/global/core/chat/type';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
export type getPaginationRecordsQuery = {}; export type getPaginationRecordsQuery = {};
@@ -22,16 +23,11 @@ export type getPaginationRecordsResponse = PaginationResponse<ChatItemType>;
async function handler( async function handler(
req: ApiRequestProps<getPaginationRecordsBody, getPaginationRecordsQuery>, req: ApiRequestProps<getPaginationRecordsBody, getPaginationRecordsQuery>,
res: ApiResponseType<any> _res: ApiResponseType<any>
): Promise<getPaginationRecordsResponse> { ): Promise<getPaginationRecordsResponse> {
const { const { appId, chatId, loadCustomFeedbacks, type = GetChatTypeEnum.normal } = req.body;
appId,
chatId, const { offset, pageSize } = parsePaginationRequest(req);
offset,
pageSize = 10,
loadCustomFeedbacks,
type = GetChatTypeEnum.normal
} = req.body;
if (!appId || !chatId) { if (!appId || !chatId) {
return { return {

View File

@@ -6,6 +6,7 @@ import { ApiRequestProps } from '@fastgpt/service/type/next';
import { ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type'; import { ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
export type ChatInputGuideProps = PaginationProps<{ export type ChatInputGuideProps = PaginationProps<{
appId: string; appId: string;
@@ -17,7 +18,8 @@ async function handler(
req: ApiRequestProps<ChatInputGuideProps>, req: ApiRequestProps<ChatInputGuideProps>,
res: NextApiResponse<any> res: NextApiResponse<any>
): Promise<ChatInputGuideResponse> { ): Promise<ChatInputGuideResponse> {
const { appId, pageSize, offset, searchKey } = req.body; const { appId, searchKey } = req.body;
const { offset, pageSize } = parsePaginationRequest(req);
await authApp({ req, appId, authToken: true, per: ReadPermissionVal }); await authApp({ req, appId, authToken: true, per: ReadPermissionVal });

View File

@@ -10,14 +10,15 @@ import { DatasetDataCollectionName } from '@fastgpt/service/core/dataset/data/sc
import { startTrainingQueue } from '@/service/core/dataset/training/utils'; import { startTrainingQueue } from '@/service/core/dataset/training/utils';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { PagingData } from '@/types';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils'; import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { collectionTagsToTagLabel } from '@fastgpt/service/core/dataset/collection/utils'; import { collectionTagsToTagLabel } from '@fastgpt/service/core/dataset/collection/utils';
import { PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
async function handler(req: NextApiRequest): Promise<PagingData<DatasetCollectionsListItemType>> { async function handler(
req: NextApiRequest
): Promise<PaginationResponse<DatasetCollectionsListItemType>> {
let { let {
pageNum = 1,
pageSize = 10,
datasetId, datasetId,
parentId = null, parentId = null,
searchText = '', searchText = '',
@@ -25,8 +26,9 @@ async function handler(req: NextApiRequest): Promise<PagingData<DatasetCollectio
filterTags = [], filterTags = [],
simple = false simple = false
} = req.body as GetDatasetCollectionsProps; } = req.body as GetDatasetCollectionsProps;
searchText = searchText?.replace(/'/g, ''); let { pageSize, offset } = parsePaginationRequest(req);
pageSize = Math.min(pageSize, 30); pageSize = Math.min(pageSize, 30);
searchText = searchText?.replace(/'/g, '');
// auth dataset and get my role // auth dataset and get my role
const { teamId, permission } = await authDataset({ const { teamId, permission } = await authDataset({
@@ -78,9 +80,7 @@ async function handler(req: NextApiRequest): Promise<PagingData<DatasetCollectio
.lean(); .lean();
return { return {
pageNum, list: await Promise.all(
pageSize,
data: await Promise.all(
collections.map(async (item) => ({ collections.map(async (item) => ({
...item, ...item,
tags: await collectionTagsToTagLabel({ tags: await collectionTagsToTagLabel({
@@ -105,7 +105,7 @@ async function handler(req: NextApiRequest): Promise<PagingData<DatasetCollectio
$sort: { updateTime: -1 } $sort: { updateTime: -1 }
}, },
{ {
$skip: (pageNum - 1) * pageSize $skip: offset
}, },
{ {
$limit: pageSize $limit: pageSize
@@ -167,7 +167,7 @@ async function handler(req: NextApiRequest): Promise<PagingData<DatasetCollectio
}) })
]); ]);
const data = await Promise.all( const list = await Promise.all(
collections.map(async (item) => ({ collections.map(async (item) => ({
...item, ...item,
tags: await collectionTagsToTagLabel({ tags: await collectionTagsToTagLabel({
@@ -178,15 +178,13 @@ async function handler(req: NextApiRequest): Promise<PagingData<DatasetCollectio
})) }))
); );
if (data.find((item) => item.trainingAmount > 0)) { if (list.find((item) => item.trainingAmount > 0)) {
startTrainingQueue(); startTrainingQueue();
} }
// count collections // count collections
return { return {
pageNum, list,
pageSize,
data,
total total
}; };
} }

View File

@@ -10,6 +10,7 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { ApiRequestProps } from '@fastgpt/service/type/next'; import { ApiRequestProps } from '@fastgpt/service/type/next';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d'; import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
export type GetScrollCollectionsProps = PaginationProps<{ export type GetScrollCollectionsProps = PaginationProps<{
datasetId: string; datasetId: string;
@@ -25,8 +26,6 @@ async function handler(
): Promise<PaginationResponse<DatasetCollectionsListItemType>> { ): Promise<PaginationResponse<DatasetCollectionsListItemType>> {
let { let {
datasetId, datasetId,
pageSize = 10,
offset,
parentId = null, parentId = null,
searchText = '', searchText = '',
selectFolder = false, selectFolder = false,
@@ -36,6 +35,7 @@ async function handler(
if (!datasetId) { if (!datasetId) {
return Promise.reject(CommonErrEnum.missingParams); return Promise.reject(CommonErrEnum.missingParams);
} }
let { offset, pageSize } = parsePaginationRequest(req);
searchText = searchText?.replace(/'/g, ''); searchText = searchText?.replace(/'/g, '');
pageSize = Math.min(pageSize, 30); pageSize = Math.min(pageSize, 30);

View File

@@ -3,19 +3,21 @@ import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { PagingData, RequestPaging } from '@/types';
import { ApiRequestProps } from '@fastgpt/service/type/next'; import { ApiRequestProps } from '@fastgpt/service/type/next';
import { DatasetDataListItemType } from '@/global/core/dataset/type'; import { DatasetDataListItemType } from '@/global/core/dataset/type';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { PaginationResponse } from '@fastgpt/web/common/fetch/type';
export type GetDatasetDataListProps = RequestPaging & { export type GetDatasetDataListProps = {
searchText?: string; searchText?: string;
collectionId: string; collectionId: string;
}; };
async function handler( async function handler(
req: ApiRequestProps<GetDatasetDataListProps> req: ApiRequestProps<GetDatasetDataListProps>
): Promise<PagingData<DatasetDataListItemType>> { ): Promise<PaginationResponse<DatasetDataListItemType>> {
let { pageNum = 1, pageSize = 10, searchText = '', collectionId } = req.body; let { searchText = '', collectionId } = req.body;
let { offset, pageSize } = parsePaginationRequest(req);
pageSize = Math.min(pageSize, 30); pageSize = Math.min(pageSize, 30);
@@ -40,19 +42,17 @@ async function handler(
: {}) : {})
}; };
const [data, total] = await Promise.all([ const [list, total] = await Promise.all([
MongoDatasetData.find(match, '_id datasetId collectionId q a chunkIndex') MongoDatasetData.find(match, '_id datasetId collectionId q a chunkIndex')
.sort({ chunkIndex: 1, updateTime: -1 }) .sort({ chunkIndex: 1, updateTime: -1 })
.skip((pageNum - 1) * pageSize) .skip(offset)
.limit(pageSize) .limit(pageSize)
.lean(), .lean(),
MongoDatasetData.countDocuments(match) MongoDatasetData.countDocuments(match)
]); ]);
return { return {
pageNum, list,
pageSize,
data,
total total
}; };
} }

View File

@@ -6,6 +6,7 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { ApiRequestProps } from '@fastgpt/service/type/next'; import { ApiRequestProps } from '@fastgpt/service/type/next';
import { DatasetDataListItemType } from '@/global/core/dataset/type'; import { DatasetDataListItemType } from '@/global/core/dataset/type';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
export type GetDatasetDataListProps = PaginationProps & { export type GetDatasetDataListProps = PaginationProps & {
searchText?: string; searchText?: string;
@@ -16,7 +17,8 @@ export type GetDatasetDataListRes = PaginationResponse<DatasetDataListItemType>;
async function handler( async function handler(
req: ApiRequestProps<GetDatasetDataListProps> req: ApiRequestProps<GetDatasetDataListProps>
): Promise<GetDatasetDataListRes> { ): Promise<GetDatasetDataListRes> {
let { offset, pageSize = 10, searchText = '', collectionId } = req.body; let { searchText = '', collectionId } = req.body;
let { offset, pageSize } = parsePaginationRequest(req);
pageSize = Math.min(pageSize, 30); pageSize = Math.min(pageSize, 30);

View File

@@ -2,7 +2,6 @@ import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { getVectorModel } from '@fastgpt/service/core/ai/model';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller'; import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
import { import {
@@ -19,6 +18,7 @@ import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { concatPer } from '@fastgpt/service/support/permission/controller'; import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers'; import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
export type GetDatasetListBody = { export type GetDatasetListBody = {
parentId: ParentIdType; parentId: ParentIdType;
@@ -174,19 +174,20 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
}) })
.filter((app) => app.permission.hasReadPer); .filter((app) => app.permission.hasReadPer);
const data = formatDatasets.map<DatasetListItemType>((item) => ({ const tmbIds = formatDatasets.map((item) => item.tmbId);
_id: item._id, const memberInfo = await MongoTeamMember.find({ _id: { $in: tmbIds } }, '_id name avatar').lean();
avatar: item.avatar,
name: item.name, const data = formatDatasets.map((item) => {
intro: item.intro, const member = memberInfo.find((member) => String(member._id) === String(item.tmbId));
type: item.type,
permission: item.permission, return {
vectorModel: getVectorModel(item.vectorModel), ...item,
inheritPermission: item.inheritPermission, sourceMember: {
tmbId: item.tmbId, name: member!.name,
updateTime: item.updateTime, avatar: member!.avatar
private: item.privateDataset }
})); };
});
return data; return data;
} }

View File

@@ -1,17 +1,18 @@
import { MongoUser } from '@fastgpt/service/support/user/schema'; import { MongoUser } from '@fastgpt/service/support/user/schema';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { UserUpdateParams } from '@/types/user'; import { UserUpdateParams } from '@/types/user';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
/* update user info */ /* update user info */
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { getUserDetail } from '@fastgpt/service/support/user/controller';
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller'; import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
export type UserAccountUpdateQuery = {}; export type UserAccountUpdateQuery = {};
export type UserAccountUpdateBody = UserUpdateParams; export type UserAccountUpdateBody = UserUpdateParams;
export type UserAccountUpdateResponse = {}; export type UserAccountUpdateResponse = {};
async function handler( async function handler(
req: ApiRequestProps<UserAccountUpdateBody, UserAccountUpdateQuery>, req: ApiRequestProps<UserAccountUpdateBody, UserAccountUpdateQuery>,
_res: ApiResponseType<any> _res: ApiResponseType<any>
@@ -19,21 +20,33 @@ async function handler(
const { avatar, timezone } = req.body; const { avatar, timezone } = req.body;
const { tmbId } = await authCert({ req, authToken: true }); const { tmbId } = await authCert({ req, authToken: true });
const user = await getUserDetail({ tmbId }); // const user = await getUserDetail({ tmbId });
// 更新对应的记录 // 更新对应的记录
await mongoSessionRun(async (session) => { await mongoSessionRun(async (session) => {
const tmb = await MongoTeamMember.findById(tmbId).session(session);
if (timezone) {
await MongoUser.updateOne( await MongoUser.updateOne(
{ {
_id: user._id _id: tmb?.userId
}, },
{ {
...(avatar && { avatar }), timezone
...(timezone && { timezone })
} }
).session(session); ).session(session);
}
await refreshSourceAvatar(avatar, user.avatar, session); // if avatar, update team member avatar
if (avatar) {
await MongoTeamMember.updateOne(
{
_id: tmbId
},
{
avatar
}
).session(session);
await refreshSourceAvatar(avatar, tmb?.avatar, session);
}
}); });
return {}; return {};

View File

@@ -13,7 +13,7 @@ import {
ModalBody, ModalBody,
HStack HStack
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar'; import UserBox from '@fastgpt/web/components/common/UserBox';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { getAppChatLogs } from '@/web/core/app/api'; import { getAppChatLogs } from '@/web/core/app/api';
@@ -30,8 +30,6 @@ import { cardStyles } from '../constants';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useMount } from 'ahooks';
const DetailLogsModal = dynamic(() => import('./DetailLogsModal')); const DetailLogsModal = dynamic(() => import('./DetailLogsModal'));
@@ -40,17 +38,11 @@ const Logs = () => {
const { isPc } = useSystem(); const { isPc } = useSystem();
const appId = useContextSelector(AppContext, (v) => v.appId); const appId = useContextSelector(AppContext, (v) => v.appId);
const { teamMembers, loadAndGetTeamMembers } = useUserStore();
useMount(() => {
loadAndGetTeamMembers();
});
const [dateRange, setDateRange] = useState<DateRangeType>({ const [dateRange, setDateRange] = useState<DateRangeType>({
from: addDays(new Date(), -7), from: addDays(new Date(), -7),
to: new Date() to: new Date()
}); });
const { const {
isOpen: isOpenMarkDesc, isOpen: isOpenMarkDesc,
onOpen: onOpenMarkDesc, onOpen: onOpenMarkDesc,
@@ -63,8 +55,7 @@ const Logs = () => {
Pagination, Pagination,
getData, getData,
pageNum pageNum
} = usePagination({ } = usePagination(getAppChatLogs, {
api: getAppChatLogs,
pageSize: 20, pageSize: 20,
params: { params: {
appId, appId,
@@ -139,15 +130,7 @@ const Logs = () => {
{!!item.outLinkUid ? ( {!!item.outLinkUid ? (
item.outLinkUid item.outLinkUid
) : ( ) : (
<HStack> <UserBox sourceMember={item.sourceMember} />
<Avatar
src={teamMembers?.find((v) => v.tmbId === item.tmbId)?.avatar}
w="1.25rem"
/>
<Box fontSize={'sm'} ml={1}>
{teamMembers?.find((v) => v.tmbId === item.tmbId)?.memberName}
</Box>
</HStack>
)} )}
</Box> </Box>
</Td> </Td>

View File

@@ -18,12 +18,11 @@ import Tag from '@fastgpt/web/components/common/Tag';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import MyPopover from '@fastgpt/web/components/common/MyPopover'; import MyPopover from '@fastgpt/web/components/common/MyPopover';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import type { AppVersionSchemaType, VersionListItemType } from '@fastgpt/global/core/app/version'; import type { AppVersionSchemaType, VersionListItemType } from '@fastgpt/global/core/app/version';
import type { SimpleAppSnapshotType } from './SimpleApp/useSnapshots'; import type { SimpleAppSnapshotType } from './SimpleApp/useSnapshots';
import UserBox from '@fastgpt/web/components/common/UserBox';
const PublishHistoriesSlider = <T extends SimpleAppSnapshotType | WorkflowSnapshotsType>({ const PublishHistoriesSlider = <T extends SimpleAppSnapshotType | WorkflowSnapshotsType>({
onClose, onClose,
@@ -183,10 +182,8 @@ const TeamCloud = ({
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appDetail } = useContextSelector(AppContext, (v) => v); const { appDetail } = useContextSelector(AppContext, (v) => v);
const { loadAndGetTeamMembers } = useUserStore();
const { feConfigs } = useSystemStore();
const { scrollDataList, ScrollList, isLoading, fetchData, setData } = useVirtualScrollPagination( const { scrollDataList, ScrollList, isLoading, setData } = useVirtualScrollPagination(
getWorkflowVersionList, getWorkflowVersionList,
{ {
itemHeight: 40, itemHeight: 40,
@@ -198,9 +195,6 @@ const TeamCloud = ({
} }
} }
); );
const { data: members = [] } = useRequest2(loadAndGetTeamMembers, {
manual: !feConfigs.isPlus
});
const [editIndex, setEditIndex] = useState<number | undefined>(undefined); const [editIndex, setEditIndex] = useState<number | undefined>(undefined);
const [hoveredIndex, setHoveredIndex] = useState<number | undefined>(undefined); const [hoveredIndex, setHoveredIndex] = useState<number | undefined>(undefined);
@@ -241,7 +235,6 @@ const TeamCloud = ({
{scrollDataList.map((data, index) => { {scrollDataList.map((data, index) => {
const item = data.data; const item = data.data;
const firstPublishedIndex = scrollDataList.findIndex((data) => data.data.isPublish); const firstPublishedIndex = scrollDataList.findIndex((data) => data.data.isPublish);
const tmb = members.find((member) => member.tmbId === item.tmbId);
return ( return (
<Flex <Flex
@@ -266,23 +259,21 @@ const TeamCloud = ({
h={'72px'} h={'72px'}
Trigger={ Trigger={
<Box> <Box>
<Avatar src={tmb?.avatar} borderRadius={'50%'} w={'24px'} h={'24px'} /> <Avatar
src={data.data.sourceMember.avatar}
borderRadius={'50%'}
w={'24px'}
h={'24px'}
/>
</Box> </Box>
} }
> >
{({ onClose }) => ( {() => (
<Flex alignItems={'center'} h={'full'} pl={5} gap={3}> <Flex alignItems={'center'} h={'full'} pl={5} gap={3}>
<Box> <UserBox sourceMember={data.data.sourceMember} avatarSize="36px" fontSize="sm" />
<Avatar src={tmb?.avatar} borderRadius={'50%'} w={'36px'} h={'36px'} />
</Box>
<Box>
<Box fontSize={'14px'} color={'myGray.900'}>
{tmb?.memberName}
</Box>
<Box fontSize={'12px'} color={'myGray.500'}> <Box fontSize={'12px'} color={'myGray.500'}>
{formatTime2YMDHMS(item.time)} {formatTime2YMDHMS(item.time)}
</Box> </Box>
</Box>
</Flex> </Flex>
)} )}
</MyPopover> </MyPopover>

View File

@@ -50,7 +50,6 @@ import { useWorkflowUtils } from './hooks/useUtils';
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants'; import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useUserStore } from '@/web/support/user/useUserStore';
import { LoopStartNode } from '@fastgpt/global/core/workflow/template/system/loop/loopStart'; import { LoopStartNode } from '@fastgpt/global/core/workflow/template/system/loop/loopStart';
import { LoopEndNode } from '@fastgpt/global/core/workflow/template/system/loop/loopEnd'; import { LoopEndNode } from '@fastgpt/global/core/workflow/template/system/loop/loopEnd';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
@@ -89,8 +88,6 @@ enum TemplateTypeEnum {
const sliderWidth = 460; const sliderWidth = 460;
const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => { const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
const { loadAndGetTeamMembers } = useUserStore();
const [parentId, setParentId] = useState<ParentIdType>(''); const [parentId, setParentId] = useState<ParentIdType>('');
const [searchKey, setSearchKey] = useState(''); const [searchKey, setSearchKey] = useState('');
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
@@ -99,10 +96,6 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const appId = useContextSelector(WorkflowContext, (v) => v.appId); const appId = useContextSelector(WorkflowContext, (v) => v.appId);
const { data: members = [] } = useRequest2(loadAndGetTeamMembers, {
manual: !feConfigs.isPlus
});
const [templateType, setTemplateType] = useState(TemplateTypeEnum.basic); const [templateType, setTemplateType] = useState(TemplateTypeEnum.basic);
const { data: basicNodes } = useRequest2( const { data: basicNodes } = useRequest2(
@@ -162,19 +155,10 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
searchVal?: string; searchVal?: string;
}) => { }) => {
if (type === TemplateTypeEnum.teamPlugin) { if (type === TemplateTypeEnum.teamPlugin) {
const teamApps = await getTeamPlugTemplates({ return getTeamPlugTemplates({
parentId, parentId,
searchKey: searchVal searchKey: searchVal
}).then((res) => res.filter((app) => app.id !== appId)); }).then((res) => res.filter((app) => app.id !== appId));
return teamApps.map<NodeTemplateListItemType>((app) => {
const member = members.find((member) => member.tmbId === app.tmbId);
return {
...app,
author: member?.memberName,
authorAvatar: member?.avatar
};
});
} }
if (type === TemplateTypeEnum.systemPlugin) { if (type === TemplateTypeEnum.systemPlugin) {
return getSystemPlugTemplates({ return getSystemPlugTemplates({
@@ -188,7 +172,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
setParentId(parentId); setParentId(parentId);
setTemplateType(type); setTemplateType(type);
}, },
refreshDeps: [members, searchKey, templateType] refreshDeps: [searchKey, templateType]
} }
); );
@@ -420,7 +404,6 @@ const RenderList = React.memo(function RenderList({
templates, templates,
type, type,
onClose, onClose,
parentId,
setParentId setParentId
}: RenderListProps) { }: RenderListProps) {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -34,8 +34,8 @@ import { postCopyApp } from '@/web/core/app/api/app';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time'; import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useChatStore } from '@/web/core/chat/context/useChatStore'; import { useChatStore } from '@/web/core/chat/context/useChatStore';
import { useUserStore } from '@/web/support/user/useUserStore';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import UserBox from '@fastgpt/web/components/common/UserBox';
const HttpEditModal = dynamic(() => import('./HttpPluginEditModal')); const HttpEditModal = dynamic(() => import('./HttpPluginEditModal'));
const ListItem = () => { const ListItem = () => {
@@ -44,8 +44,6 @@ const ListItem = () => {
const { parentId = null } = router.query; const { parentId = null } = router.query;
const { isPc } = useSystem(); const { isPc } = useSystem();
const { loadAndGetTeamMembers } = useUserStore();
const { openConfirm: openMoveConfirm, ConfirmModal: MoveConfirmModal } = useConfirm({ const { openConfirm: openMoveConfirm, ConfirmModal: MoveConfirmModal } = useConfirm({
type: 'common', type: 'common',
title: t('common:move.confirm'), title: t('common:move.confirm'),
@@ -115,10 +113,6 @@ const ListItem = () => {
successToast: t('app:create_copy_success') successToast: t('app:create_copy_success')
}); });
const { data: members = [] } = useRequest2(loadAndGetTeamMembers, {
manual: false
});
const { runAsync: onResumeInheritPermission } = useRequest2( const { runAsync: onResumeInheritPermission } = useRequest2(
() => { () => {
return resumeInheritPer(editPerApp!._id); return resumeInheritPer(editPerApp!._id);
@@ -145,7 +139,6 @@ const ListItem = () => {
alignItems={'stretch'} alignItems={'stretch'}
> >
{myApps.map((app, index) => { {myApps.map((app, index) => {
const owner = members.find((v) => v.tmbId === app.tmbId);
return ( return (
<MyTooltip <MyTooltip
key={app._id} key={app._id}
@@ -229,15 +222,7 @@ const ListItem = () => {
color={'myGray.500'} color={'myGray.500'}
> >
<HStack spacing={3.5}> <HStack spacing={3.5}>
{owner && ( <UserBox sourceMember={app.sourceMember} fontSize="xs" avatarSize="1.25rem" />
<HStack spacing={1}>
<Avatar src={owner.avatar} w={'0.875rem'} borderRadius={'50%'} />
<Box maxW={'150px'} className="textEllipsis">
{owner.memberName}
</Box>
</HStack>
)}
<PermissionIconText <PermissionIconText
private={app.private} private={app.private}
color={'myGray.500'} color={'myGray.500'}

View File

@@ -111,8 +111,7 @@ const CollectionPageContextProvider = ({ children }: { children: ReactNode }) =>
isLoading: isGetting, isLoading: isGetting,
pageNum, pageNum,
pageSize pageSize
} = usePagination<DatasetCollectionsListItemType>({ } = usePagination(getDatasetCollections, {
api: getDatasetCollections,
pageSize: 20, pageSize: 20,
params: { params: {
datasetId, datasetId,

View File

@@ -28,10 +28,10 @@ import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { useFolderDrag } from '@/components/common/folder/useFolderDrag'; import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import SideTag from './SideTag'; import SideTag from './SideTag';
import { getModelProvider } from '@fastgpt/global/core/ai/provider'; import { getModelProvider } from '@fastgpt/global/core/ai/provider';
import UserBox from '@fastgpt/web/components/common/UserBox';
const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditResourceModal')); const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditResourceModal'));
@@ -39,7 +39,6 @@ function List() {
const { setLoading } = useSystemStore(); const { setLoading } = useSystemStore();
const { isPc } = useSystem(); const { isPc } = useSystem();
const { t } = useTranslation(); const { t } = useTranslation();
const { loadAndGetTeamMembers } = useUserStore();
const { const {
loadMyDatasets, loadMyDatasets,
setMoveDatasetId, setMoveDatasetId,
@@ -81,10 +80,6 @@ function List() {
} }
}); });
const { data: members = [] } = useRequest2(loadAndGetTeamMembers, {
manual: false
});
const editPerDataset = useMemo( const editPerDataset = useMemo(
() => (editPerDatasetIndex !== undefined ? myDatasets[editPerDatasetIndex] : undefined), () => (editPerDatasetIndex !== undefined ? myDatasets[editPerDatasetIndex] : undefined),
[editPerDatasetIndex, myDatasets] [editPerDatasetIndex, myDatasets]
@@ -156,7 +151,6 @@ function List() {
alignItems={'stretch'} alignItems={'stretch'}
> >
{formatDatasets.map((dataset, index) => { {formatDatasets.map((dataset, index) => {
const owner = members.find((v) => v.tmbId === dataset.tmbId);
const vectorModelAvatar = getModelProvider(dataset.vectorModel.provider)?.avatar; const vectorModelAvatar = getModelProvider(dataset.vectorModel.provider)?.avatar;
return ( return (
@@ -265,14 +259,11 @@ function List() {
color={'myGray.500'} color={'myGray.500'}
> >
<HStack spacing={3.5}> <HStack spacing={3.5}>
{owner && ( <UserBox
<HStack spacing={1}> sourceMember={dataset.sourceMember}
<Avatar src={owner.avatar} w={'0.875rem'} borderRadius={'50%'} /> fontSize="xs"
<Box maxW={'150px'} className="textEllipsis" fontSize={'mini'}> avatarSize="1.25rem"
{owner.memberName} />
</Box>
</HStack>
)}
<PermissionIconText <PermissionIconText
flexShrink={0} flexShrink={0}
private={dataset.private} private={dataset.private}

View File

@@ -13,6 +13,8 @@ import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/no
import type { ChatSchema } from '@fastgpt/global/core/chat/type'; import type { ChatSchema } from '@fastgpt/global/core/chat/type';
import type { AppSchema } from '@fastgpt/global/core/app/type'; import type { AppSchema } from '@fastgpt/global/core/app/type';
import { ChatModelType } from '@/constants/model'; import { ChatModelType } from '@/constants/model';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
import { SourceMember } from '@fastgpt/global/support/user/type';
export interface ShareAppItem { export interface ShareAppItem {
_id: string; _id: string;
@@ -45,4 +47,5 @@ export type AppLogsListItemType = {
markCount: number; markCount: number;
outLinkUid?: string; outLinkUid?: string;
tmbId: string; tmbId: string;
sourceMember: SourceMember;
}; };

View File

@@ -10,15 +10,6 @@ import {
import { TrackEventName } from '@/web/common/system/constants'; import { TrackEventName } from '@/web/common/system/constants';
import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type'; import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type';
export type PagingData<T> = {
pageNum: number;
pageSize: number;
data: T[];
total?: number;
};
export type RequestPaging = { pageNum: number; pageSize: number; [key]: any };
declare global { declare global {
var qaQueueLen: number; var qaQueueLen: number;
var vectorQueueLen: number; var vectorQueueLen: number;

View File

@@ -5,7 +5,7 @@ import { AppUpdateParams, AppChangeOwnerBody } from '@/global/core/app/api';
import type { CreateAppBody } from '@/pages/api/core/app/create'; import type { CreateAppBody } from '@/pages/api/core/app/create';
import type { ListAppBody } from '@/pages/api/core/app/list'; import type { ListAppBody } from '@/pages/api/core/app/list';
import { AppLogsListItemType } from '@/types/app'; import { AppLogsListItemType } from '@/types/app';
import { PagingData } from '@/types'; import { PaginationResponse } from '@fastgpt/web/common/fetch/type';
/** /**
* 获取应用列表 * 获取应用列表
@@ -39,7 +39,7 @@ export const putAppById = (id: string, data: AppUpdateParams) =>
// =================== chat logs // =================== chat logs
export const getAppChatLogs = (data: GetAppChatLogsParams) => export const getAppChatLogs = (data: GetAppChatLogsParams) =>
POST<PagingData<AppLogsListItemType>>(`/core/app/getChatLogs`, data); POST<PaginationResponse<AppLogsListItemType>>(`/core/app/getChatLogs`, data);
export const resumeInheritPer = (appId: string) => export const resumeInheritPer = (appId: string) =>
GET(`/core/app/resumeInheritPermission`, { appId }); GET(`/core/app/resumeInheritPermission`, { appId });

View File

@@ -35,7 +35,8 @@ export const getTeamPlugTemplates = (data?: ListAppBody) =>
intro: app.intro, intro: app.intro,
showStatus: false, showStatus: false,
version: app.pluginData?.nodeVersion || defaultNodeVersion, version: app.pluginData?.nodeVersion || defaultNodeVersion,
isTool: true isTool: true,
sourceMember: app.sourceMember
})) }))
); );

View File

@@ -1,5 +1,5 @@
import { PostPublishAppProps } from '@/global/core/app/api'; import { PostPublishAppProps } from '@/global/core/app/api';
import { GET, POST, DELETE, PUT } from '@/web/common/api/request'; import { GET, POST } from '@/web/common/api/request';
import type { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import type { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import { PaginationProps } from '@fastgpt/web/common/fetch/type'; import { PaginationProps } from '@fastgpt/web/common/fetch/type';
import type { import type {

View File

@@ -2,7 +2,7 @@ import { getPaginationRecordsBody } from '@/pages/api/core/chat/getPaginationRec
import { ChatSiteItemType } from '@fastgpt/global/core/chat/type'; import { ChatSiteItemType } from '@fastgpt/global/core/chat/type';
import { PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import React, { ReactNode, useEffect, useMemo, useState } from 'react'; import React, { ReactNode, useMemo, useState } from 'react';
import { createContext, useContextSelector } from 'use-context-selector'; import { createContext, useContextSelector } from 'use-context-selector';
import { ChatItemContext } from './chatItemContext'; import { ChatItemContext } from './chatItemContext';
import { getChatRecords } from '../api'; import { getChatRecords } from '../api';
@@ -68,7 +68,7 @@ const ChatRecordContextProvider = ({
const res = await getChatRecords(data); const res = await getChatRecords(data);
// First load scroll to bottom // First load scroll to bottom
if (data.offset === 0) { if (Number(data.offset) === 0) {
function scrollToBottom() { function scrollToBottom() {
requestAnimationFrame( requestAnimationFrame(
ChatBoxRef?.current ? () => ChatBoxRef?.current?.scrollToBottom?.() : scrollToBottom ChatBoxRef?.current ? () => ChatBoxRef?.current?.scrollToBottom?.() : scrollToBottom

View File

@@ -37,7 +37,6 @@ import type { DatasetCollectionItemType } from '@fastgpt/global/core/dataset/typ
import { DatasetCollectionSyncResultEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetCollectionSyncResultEnum } from '@fastgpt/global/core/dataset/constants';
import type { DatasetDataItemType } from '@fastgpt/global/core/dataset/type'; import type { DatasetDataItemType } from '@fastgpt/global/core/dataset/type';
import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d'; import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d';
import { PagingData } from '@/types';
import type { getDatasetTrainingQueueResponse } from '@/pages/api/core/dataset/training/getDatasetTrainingQueue'; import type { getDatasetTrainingQueueResponse } from '@/pages/api/core/dataset/training/getDatasetTrainingQueue';
import type { rebuildEmbeddingBody } from '@/pages/api/core/dataset/training/rebuildEmbedding'; import type { rebuildEmbeddingBody } from '@/pages/api/core/dataset/training/rebuildEmbedding';
import type { import type {
@@ -66,8 +65,6 @@ import type {
listExistIdQuery, listExistIdQuery,
listExistIdResponse listExistIdResponse
} from '@/pages/api/core/dataset/apiDataset/listExistId'; } from '@/pages/api/core/dataset/apiDataset/listExistId';
import { FeishuServer, YuqueServer } from '@fastgpt/global/core/dataset/apiDataset';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
/* ======================== dataset ======================= */ /* ======================== dataset ======================= */
export const getDatasets = (data: GetDatasetListBody) => export const getDatasets = (data: GetDatasetListBody) =>
@@ -110,7 +107,7 @@ export const postSearchText = (data: SearchTestProps) =>
/* ============================= collections ==================================== */ /* ============================= collections ==================================== */
export const getDatasetCollections = (data: GetDatasetCollectionsProps) => export const getDatasetCollections = (data: GetDatasetCollectionsProps) =>
POST<PagingData<DatasetCollectionsListItemType>>(`/core/dataset/collection/list`, data); POST<PaginationResponse<DatasetCollectionsListItemType>>(`/core/dataset/collection/list`, data);
export const getDatasetCollectionPathById = (parentId: string) => export const getDatasetCollectionPathById = (parentId: string) =>
GET<ParentTreePathItemType[]>(`/core/dataset/collection/paths`, { parentId }); GET<ParentTreePathItemType[]>(`/core/dataset/collection/paths`, { parentId });
export const getDatasetCollectionById = (id: string) => export const getDatasetCollectionById = (id: string) =>

View File

@@ -1,7 +1,7 @@
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import ParentPaths from '@/components/common/ParentPaths'; import ParentPaths from '@/components/common/ParentPaths';
import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { getDatasetCollectionPathById, getDatasetCollections } from '@/web/core/dataset/api'; import { getDatasetCollectionPathById, getDatasetCollections } from '@/web/core/dataset/api';
import { Box, Flex, ModalFooter, Button, useTheme, Grid, Card, ModalBody } from '@chakra-ui/react'; import { Box, Flex, ModalFooter, Button, useTheme, Grid, Card, ModalBody } from '@chakra-ui/react';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants';
@@ -48,7 +48,8 @@ const SelectCollections = ({
useQuery(['loadDatasetDetail', datasetId], () => loadDatasetDetail(datasetId)); useQuery(['loadDatasetDetail', datasetId], () => loadDatasetDetail(datasetId));
const { data, isLoading } = useQuery(['getDatasetCollections', parentId], () => const { data, loading: isLoading } = useRequest2(
() =>
getDatasetCollections({ getDatasetCollections({
datasetId, datasetId,
parentId, parentId,
@@ -56,12 +57,15 @@ const SelectCollections = ({
simple: true, simple: true,
pageNum: 1, pageNum: 1,
pageSize: 50 pageSize: 50
}) }),
{
manual: false,
refreshDeps: [datasetId, parentId, type]
}
); );
const formatCollections = useMemo( const formatCollections = useMemo(
() => () =>
data?.data.map((collection) => { data?.list.map((collection) => {
const icon = getCollectionIcon(collection.type, collection.name); const icon = getCollectionIcon(collection.type, collection.name);
return { return {
@@ -111,7 +115,7 @@ const SelectCollections = ({
title={ title={
<Box> <Box>
<ParentPaths <ParentPaths
paths={paths.map((path, i) => ({ paths={paths.map((path) => ({
parentId: path.parentId, parentId: path.parentId,
parentName: path.parentName parentName: path.parentName
}))} }))}

View File

@@ -1,6 +1,6 @@
import { GET, POST, PUT } from '@/web/common/api/request'; import { GET, POST } from '@/web/common/api/request';
import type { PromotionRecordType } from '@/global/support/api/userRes.d'; import type { PromotionRecordType } from '@/global/support/api/userRes.d';
import { PagingData, type RequestPaging } from '@/types'; import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
/* get promotion init data */ /* get promotion init data */
export const getPromotionInitData = () => export const getPromotionInitData = () =>
@@ -10,5 +10,8 @@ export const getPromotionInitData = () =>
}>('/proApi/support/activity/promotion/getPromotionData'); }>('/proApi/support/activity/promotion/getPromotionData');
/* promotion records */ /* promotion records */
export const getPromotionRecords = (data: RequestPaging) => export const getPromotionRecords = (data: PaginationProps) =>
POST<PagingData<PromotionRecordType>>(`/proApi/support/activity/promotion/getPromotions`, data); POST<PaginationResponse<PromotionRecordType>>(
`/proApi/support/activity/promotion/getPromotions`,
data
);

View File

@@ -90,3 +90,5 @@ export const getCaptchaPic = (username: string) =>
GET<{ GET<{
captchaImage: string; captchaImage: string;
}>('/proApi/support/user/account/captcha/getImgCaptcha', { username }); }>('/proApi/support/user/account/captcha/getImgCaptcha', { username });
export const postSyncMembers = () => POST('/proApi/support/user/team/org/sync');

View File

@@ -1,10 +1,10 @@
import { GET, POST, PUT } from '@/web/common/api/request'; import { GET, POST } from '@/web/common/api/request';
import type { PagingData, RequestPaging } from '@/types';
import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type'; import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type';
import { SystemMsgModalValueType } from '@fastgpt/service/support/user/inform/type'; import { SystemMsgModalValueType } from '@fastgpt/service/support/user/inform/type';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
export const getInforms = (data: RequestPaging) => export const getInforms = (data: PaginationProps) =>
POST<PagingData<UserInformSchema>>(`/proApi/support/user/inform/list`, data); POST<PaginationResponse<UserInformSchema>>(`/proApi/support/user/inform/list`, data);
export const getUnreadCount = () => export const getUnreadCount = () =>
GET<{ GET<{

View File

@@ -19,6 +19,7 @@ import {
} from '@fastgpt/global/support/user/team/type.d'; } from '@fastgpt/global/support/user/team/type.d';
import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type'; import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type'; import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
/* --------------- team ---------------- */ /* --------------- team ---------------- */
export const getTeamList = (status: `${TeamMemberSchema['status']}`) => export const getTeamList = (status: `${TeamMemberSchema['status']}`) =>
@@ -30,8 +31,8 @@ export const putSwitchTeam = (teamId: string) =>
PUT<string>(`/proApi/support/user/team/switch`, { teamId }); PUT<string>(`/proApi/support/user/team/switch`, { teamId });
/* --------------- team member ---------------- */ /* --------------- team member ---------------- */
export const getTeamMembers = () => export const getTeamMembers = (props: PaginationProps) =>
GET<TeamMemberItemType[]>(`/proApi/support/user/team/member/list`); GET<PaginationResponse<TeamMemberItemType>>(`/proApi/support/user/team/member/list`, props);
export const postInviteTeamMember = (data: InviteMemberProps) => export const postInviteTeamMember = (data: InviteMemberProps) =>
POST<InviteMemberResponse>(`/proApi/support/user/team/member/invite`, data); POST<InviteMemberResponse>(`/proApi/support/user/team/member/invite`, data);
export const putUpdateMemberName = (name: string) => export const putUpdateMemberName = (name: string) =>

View File

@@ -30,9 +30,6 @@ type State = {
teamPlanStatus: FeTeamPlanStatusType | null; teamPlanStatus: FeTeamPlanStatusType | null;
initTeamPlanStatus: () => Promise<any>; initTeamPlanStatus: () => Promise<any>;
teamMembers: TeamMemberItemType[];
loadAndGetTeamMembers: (init?: boolean) => Promise<TeamMemberItemType[]>;
teamMemberGroups: MemberGroupListType; teamMemberGroups: MemberGroupListType;
myGroups: MemberGroupListType; myGroups: MemberGroupListType;
loadAndGetGroups: (init?: boolean) => Promise<MemberGroupListType>; loadAndGetGroups: (init?: boolean) => Promise<MemberGroupListType>;
@@ -102,7 +99,7 @@ export const useUserStore = create<State>()(
}, },
// team // team
teamPlanStatus: null, teamPlanStatus: null,
initTeamPlanStatus() { async initTeamPlanStatus() {
return getTeamPlanStatus().then((res) => { return getTeamPlanStatus().then((res) => {
set((state) => { set((state) => {
state.teamPlanStatus = res; state.teamPlanStatus = res;
@@ -110,21 +107,6 @@ export const useUserStore = create<State>()(
return res; return res;
}); });
}, },
teamMembers: [],
loadAndGetTeamMembers: async (init = false) => {
if (!useSystemStore.getState()?.feConfigs?.isPlus) return [];
const randomRefresh = Math.random() > 0.7;
if (!randomRefresh && !init && get().teamMembers?.length)
return Promise.resolve(get().teamMembers);
const res = await getTeamMembers();
set((state) => {
state.teamMembers = res;
});
return res;
},
teamMemberGroups: [], teamMemberGroups: [],
teamOrgs: [], teamOrgs: [],
myGroups: [], myGroups: [],

View File

@@ -1,14 +1,14 @@
import { PagingData, RequestPaging } from '@/types';
import { GET, POST } from '@/web/common/api/request'; import { GET, POST } from '@/web/common/api/request';
import { CreateBillProps, CreateBillResponse } from '@fastgpt/global/support/wallet/bill/api'; import { CreateBillProps, CreateBillResponse } from '@fastgpt/global/support/wallet/bill/api';
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants'; import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
import type { BillSchemaType } from '@fastgpt/global/support/wallet/bill/type.d'; import type { BillSchemaType } from '@fastgpt/global/support/wallet/bill/type.d';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
export const getBills = ( export const getBills = (
data: RequestPaging & { data: PaginationProps<{
type?: BillTypeEnum; type?: BillTypeEnum;
} }>
) => POST<PagingData<BillSchemaType>>(`/proApi/support/wallet/bill/list`, data); ) => POST<PaginationResponse<BillSchemaType>>(`/proApi/support/wallet/bill/list`, data);
export const getWxPayQRCode = (data: CreateBillProps) => export const getWxPayQRCode = (data: CreateBillProps) =>
POST<CreateBillResponse>(`/proApi/support/wallet/bill/create`, data); POST<CreateBillResponse>(`/proApi/support/wallet/bill/create`, data);

View File

@@ -1,8 +1,8 @@
import { PagingData, RequestPaging } from '@/types';
import { GET, POST } from '@/web/common/api/request'; import { GET, POST } from '@/web/common/api/request';
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants'; import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
import { InvoiceType } from '@fastgpt/global/support/wallet/bill/type'; import { InvoiceType } from '@fastgpt/global/support/wallet/bill/type';
import { InvoiceSchemaType } from '@fastgpt/global/support/wallet/bill/type'; import { InvoiceSchemaType } from '@fastgpt/global/support/wallet/bill/type';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
export type invoiceBillDataType = { export type invoiceBillDataType = {
type: BillTypeEnum; type: BillTypeEnum;
price: number; price: number;
@@ -16,5 +16,5 @@ export const getInvoiceBillsList = () =>
export const submitInvoice = (data: InvoiceType) => export const submitInvoice = (data: InvoiceType) =>
POST(`/proApi/support/wallet/bill/invoice/submit`, data); POST(`/proApi/support/wallet/bill/invoice/submit`, data);
export const getInvoiceRecords = (data: RequestPaging) => export const getInvoiceRecords = (data: PaginationProps) =>
POST<PagingData<InvoiceSchemaType>>(`/proApi/support/wallet/bill/invoice/records`, data); POST<PaginationResponse<InvoiceSchemaType>>(`/proApi/support/wallet/bill/invoice/records`, data);

View File

@@ -1,10 +1,17 @@
import { GET, POST } from '@/web/common/api/request'; import { POST } from '@/web/common/api/request';
import { CreateTrainingUsageProps } from '@fastgpt/global/support/wallet/usage/api.d'; import { CreateTrainingUsageProps } from '@fastgpt/global/support/wallet/usage/api.d';
import type { PagingData, RequestPaging } from '@/types'; import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
import type { UsageItemType } from '@fastgpt/global/support/wallet/usage/type'; import type { UsageItemType } from '@fastgpt/global/support/wallet/usage/type';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
export const getUserUsages = (data: RequestPaging) => export const getUserUsages = (
POST<PagingData<UsageItemType>>(`/proApi/support/wallet/usage/getUsage`, data); data: PaginationProps<{
dateStart: Date;
dateEnd: Date;
source?: UsageSourceEnum;
teamMemberId: string;
}>
) => POST<PaginationResponse<UsageItemType>>(`/proApi/support/wallet/usage/getUsage`, data);
export const postCreateTrainingUsage = (data: CreateTrainingUsageProps) => export const postCreateTrainingUsage = (data: CreateTrainingUsageProps) =>
POST<string>(`/support/wallet/usage/createTrainingUsage`, data); POST<string>(`/support/wallet/usage/createTrainingUsage`, data);