mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
4.8.19-feature (#3636)
* 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 * perf: scroll page code * perf: list data * docs:更新用户答疑 (#3576) * docs: add custom uid docs (#3572) * fix: pagination bug (#3577) * 4.8.19 test (#3584) * faet: dataset search filter * fix: scroll page * fix: collection list api old version (#3591) * fix: collection list api format * fix: type error of addSourceMemeber * fix: scroll fetch (#3592) * fix: yuque dataset file folder can enter (#3593) * perf: load members;perf: yuque load;fix: workflow llm params cannot close (#3594) * chat openapi doc * feat: dataset openapi doc * perf: load members * perf: member load code * perf: yuque load * fix: workflow llm params cannot close * fix: api dataset reference tag preview (#3600) * perf: doc * feat: chat page config * fix: http parse (#3634) * update doc * fix: http parse * fix code run node reset template (#3633) Co-authored-by: Archer <545436317@qq.com> * docs:faq (#3627) * docs:faq * docsFix * perf: sleep plugin * fix: selector --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: Jiangween <145003935+Jiangween@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
@@ -40,7 +40,7 @@ export type FastGPTConfigFileType = {
|
||||
|
||||
export type FastGPTFeConfigsType = {
|
||||
show_emptyChat?: boolean;
|
||||
register_method?: ['email' | 'phone'];
|
||||
register_method?: ['email' | 'phone' | 'sync'];
|
||||
login_method?: ['email' | 'phone']; // Attention: login method is diffrent with oauth
|
||||
find_password_method?: ['email' | 'phone'];
|
||||
bind_notification_method?: ['email' | 'phone'];
|
||||
@@ -76,7 +76,6 @@ export type FastGPTFeConfigsType = {
|
||||
wecom?: {
|
||||
corpid?: string;
|
||||
agentid?: string;
|
||||
secret?: string;
|
||||
};
|
||||
microsoft?: {
|
||||
clientId?: string;
|
||||
|
@@ -33,7 +33,7 @@ export const defaultWhisperConfig: AppWhisperConfigType = {
|
||||
export const defaultQGConfig: AppQGConfigType = {
|
||||
open: false,
|
||||
model: 'gpt-4o-mini',
|
||||
customPrompt: PROMPT_QUESTION_GUIDE
|
||||
customPrompt: ''
|
||||
};
|
||||
|
||||
export const defaultChatInputGuideConfig = {
|
||||
|
4
packages/global/core/app/type.d.ts
vendored
4
packages/global/core/app/type.d.ts
vendored
@@ -12,8 +12,9 @@ import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/use
|
||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||
import { AppPermission } from '../../support/permission/app/controller';
|
||||
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 { SourceMemberType } from '../../support/user/type';
|
||||
|
||||
export type AppSchema = {
|
||||
_id: string;
|
||||
@@ -63,6 +64,7 @@ export type AppListItemType = {
|
||||
permission: AppPermission;
|
||||
inheritPermission?: boolean;
|
||||
private?: boolean;
|
||||
sourceMember: SourceMemberType;
|
||||
};
|
||||
|
||||
export type AppDetailType = AppSchema & {
|
||||
|
3
packages/global/core/app/version.d.ts
vendored
3
packages/global/core/app/version.d.ts
vendored
@@ -1,5 +1,7 @@
|
||||
import { TeamMemberStatusEnum } from 'support/user/team/constant';
|
||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||
import { AppChatConfigType, AppSchema } from './type';
|
||||
import { SourceMemberType } from 'support/user/type';
|
||||
|
||||
export type AppVersionSchemaType = {
|
||||
_id: string;
|
||||
@@ -20,4 +22,5 @@ export type VersionListItemType = {
|
||||
time: Date;
|
||||
isPublish: boolean | undefined;
|
||||
tmbId: string;
|
||||
sourceMember: SourceMemberType;
|
||||
};
|
||||
|
1
packages/global/core/dataset/apiDataset.d.ts
vendored
1
packages/global/core/dataset/apiDataset.d.ts
vendored
@@ -5,6 +5,7 @@ export type APIFileItem = {
|
||||
type: 'file' | 'folder';
|
||||
updateTime: Date;
|
||||
createTime: Date;
|
||||
hasChild?: boolean;
|
||||
};
|
||||
|
||||
export type APIFileServer = {
|
||||
|
2
packages/global/core/dataset/type.d.ts
vendored
2
packages/global/core/dataset/type.d.ts
vendored
@@ -11,6 +11,7 @@ import {
|
||||
import { DatasetPermission } from '../../support/permission/dataset/controller';
|
||||
import { Permission } from '../../support/permission/controller';
|
||||
import { APIFileServer, FeishuServer, YuqueServer } from './apiDataset';
|
||||
import { SourceMemberType } from 'support/user/type';
|
||||
|
||||
export type DatasetSchemaType = {
|
||||
_id: string;
|
||||
@@ -165,6 +166,7 @@ export type DatasetListItemType = {
|
||||
vectorModel: VectorModelItemType;
|
||||
inheritPermission: boolean;
|
||||
private?: boolean;
|
||||
sourceMember?: SourceMemberType;
|
||||
};
|
||||
|
||||
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
|
||||
|
@@ -34,7 +34,7 @@ export function getSourceNameIcon({
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
return 'file/fill/manual';
|
||||
return 'file/fill/file';
|
||||
}
|
||||
|
||||
/* get dataset data default index */
|
||||
|
@@ -152,6 +152,7 @@ export enum NodeInputKeyEnum {
|
||||
datasetSearchExtensionModel = 'datasetSearchExtensionModel',
|
||||
datasetSearchExtensionBg = 'datasetSearchExtensionBg',
|
||||
collectionFilterMatch = 'collectionFilterMatch',
|
||||
authTmbId = 'authTmbId',
|
||||
|
||||
// concat dataset
|
||||
datasetQuoteList = 'system_datasetQuoteList',
|
||||
|
@@ -41,6 +41,10 @@ export type ChatDispatchProps = {
|
||||
teamId: string;
|
||||
tmbId: string; // App tmbId
|
||||
};
|
||||
runningUserInfo: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
};
|
||||
uid: string; // Who run this workflow
|
||||
|
||||
chatId?: string;
|
||||
|
@@ -89,6 +89,13 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.authTmbId,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: '',
|
||||
valueType: WorkflowIOValueTypeEnum.boolean,
|
||||
value: false
|
||||
},
|
||||
{
|
||||
...Input_Template_UserChatInput,
|
||||
toolDescription: i18nT('workflow:content_to_search')
|
||||
|
1
packages/global/core/workflow/type/node.d.ts
vendored
1
packages/global/core/workflow/type/node.d.ts
vendored
@@ -89,6 +89,7 @@ export type NodeTemplateListItemType = {
|
||||
hasTokenFee?: boolean; // 是否配置积分
|
||||
instructions?: string; // 使用说明
|
||||
courseUrl?: string; // 教程链接
|
||||
sourceMember?: SourceMember;
|
||||
};
|
||||
|
||||
export type NodeTemplateListType = {
|
||||
|
@@ -12,6 +12,7 @@ export type CreateTeamProps = {
|
||||
avatar?: string;
|
||||
defaultTeam?: boolean;
|
||||
memberName?: string;
|
||||
memberAvatar?: string;
|
||||
};
|
||||
export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & {
|
||||
name?: string;
|
||||
|
@@ -5,8 +5,6 @@ export const OrgMemberCollectionName = 'team_org_members';
|
||||
|
||||
export const getOrgChildrenPath = (org: OrgSchemaType) => `${org.path}/${org.pathId}`;
|
||||
|
||||
// export enum OrgMemberRole {
|
||||
// owner = 'owner',
|
||||
// admin = 'admin',
|
||||
// member = 'member'
|
||||
// }
|
||||
export enum SyncOrgSourceEnum {
|
||||
wecom = 'wecom'
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ type OrgSchemaType = {
|
||||
};
|
||||
|
||||
type OrgMemberSchemaType = {
|
||||
_id: string;
|
||||
teamId: string;
|
||||
orgId: string;
|
||||
tmbId: string;
|
||||
@@ -20,6 +21,6 @@ type OrgMemberSchemaType = {
|
||||
|
||||
type OrgType = Omit<OrgSchemaType, 'avatar'> & {
|
||||
avatar: string;
|
||||
members: OrgMemberSchemaType[];
|
||||
permission: TeamPermission;
|
||||
members: OrgMemberSchemaType[];
|
||||
};
|
||||
|
1
packages/global/support/user/team/type.d.ts
vendored
1
packages/global/support/user/team/type.d.ts
vendored
@@ -44,6 +44,7 @@ export type TeamMemberSchema = {
|
||||
name: string;
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
status: `${TeamMemberStatusEnum}`;
|
||||
avatar: string;
|
||||
defaultTeam: boolean;
|
||||
};
|
||||
|
||||
|
10
packages/global/support/user/type.d.ts
vendored
10
packages/global/support/user/type.d.ts
vendored
@@ -1,12 +1,12 @@
|
||||
import { TeamPermission } from '../permission/user/controller';
|
||||
import { UserStatusEnum } from './constant';
|
||||
import { TeamMemberStatusEnum } from './team/constant';
|
||||
import { TeamTmbItemType } from './team/type';
|
||||
|
||||
export type UserModelSchema = {
|
||||
_id: string;
|
||||
username: string;
|
||||
password: string;
|
||||
avatar: string;
|
||||
promotionRate: number;
|
||||
inviterId?: string;
|
||||
openaiKey: string;
|
||||
@@ -22,7 +22,7 @@ export type UserModelSchema = {
|
||||
export type UserType = {
|
||||
_id: string;
|
||||
username: string;
|
||||
avatar: string;
|
||||
avatar: string; // it should be team member's avatar after 4.8.18
|
||||
timezone: string;
|
||||
promotionRate: UserModelSchema['promotionRate'];
|
||||
team: TeamTmbItemType;
|
||||
@@ -30,3 +30,9 @@ export type UserType = {
|
||||
notificationAccount?: string;
|
||||
permission: TeamPermission;
|
||||
};
|
||||
|
||||
export type SourceMemberType = {
|
||||
name: string;
|
||||
avatar: string;
|
||||
status: `${TeamMemberStatusEnum}`;
|
||||
};
|
||||
|
@@ -12,7 +12,8 @@ const staticPluginList = [
|
||||
'DingTalkWebhook',
|
||||
'WeWorkWebhook',
|
||||
'google',
|
||||
'bing'
|
||||
'bing',
|
||||
'delay'
|
||||
];
|
||||
// Run in worker thread (Have npm packages)
|
||||
const packagePluginList = [
|
||||
@@ -28,8 +29,7 @@ const packagePluginList = [
|
||||
'databaseConnection',
|
||||
'Doc2X',
|
||||
'Doc2X/PDF2text',
|
||||
'searchXNG',
|
||||
'sleep'
|
||||
'searchXNG'
|
||||
];
|
||||
|
||||
export const list = [...staticPluginList, ...packagePluginList];
|
||||
|
18
packages/plugins/src/delay/index.ts
Normal file
18
packages/plugins/src/delay/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
|
||||
type Props = {
|
||||
ms: number;
|
||||
};
|
||||
type Response = Promise<Number>;
|
||||
|
||||
const main = async ({ ms }: Props): Response => {
|
||||
if (typeof ms !== 'number' || ms <= 0 || ms > 300000) {
|
||||
return ms;
|
||||
}
|
||||
|
||||
await delay(ms);
|
||||
|
||||
return ms;
|
||||
};
|
||||
|
||||
export default main;
|
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"author": "collin",
|
||||
"version": "4817",
|
||||
"name": "流程暂停",
|
||||
"name": "流程等待",
|
||||
"avatar": "core/workflow/template/sleep",
|
||||
"intro": "让工作流暂停指定时间后运行",
|
||||
"intro": "让工作流等待指定时间后运行",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
"weight": 1,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "tools",
|
||||
@@ -20,22 +20,19 @@
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 616.4226348688949,
|
||||
"y": -165.05298493910115
|
||||
"x": 627.6352390819724,
|
||||
"y": -165.05298493910118
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": [
|
||||
"numberInput",
|
||||
"reference"
|
||||
],
|
||||
"renderTypeList": ["numberInput", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "number",
|
||||
"canEdit": true,
|
||||
"key": "ms",
|
||||
"label": "ms",
|
||||
"description": "需要暂停的时间,毫秒",
|
||||
"key": "延迟时长",
|
||||
"label": "延迟时长",
|
||||
"description": "需要暂停的时间,单位毫秒",
|
||||
"defaultValue": 1000,
|
||||
"list": [
|
||||
{
|
||||
@@ -47,8 +44,8 @@
|
||||
"canSelectFile": true,
|
||||
"canSelectImg": true,
|
||||
"required": true,
|
||||
"toolDescription": "需要暂停的时间,毫秒",
|
||||
"max": 999999999999,
|
||||
"toolDescription": "需要暂停的时间,单位毫秒",
|
||||
"max": 300000,
|
||||
"min": 1
|
||||
}
|
||||
],
|
||||
@@ -56,8 +53,8 @@
|
||||
{
|
||||
"id": "ms",
|
||||
"valueType": "number",
|
||||
"key": "ms",
|
||||
"label": "ms",
|
||||
"key": "延迟时长",
|
||||
"label": "延迟时长",
|
||||
"type": "hidden"
|
||||
}
|
||||
]
|
||||
@@ -70,15 +67,13 @@
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1925.5772573010433,
|
||||
"y": -131.55298493910115
|
||||
"x": 1921.839722563351,
|
||||
"y": -160.05298493910115
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": [
|
||||
"reference"
|
||||
],
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "any",
|
||||
"canEdit": true,
|
||||
"key": "result",
|
||||
@@ -86,10 +81,7 @@
|
||||
"isToolOutput": true,
|
||||
"description": "",
|
||||
"required": true,
|
||||
"value": [
|
||||
"zCJC6zw7c14i",
|
||||
"httpRawResponse"
|
||||
]
|
||||
"value": ["zCJC6zw7c14i", "httpRawResponse"]
|
||||
}
|
||||
],
|
||||
"outputs": []
|
||||
@@ -116,16 +108,14 @@
|
||||
"flowNodeType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1152.535395637613,
|
||||
"y": -433.21496011686054
|
||||
"x": 1154.4041630064592,
|
||||
"y": -455.0529849391012
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
"renderTypeList": [
|
||||
"addInputParam"
|
||||
],
|
||||
"renderTypeList": ["addInputParam"],
|
||||
"valueType": "dynamic",
|
||||
"label": "",
|
||||
"required": false,
|
||||
@@ -157,9 +147,7 @@
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"renderTypeList": [
|
||||
"custom"
|
||||
],
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
@@ -171,9 +159,7 @@
|
||||
},
|
||||
{
|
||||
"key": "system_httpTimeout",
|
||||
"renderTypeList": [
|
||||
"custom"
|
||||
],
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "number",
|
||||
"label": "",
|
||||
"value": 30,
|
||||
@@ -187,24 +173,20 @@
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "common:core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"value": "sleep",
|
||||
"value": "delay",
|
||||
"valueDesc": "",
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"renderTypeList": [
|
||||
"custom"
|
||||
],
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
@@ -217,9 +199,7 @@
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
@@ -231,9 +211,7 @@
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": "{\n\"ms\": {{$pluginInput.ms$}}\n}",
|
||||
"label": "",
|
||||
@@ -245,9 +223,7 @@
|
||||
},
|
||||
{
|
||||
"key": "system_httpFormBody",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
@@ -259,9 +235,7 @@
|
||||
},
|
||||
{
|
||||
"key": "system_httpContentType",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "string",
|
||||
"value": "json",
|
||||
"label": "",
|
||||
@@ -338,32 +312,7 @@
|
||||
}
|
||||
],
|
||||
"chatConfig": {
|
||||
"welcomeText": "",
|
||||
"variables": [],
|
||||
"questionGuide": {
|
||||
"open": false,
|
||||
"model": "gpt-4o-mini",
|
||||
"customPrompt": "You are an AI assistant tasked with predicting the user's next question based on the conversation history. Your goal is to generate 3 potential questions that will guide the user to continue the conversation. When generating these questions, adhere to the following rules:\n\n1. Use the same language as the user's last question in the conversation history.\n2. Keep each question under 20 characters in length.\n\nAnalyze the conversation history provided to you and use it as context to generate relevant and engaging follow-up questions. Your predictions should be logical extensions of the current topic or related areas that the user might be interested in exploring further.\n\nRemember to maintain consistency in tone and style with the existing conversation while providing diverse options for the user to choose from. Your goal is to keep the conversation flowing naturally and help the user delve deeper into the subject matter or explore related topics."
|
||||
},
|
||||
"ttsConfig": {
|
||||
"type": "web"
|
||||
},
|
||||
"whisperConfig": {
|
||||
"open": false,
|
||||
"autoSend": false,
|
||||
"autoTTSResponse": false
|
||||
},
|
||||
"chatInputGuide": {
|
||||
"open": false,
|
||||
"textList": [],
|
||||
"customUrl": ""
|
||||
},
|
||||
"instruction": "",
|
||||
"autoExecute": {
|
||||
"open": false,
|
||||
"defaultPrompt": ""
|
||||
},
|
||||
"_id": "6784ba2c227f92f6c723ead0"
|
||||
"welcomeText": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
type Props = {
|
||||
ms: number;
|
||||
};
|
||||
type Response = Promise<{
|
||||
result: any;
|
||||
}>;
|
||||
|
||||
const main = async ({ ms }: Props): Response => {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
return {
|
||||
result: ms
|
||||
};
|
||||
};
|
||||
|
||||
export default main;
|
21
packages/service/common/api/pagination.ts
Normal file
21
packages/service/common/api/pagination.ts
Normal 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)
|
||||
};
|
||||
}
|
@@ -2,7 +2,7 @@ import type { NextApiResponse, NextApiRequest } from 'next';
|
||||
import NextCors from 'nextjs-cors';
|
||||
|
||||
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 origin = req.headers.origin;
|
||||
|
@@ -99,7 +99,13 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
|
||||
if (files.some((file) => !file.id || !file.name || typeof file.type === 'undefined')) {
|
||||
return Promise.reject('Invalid file data format');
|
||||
}
|
||||
return files;
|
||||
|
||||
const formattedFiles = files.map((file) => ({
|
||||
...file,
|
||||
hasChild: file.type === 'folder'
|
||||
}));
|
||||
|
||||
return formattedFiles;
|
||||
};
|
||||
|
||||
const getFileContent = async ({ teamId, apiFileId }: { teamId: string; apiFileId: string }) => {
|
||||
|
@@ -284,7 +284,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
{
|
||||
_id: { $in: collectionIdList }
|
||||
},
|
||||
'_id name fileId rawLink externalFileId externalFileUrl',
|
||||
'_id name fileId rawLink apiFileId externalFileId externalFileUrl',
|
||||
{ ...readFromSecondary }
|
||||
).lean()
|
||||
]);
|
||||
@@ -525,7 +525,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
{
|
||||
_id: { $in: searchResults.map((item) => item.collectionId) }
|
||||
},
|
||||
'_id name fileId rawLink externalFileId externalFileUrl',
|
||||
'_id name fileId rawLink apiFileId externalFileId externalFileUrl',
|
||||
{ ...readFromSecondary }
|
||||
).lean()
|
||||
]);
|
||||
|
39
packages/service/core/dataset/utils.ts
Normal file
39
packages/service/core/dataset/utils.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { getTmbInfoByTmbId } from '../../support/user/team/controller';
|
||||
import { getResourcePermission } from '../../support/permission/controller';
|
||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
|
||||
|
||||
// TODO: 需要优化成批量获取权限
|
||||
export const filterDatasetsByTmbId = async ({
|
||||
datasetIds,
|
||||
tmbId
|
||||
}: {
|
||||
datasetIds: string[];
|
||||
tmbId: string;
|
||||
}) => {
|
||||
const { teamId, permission: tmbPer } = await getTmbInfoByTmbId({ tmbId });
|
||||
|
||||
// First get all permissions
|
||||
const permissions = await Promise.all(
|
||||
datasetIds.map(async (datasetId) => {
|
||||
const per = await getResourcePermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId: datasetId,
|
||||
resourceType: PerResourceTypeEnum.dataset
|
||||
});
|
||||
|
||||
if (per === undefined) return false;
|
||||
|
||||
const datasetPer = new DatasetPermission({
|
||||
per,
|
||||
isOwner: tmbPer.isOwner
|
||||
});
|
||||
|
||||
return datasetPer.hasReadPer;
|
||||
})
|
||||
);
|
||||
|
||||
// Then filter datasetIds based on permissions
|
||||
return datasetIds.filter((_, index) => permissions[index]);
|
||||
};
|
@@ -17,6 +17,7 @@ import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { checkTeamReRankPermission } from '../../../../support/permission/teamLimit';
|
||||
import { MongoDataset } from '../../../dataset/schema';
|
||||
import { i18nT } from '../../../../../web/i18n/utils';
|
||||
import { filterDatasetsByTmbId } from '../../../dataset/utils';
|
||||
|
||||
type DatasetSearchProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.datasetSelectList]: SelectedDatasetType;
|
||||
@@ -29,6 +30,7 @@ type DatasetSearchProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.datasetSearchExtensionModel]: string;
|
||||
[NodeInputKeyEnum.datasetSearchExtensionBg]: string;
|
||||
[NodeInputKeyEnum.collectionFilterMatch]: string;
|
||||
[NodeInputKeyEnum.authTmbId]: boolean;
|
||||
}>;
|
||||
export type DatasetSearchResponse = DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.datasetQuoteQA]: SearchDataResponseItemType[];
|
||||
@@ -39,6 +41,7 @@ export async function dispatchDatasetSearch(
|
||||
): Promise<DatasetSearchResponse> {
|
||||
const {
|
||||
runningAppInfo: { teamId },
|
||||
runningUserInfo: { tmbId },
|
||||
histories,
|
||||
node,
|
||||
params: {
|
||||
@@ -52,7 +55,8 @@ export async function dispatchDatasetSearch(
|
||||
datasetSearchUsingExtensionQuery,
|
||||
datasetSearchExtensionModel,
|
||||
datasetSearchExtensionBg,
|
||||
collectionFilterMatch
|
||||
collectionFilterMatch,
|
||||
authTmbId = false
|
||||
}
|
||||
} = props as DatasetSearchProps;
|
||||
|
||||
@@ -64,18 +68,20 @@ export async function dispatchDatasetSearch(
|
||||
return Promise.reject(i18nT('common:core.chat.error.Select dataset empty'));
|
||||
}
|
||||
|
||||
const emptyResult = {
|
||||
quoteQA: [],
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: 0,
|
||||
query: '',
|
||||
limit,
|
||||
searchMode
|
||||
},
|
||||
nodeDispatchUsages: [],
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: []
|
||||
};
|
||||
|
||||
if (!userChatInput) {
|
||||
return {
|
||||
quoteQA: [],
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: 0,
|
||||
query: '',
|
||||
limit,
|
||||
searchMode
|
||||
},
|
||||
nodeDispatchUsages: [],
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: []
|
||||
};
|
||||
return emptyResult;
|
||||
}
|
||||
|
||||
// query extension
|
||||
@@ -83,13 +89,24 @@ export async function dispatchDatasetSearch(
|
||||
? getLLMModel(datasetSearchExtensionModel)
|
||||
: undefined;
|
||||
|
||||
const { concatQueries, rewriteQuery, aiExtensionResult } = await datasetSearchQueryExtension({
|
||||
query: userChatInput,
|
||||
extensionModel,
|
||||
extensionBg: datasetSearchExtensionBg,
|
||||
histories: getHistories(6, histories)
|
||||
});
|
||||
const [{ concatQueries, rewriteQuery, aiExtensionResult }, datasetIds] = await Promise.all([
|
||||
datasetSearchQueryExtension({
|
||||
query: userChatInput,
|
||||
extensionModel,
|
||||
extensionBg: datasetSearchExtensionBg,
|
||||
histories: getHistories(6, histories)
|
||||
}),
|
||||
authTmbId
|
||||
? filterDatasetsByTmbId({
|
||||
datasetIds: datasets.map((item) => item.datasetId),
|
||||
tmbId
|
||||
})
|
||||
: Promise.resolve(datasets.map((item) => item.datasetId))
|
||||
]);
|
||||
|
||||
if (datasetIds.length === 0) {
|
||||
return emptyResult;
|
||||
}
|
||||
// console.log(concatQueries, rewriteQuery, aiExtensionResult);
|
||||
|
||||
// get vector
|
||||
@@ -110,7 +127,7 @@ export async function dispatchDatasetSearch(
|
||||
model: vectorModel.model,
|
||||
similarity,
|
||||
limit,
|
||||
datasetIds: datasets.map((item) => item.datasetId),
|
||||
datasetIds,
|
||||
searchMode,
|
||||
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)),
|
||||
collectionFilterMatch
|
||||
|
@@ -72,6 +72,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
showStatus: false
|
||||
};
|
||||
});
|
||||
|
||||
const runtimeVariables = {
|
||||
...filterSystemVariables(props.variables),
|
||||
appId: String(plugin.id)
|
||||
|
@@ -127,8 +127,16 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
if (typeof val === 'object') return JSON.stringify(val);
|
||||
|
||||
if (typeof val === 'string') {
|
||||
const str = JSON.stringify(val);
|
||||
return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
|
||||
try {
|
||||
const parsed = JSON.parse(val);
|
||||
if (typeof parsed === 'object') {
|
||||
return JSON.stringify(parsed);
|
||||
}
|
||||
return val;
|
||||
} catch (error) {
|
||||
const str = JSON.stringify(val);
|
||||
return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
|
||||
}
|
||||
}
|
||||
|
||||
return String(val);
|
||||
@@ -235,7 +243,9 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
}
|
||||
if (!httpJsonBody) return {};
|
||||
if (httpContentType === ContentTypes.json) {
|
||||
return json5.parse(replaceJsonBodyString(httpJsonBody));
|
||||
httpJsonBody = replaceJsonBodyString(httpJsonBody);
|
||||
console.log(httpJsonBody);
|
||||
return json5.parse(httpJsonBody);
|
||||
}
|
||||
|
||||
// Raw text, xml
|
||||
|
@@ -17,7 +17,6 @@ import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/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 { getOrgIdSetWithParentByTmbId } from './org/controllers';
|
||||
|
||||
@@ -151,13 +150,9 @@ export const getClbsAndGroupsWithInfo = async ({
|
||||
$exists: true
|
||||
}
|
||||
})
|
||||
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({
|
||||
.populate<{ tmb: TeamMemberSchema }>({
|
||||
path: 'tmb',
|
||||
select: 'name userId role',
|
||||
populate: {
|
||||
path: 'user',
|
||||
select: 'avatar'
|
||||
}
|
||||
select: 'name userId avatar'
|
||||
})
|
||||
.lean(),
|
||||
MongoResourcePermission.find({
|
||||
|
@@ -47,14 +47,11 @@ export const OrgSchema = new Schema(
|
||||
OrgSchema.virtual('members', {
|
||||
ref: OrgMemberCollectionName,
|
||||
localField: '_id',
|
||||
foreignField: 'orgId'
|
||||
foreignField: 'orgId',
|
||||
match: function (this: OrgSchemaType) {
|
||||
return { teamId: this.teamId };
|
||||
}
|
||||
});
|
||||
// OrgSchema.virtual('permission', {
|
||||
// ref: ResourcePermissionCollectionName,
|
||||
// localField: '_id',
|
||||
// foreignField: 'orgId',
|
||||
// justOne: true
|
||||
// });
|
||||
|
||||
try {
|
||||
OrgSchema.index({
|
||||
|
@@ -41,7 +41,7 @@ export async function getUserDetail({
|
||||
return {
|
||||
_id: user._id,
|
||||
username: user.username,
|
||||
avatar: user.avatar,
|
||||
avatar: tmb.avatar,
|
||||
timezone: user.timezone,
|
||||
promotionRate: user.promotionRate,
|
||||
team: tmb,
|
||||
|
@@ -3,7 +3,6 @@ const { Schema } = connectionMongo;
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
|
||||
import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils';
|
||||
|
||||
export const userCollectionName = 'users';
|
||||
|
||||
@@ -33,11 +32,6 @@ const UserSchema = new Schema({
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: () => getRandomUserAvatar()
|
||||
},
|
||||
|
||||
promotionRate: {
|
||||
type: Number,
|
||||
default: 15
|
||||
@@ -62,7 +56,10 @@ const UserSchema = new Schema({
|
||||
ref: userCollectionName
|
||||
},
|
||||
fastgpt_sem: Object,
|
||||
sourceDomain: String
|
||||
sourceDomain: String,
|
||||
|
||||
/** @deprecated */
|
||||
avatar: String
|
||||
});
|
||||
|
||||
try {
|
||||
|
@@ -37,7 +37,7 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
|
||||
teamAvatar: tmb.team.avatar,
|
||||
teamName: tmb.team.name,
|
||||
memberName: tmb.name,
|
||||
avatar: tmb.team.avatar,
|
||||
avatar: tmb.avatar,
|
||||
balance: tmb.team.balance,
|
||||
tmbId: String(tmb._id),
|
||||
teamDomain: tmb.team?.teamDomain,
|
||||
|
@@ -7,6 +7,7 @@ import {
|
||||
TeamMemberCollectionName,
|
||||
TeamCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils';
|
||||
|
||||
const TeamMemberSchema = new Schema({
|
||||
teamId: {
|
||||
@@ -19,6 +20,10 @@ const TeamMemberSchema = new Schema({
|
||||
ref: userCollectionName,
|
||||
required: true
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: () => getRandomUserAvatar()
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: 'Member'
|
||||
@@ -39,7 +44,6 @@ const TeamMemberSchema = new Schema({
|
||||
// Abandoned
|
||||
role: {
|
||||
type: String
|
||||
// enum: Object.keys(TeamMemberRoleMap) // disable enum validation for old data
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -1,4 +1,7 @@
|
||||
import { SourceMemberType } from '@fastgpt/global/support/user/type';
|
||||
import { MongoTeam } from './team/teamSchema';
|
||||
import { MongoTeamMember } from './team/teamMemberSchema';
|
||||
import { ClientSession } from '../../common/mongo';
|
||||
|
||||
/* export dataset limit */
|
||||
export const updateExportDatasetLimit = async (teamId: string) => {
|
||||
@@ -67,3 +70,41 @@ export const checkWebSyncLimit = async ({
|
||||
return Promise.reject(`每个团队,每 ${limitMinutes} 分钟仅使用一次同步功能。`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will add a property named sourceMember to the list passed in.
|
||||
* @param list The list to add the sourceMember property to. [TmbId] property is required.
|
||||
* @error If member is not found, this item will be skipped.
|
||||
* @returns The list with the sourceMember property added.
|
||||
*/
|
||||
export async function addSourceMember<T extends { tmbId: string }>({
|
||||
list,
|
||||
session
|
||||
}: {
|
||||
list: T[];
|
||||
session?: ClientSession;
|
||||
}): Promise<Array<T & { sourceMember: SourceMemberType }>> {
|
||||
if (!Array.isArray(list)) return [];
|
||||
|
||||
const tmbList = await MongoTeamMember.find(
|
||||
{
|
||||
_id: { $in: list.map((item) => String(item.tmbId)) }
|
||||
},
|
||||
'tmbId name avatar status',
|
||||
{
|
||||
session
|
||||
}
|
||||
).lean();
|
||||
|
||||
return list
|
||||
.map((item) => {
|
||||
const tmb = tmbList.find((tmb) => String(tmb._id) === String(item.tmbId));
|
||||
if (!tmb) return;
|
||||
|
||||
return {
|
||||
...item,
|
||||
sourceMember: { name: tmb.name, avatar: tmb.avatar, status: tmb.status }
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as Array<T & { sourceMember: SourceMemberType }>;
|
||||
}
|
||||
|
15
packages/web/common/fetch/type.d.ts
vendored
15
packages/web/common/fetch/type.d.ts
vendored
@@ -1,8 +1,13 @@
|
||||
export type PaginationProps<T = {}> = T & {
|
||||
offset: number;
|
||||
pageSize: number;
|
||||
};
|
||||
export type PaginationResponse<T = any> = {
|
||||
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
|
||||
type PaginationProps<T = {}> = T & {
|
||||
pageSize: number | string;
|
||||
} & RequireOnlyOne<{
|
||||
offset: number | string;
|
||||
pageNum: number | string;
|
||||
}>;
|
||||
|
||||
type PaginationResponse<T = {}> = {
|
||||
total: number;
|
||||
list: T[];
|
||||
};
|
||||
|
@@ -23,7 +23,7 @@ function AvatarGroup({
|
||||
<Flex position="relative">
|
||||
{avatars.slice(0, max).map((avatar, index) => (
|
||||
<Avatar
|
||||
key={avatar + groupId}
|
||||
key={index}
|
||||
src={avatar}
|
||||
position={index > 0 ? 'absolute' : 'relative'}
|
||||
left={index > 0 ? `${index * 15}px` : 0}
|
||||
|
@@ -21,7 +21,15 @@ import type { ButtonProps, MenuItemProps } from '@chakra-ui/react';
|
||||
import MyIcon from '../Icon';
|
||||
import { useRequest2 } from '../../../hooks/useRequest';
|
||||
import MyDivider from '../MyDivider';
|
||||
import { useScrollPagination } from '../../../hooks/useScrollPagination';
|
||||
|
||||
/** 选择组件 Props 类型
|
||||
* value: 选中的值
|
||||
* placeholder: 占位符
|
||||
* list: 列表数据
|
||||
* isLoading: 是否加载中
|
||||
* ScrollData: 分页滚动数据控制器 [useScrollPagination] 的返回值
|
||||
* */
|
||||
export type SelectProps<T = any> = ButtonProps & {
|
||||
value?: T;
|
||||
placeholder?: string;
|
||||
@@ -34,6 +42,7 @@ export type SelectProps<T = any> = ButtonProps & {
|
||||
}[];
|
||||
isLoading?: boolean;
|
||||
onchange?: (val: T) => any | Promise<any>;
|
||||
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
|
||||
};
|
||||
|
||||
const MySelect = <T = any,>(
|
||||
@@ -44,6 +53,7 @@ const MySelect = <T = any,>(
|
||||
list = [],
|
||||
onchange,
|
||||
isLoading = false,
|
||||
ScrollData,
|
||||
...props
|
||||
}: SelectProps<T>,
|
||||
ref: ForwardedRef<{
|
||||
@@ -87,6 +97,46 @@ const MySelect = <T = any,>(
|
||||
|
||||
const isSelecting = loading || isLoading;
|
||||
|
||||
const ListRender = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
{list.map((item, i) => (
|
||||
<Box key={i}>
|
||||
<MenuItem
|
||||
{...menuItemStyles}
|
||||
{...(value === item.value
|
||||
? {
|
||||
ref: SelectedItemRef,
|
||||
color: 'primary.700',
|
||||
bg: 'myGray.100',
|
||||
fontWeight: '600'
|
||||
}
|
||||
: {
|
||||
color: 'myGray.900'
|
||||
})}
|
||||
onClick={() => {
|
||||
if (onChange && value !== item.value) {
|
||||
onChange(item.value);
|
||||
}
|
||||
}}
|
||||
whiteSpace={'pre-wrap'}
|
||||
fontSize={'sm'}
|
||||
display={'block'}
|
||||
>
|
||||
<Box>{item.label}</Box>
|
||||
{item.description && (
|
||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||
{item.description}
|
||||
</Box>
|
||||
)}
|
||||
</MenuItem>
|
||||
{item.showBorder && <MyDivider my={2} />}
|
||||
</Box>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}, [list, value]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
css={css({
|
||||
@@ -154,39 +204,7 @@ const MySelect = <T = any,>(
|
||||
maxH={'40vh'}
|
||||
overflowY={'auto'}
|
||||
>
|
||||
{list.map((item, i) => (
|
||||
<Box key={i}>
|
||||
<MenuItem
|
||||
{...menuItemStyles}
|
||||
{...(value === item.value
|
||||
? {
|
||||
ref: SelectedItemRef,
|
||||
color: 'primary.700',
|
||||
bg: 'myGray.100',
|
||||
fontWeight: '600'
|
||||
}
|
||||
: {
|
||||
color: 'myGray.900'
|
||||
})}
|
||||
onClick={() => {
|
||||
if (onChange && value !== item.value) {
|
||||
onChange(item.value);
|
||||
}
|
||||
}}
|
||||
whiteSpace={'pre-wrap'}
|
||||
fontSize={'sm'}
|
||||
display={'block'}
|
||||
>
|
||||
<Box>{item.label}</Box>
|
||||
{item.description && (
|
||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||
{item.description}
|
||||
</Box>
|
||||
)}
|
||||
</MenuItem>
|
||||
{item.showBorder && <MyDivider my={2} />}
|
||||
</Box>
|
||||
))}
|
||||
{ScrollData ? <ScrollData>{ListRender}</ScrollData> : ListRender}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</Box>
|
||||
|
23
packages/web/components/common/UserBox/index.tsx
Normal file
23
packages/web/components/common/UserBox/index.tsx
Normal 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('common:user_leaved')}</Tag>}
|
||||
</HStack>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(UserBox);
|
@@ -4,7 +4,6 @@ import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useToast } from './useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
import {
|
||||
useBoolean,
|
||||
useLockFn,
|
||||
@@ -14,37 +13,33 @@ import {
|
||||
useThrottleEffect
|
||||
} from 'ahooks';
|
||||
|
||||
import { PaginationProps, PaginationResponse } from '../common/fetch/type';
|
||||
|
||||
const thresholdVal = 200;
|
||||
|
||||
type PagingData<T> = {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
data: T[];
|
||||
total?: number;
|
||||
};
|
||||
|
||||
export function usePagination<ResT = any>({
|
||||
api,
|
||||
pageSize = 10,
|
||||
params = {},
|
||||
defaultRequest = true,
|
||||
type = 'button',
|
||||
onChange,
|
||||
refreshDeps,
|
||||
scrollLoadType = 'bottom',
|
||||
EmptyTip
|
||||
}: {
|
||||
api: (data: any) => Promise<PagingData<ResT>>;
|
||||
pageSize?: number;
|
||||
params?: Record<string, any>;
|
||||
defaultRequest?: boolean;
|
||||
type?: 'button' | 'scroll';
|
||||
onChange?: (pageNum: number) => void;
|
||||
refreshDeps?: any[];
|
||||
throttleWait?: number;
|
||||
scrollLoadType?: 'top' | 'bottom';
|
||||
EmptyTip?: React.JSX.Element;
|
||||
}) {
|
||||
export function usePagination<DataT, ResT = {}>(
|
||||
api: (data: PaginationProps<DataT>) => Promise<PaginationResponse<ResT>>,
|
||||
{
|
||||
pageSize = 10,
|
||||
params,
|
||||
defaultRequest = true,
|
||||
type = 'button',
|
||||
onChange,
|
||||
refreshDeps,
|
||||
scrollLoadType = 'bottom',
|
||||
EmptyTip
|
||||
}: {
|
||||
pageSize?: number;
|
||||
params?: DataT;
|
||||
defaultRequest?: boolean;
|
||||
type?: 'button' | 'scroll';
|
||||
onChange?: (pageNum: number) => void;
|
||||
refreshDeps?: any[];
|
||||
throttleWait?: number;
|
||||
scrollLoadType?: 'top' | 'bottom';
|
||||
EmptyTip?: React.JSX.Element;
|
||||
}
|
||||
) {
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -64,7 +59,7 @@ export function usePagination<ResT = any>({
|
||||
setTrue();
|
||||
|
||||
try {
|
||||
const res: PagingData<ResT> = await api({
|
||||
const res = await api({
|
||||
pageNum: num,
|
||||
pageSize,
|
||||
...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();
|
||||
} else {
|
||||
setData((prevData) => (num === 1 ? res.data : [...prevData, ...res.data]));
|
||||
setData((prevData) => (num === 1 ? res.list : [...prevData, ...res.list]));
|
||||
}
|
||||
} else {
|
||||
setData(res.data);
|
||||
setData(res.list);
|
||||
}
|
||||
|
||||
onChange?.(num);
|
||||
|
@@ -16,7 +16,7 @@ import MyBox from '../components/common/MyBox';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
type ItemHeight<T> = (index: number, data: T) => number;
|
||||
const thresholdVal = 200;
|
||||
const thresholdVal = 100;
|
||||
|
||||
export type ScrollListType = ({
|
||||
children,
|
||||
@@ -269,8 +269,10 @@ export function useScrollPagination<
|
||||
({
|
||||
children,
|
||||
ScrollContainerRef,
|
||||
isLoading,
|
||||
...props
|
||||
}: {
|
||||
isLoading?: boolean;
|
||||
children: ReactNode;
|
||||
ScrollContainerRef?: RefObject<HTMLDivElement>;
|
||||
} & BoxProps) => {
|
||||
@@ -302,7 +304,7 @@ export function useScrollPagination<
|
||||
);
|
||||
|
||||
return (
|
||||
<Box {...props} ref={ref} overflow={'overlay'}>
|
||||
<MyBox {...props} ref={ref} overflow={'overlay'} isLoading={isLoading}>
|
||||
{scrollLoadType === 'top' && total > 0 && isLoading && (
|
||||
<Box mt={2} fontSize={'xs'} color={'blackAlpha.500'} textAlign={'center'}>
|
||||
{t('common:common.is_requesting')}
|
||||
@@ -325,7 +327,7 @@ export function useScrollPagination<
|
||||
</Box>
|
||||
)}
|
||||
{isEmpty && EmptyTip}
|
||||
</Box>
|
||||
</MyBox>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@@ -39,6 +39,7 @@
|
||||
"classification": "Classification",
|
||||
"click_to_resume": "Click to Resume",
|
||||
"code_editor": "Code Editor",
|
||||
"code_error.account_error": "Incorrect account name or password",
|
||||
"code_error.app_error.invalid_app_type": "Invalid Application Type",
|
||||
"code_error.app_error.invalid_owner": "Unauthorized Application Owner",
|
||||
"code_error.app_error.not_exist": "Application Does Not Exist",
|
||||
@@ -95,7 +96,6 @@
|
||||
"code_error.team_error.website_sync_not_enough": "Unauthorized to Use Website Sync",
|
||||
"code_error.token_error_code.403": "Invalid Login Status, Please Re-login",
|
||||
"code_error.user_error.balance_not_enough": "Insufficient Account Balance",
|
||||
"code_error.account_error": "Incorrect account name or password",
|
||||
"code_error.user_error.bin_visitor_guest": "You Are Currently a Guest, Unauthorized to Operate",
|
||||
"code_error.user_error.un_auth_user": "User Not Found",
|
||||
"common.Action": "Action",
|
||||
@@ -1273,6 +1273,7 @@
|
||||
"user.team.role.Visitor": "visitor",
|
||||
"user.team.role.writer": "writable member",
|
||||
"user.type": "Type",
|
||||
"user_leaved": "Leaved",
|
||||
"verification": "Verification",
|
||||
"workflow.template.communication": "Communication",
|
||||
"xx_search_result": "{{key}} Search Results",
|
||||
|
@@ -13,6 +13,8 @@
|
||||
"append_application_reply_to_history_as_new_context": "Append the application's reply to the history as new context",
|
||||
"application_call": "Application Call",
|
||||
"assigned_reply": "Assigned Reply",
|
||||
"auth_tmb_id": "Auth member",
|
||||
"auth_tmb_id_tip": "After it is turned on, when the application is released to the outside world, the knowledge base will be filtered based on whether the user has permission to the knowledge base.\n\nIf it is not enabled, the configured knowledge base will be searched directly without permission filtering.",
|
||||
"can_not_loop": "This node can't loop.",
|
||||
"choose_another_application_to_call": "Select another application to call",
|
||||
"classification_result": "Classification Result",
|
||||
|
@@ -35,5 +35,8 @@
|
||||
"user_team_invite_member": "邀请成员",
|
||||
"user_team_leave_team": "离开团队",
|
||||
"user_team_leave_team_failed": "离开团队失败",
|
||||
"waiting": "待接受"
|
||||
"waiting": "待接受",
|
||||
"sync_immediately": "立即同步",
|
||||
"sync_member_failed": "同步成员失败",
|
||||
"sync_member_success": "同步成员成功"
|
||||
}
|
||||
|
@@ -43,6 +43,7 @@
|
||||
"classification": "分类",
|
||||
"click_to_resume": "点击恢复",
|
||||
"code_editor": "代码编辑",
|
||||
"code_error.account_error": "账号名或密码错误",
|
||||
"code_error.app_error.invalid_app_type": "错误的应用类型",
|
||||
"code_error.app_error.invalid_owner": "非法的应用所有者",
|
||||
"code_error.app_error.not_exist": "应用不存在",
|
||||
@@ -99,7 +100,6 @@
|
||||
"code_error.team_error.website_sync_not_enough": "无权使用Web站点同步~",
|
||||
"code_error.token_error_code.403": "登录状态无效,请重新登录",
|
||||
"code_error.user_error.balance_not_enough": "账号余额不足~",
|
||||
"code_error.account_error": "账号名或密码错误",
|
||||
"code_error.user_error.bin_visitor_guest": "您当前身份为游客,无权操作",
|
||||
"code_error.user_error.un_auth_user": "找不到该用户",
|
||||
"common.Action": "操作",
|
||||
@@ -1268,6 +1268,7 @@
|
||||
"user.team.role.Visitor": "访客",
|
||||
"user.team.role.writer": "可写成员",
|
||||
"user.type": "类型",
|
||||
"user_leaved": "已离开",
|
||||
"verification": "验证",
|
||||
"workflow.template.communication": "通信",
|
||||
"xx_search_result": "{{key}} 的搜索结果",
|
||||
|
@@ -13,6 +13,8 @@
|
||||
"append_application_reply_to_history_as_new_context": "将该应用回复内容拼接到历史记录中,作为新的上下文返回",
|
||||
"application_call": "应用调用",
|
||||
"assigned_reply": "指定回复",
|
||||
"auth_tmb_id": "使用者鉴权",
|
||||
"auth_tmb_id_tip": "开启后,对外发布该应用时,还会根据用户是否有该知识库权限进行知识库过滤。\n若未开启,则直接按配置的知识库进行检索,不进行权限过滤。",
|
||||
"can_not_loop": "该节点不支持循环嵌套",
|
||||
"choose_another_application_to_call": "选择一个其他应用进行调用",
|
||||
"classification_result": "分类结果",
|
||||
|
@@ -39,6 +39,7 @@
|
||||
"classification": "分類",
|
||||
"click_to_resume": "點選繼續",
|
||||
"code_editor": "程式碼編輯器",
|
||||
"code_error.account_error": "帳號名稱或密碼錯誤",
|
||||
"code_error.app_error.invalid_app_type": "無效的應用程式類型",
|
||||
"code_error.app_error.invalid_owner": "非法的應用程式擁有者",
|
||||
"code_error.app_error.not_exist": "應用程式不存在",
|
||||
@@ -95,7 +96,6 @@
|
||||
"code_error.team_error.website_sync_not_enough": "無權使用網站同步",
|
||||
"code_error.token_error_code.403": "登入狀態無效,請重新登入",
|
||||
"code_error.user_error.balance_not_enough": "帳戶餘額不足",
|
||||
"code_error.account_error": "帳號名稱或密碼錯誤",
|
||||
"code_error.user_error.bin_visitor_guest": "您目前身份為訪客,無權操作",
|
||||
"code_error.user_error.un_auth_user": "找不到此使用者",
|
||||
"common.Action": "操作",
|
||||
@@ -1273,6 +1273,7 @@
|
||||
"user.team.role.Visitor": "訪客",
|
||||
"user.team.role.writer": "可寫入成員",
|
||||
"user.type": "類型",
|
||||
"user_leaved": "已離開",
|
||||
"verification": "驗證",
|
||||
"workflow.template.communication": "通訊",
|
||||
"xx_search_result": "{{key}} 的搜尋結果",
|
||||
|
@@ -13,6 +13,8 @@
|
||||
"append_application_reply_to_history_as_new_context": "將應用程式的回覆附加到歷史紀錄中,作為新的脈絡",
|
||||
"application_call": "應用程式呼叫",
|
||||
"assigned_reply": "指定回覆",
|
||||
"auth_tmb_id": "使用者鑑權",
|
||||
"auth_tmb_id_tip": "開啟後,對外發布應用程式時,也會根據使用者是否有該知識庫權限進行知識庫過濾。\n\n若未開啟,則直接按配置的知識庫進行檢索,不進行權限過濾。",
|
||||
"can_not_loop": "這個節點不能迴圈。",
|
||||
"choose_another_application_to_call": "選擇另一個應用程式來呼叫",
|
||||
"classification_result": "分類結果",
|
||||
|
Reference in New Issue
Block a user