mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-16 08:01:18 +00:00
4.8.10 perf (#2378)
* perf: helpline code * fix: prompt call stream=false response prefix * fix: app chat log auth * perf: new chat i18n * fix: milvus dataset cannot export data * perf: doc intro
This commit is contained in:
@@ -20,4 +20,10 @@ weight: 816
|
|||||||
## V4.8.10 更新说明
|
## V4.8.10 更新说明
|
||||||
|
|
||||||
1. 新增 - 模板市场
|
1. 新增 - 模板市场
|
||||||
2.
|
2. 新增 - 工作流节点拖动自动对齐吸附
|
||||||
|
3. 新增 - 用户选择节点(Debug 模式暂未支持)
|
||||||
|
4. 商业版新增 - 飞书机器人接入
|
||||||
|
5. 商业版新增 - 公众号接入接入
|
||||||
|
6. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
|
||||||
|
7. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
|
||||||
|
8. 修复 - 选择 Milvus 部署时,无法导出知识库。
|
||||||
|
@@ -411,6 +411,7 @@ const parseAnswer = (
|
|||||||
str = str.trim();
|
str = str.trim();
|
||||||
// 首先,使用正则表达式提取TOOL_ID和TOOL_ARGUMENTS
|
// 首先,使用正则表达式提取TOOL_ID和TOOL_ARGUMENTS
|
||||||
const prefixReg = /^1(:|:)/;
|
const prefixReg = /^1(:|:)/;
|
||||||
|
const answerPrefixReg = /^0(:|:)/;
|
||||||
|
|
||||||
if (prefixReg.test(str)) {
|
if (prefixReg.test(str)) {
|
||||||
const toolString = sliceJsonStr(str);
|
const toolString = sliceJsonStr(str);
|
||||||
@@ -432,7 +433,7 @@ const parseAnswer = (
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
answer: str
|
answer: str.replace(answerPrefixReg, '')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
2
projects/app/src/global/core/chat/api.d.ts
vendored
2
projects/app/src/global/core/chat/api.d.ts
vendored
@@ -30,7 +30,7 @@ export type InitChatResponse = {
|
|||||||
chatId?: string;
|
chatId?: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
userAvatar?: string;
|
userAvatar?: string;
|
||||||
title: string;
|
title?: string;
|
||||||
variables: Record<string, any>;
|
variables: Record<string, any>;
|
||||||
history: ChatItemType[];
|
history: ChatItemType[];
|
||||||
app: {
|
app: {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { InitChatResponse } from './api';
|
import { InitChatResponse } from './api';
|
||||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
|
||||||
export const defaultChatData: InitChatResponse = {
|
export const defaultChatData: InitChatResponse = {
|
||||||
chatId: '',
|
chatId: '',
|
||||||
appId: '',
|
appId: '',
|
||||||
@@ -12,7 +12,7 @@ export const defaultChatData: InitChatResponse = {
|
|||||||
type: AppTypeEnum.simple,
|
type: AppTypeEnum.simple,
|
||||||
pluginInputs: []
|
pluginInputs: []
|
||||||
},
|
},
|
||||||
title: i18nT('chat:new_chat'),
|
title: '',
|
||||||
variables: {},
|
variables: {},
|
||||||
history: []
|
history: []
|
||||||
};
|
};
|
||||||
|
@@ -1,11 +1,15 @@
|
|||||||
import { authChatCrud } from '@/service/support/permission/auth/chat';
|
import { authChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
import {
|
||||||
|
ManagePermissionVal,
|
||||||
|
ReadPermissionVal
|
||||||
|
} from '@fastgpt/global/support/permission/constant';
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
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 { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
||||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||||
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
|
|
||||||
export type getResDataQuery = OutLinkChatAuthProps & {
|
export type getResDataQuery = OutLinkChatAuthProps & {
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
@@ -25,12 +29,24 @@ async function handler(
|
|||||||
if (!appId || !chatId || !dataId) {
|
if (!appId || !chatId || !dataId) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Un login api: share chat, team chat
|
||||||
|
// 2. Login api: account chat, chat log
|
||||||
|
try {
|
||||||
await authChatCrud({
|
await authChatCrud({
|
||||||
req,
|
req,
|
||||||
authToken: true,
|
authToken: true,
|
||||||
...req.query,
|
...req.query,
|
||||||
per: ReadPermissionVal
|
per: ReadPermissionVal
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
await authApp({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
appId,
|
||||||
|
per: ManagePermissionVal
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const chatData = await MongoChatItem.findOne({
|
const chatData = await MongoChatItem.findOne({
|
||||||
appId,
|
appId,
|
||||||
|
@@ -14,7 +14,7 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
|||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
|
||||||
async function handler(
|
async function handler(
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse
|
res: NextApiResponse
|
||||||
@@ -62,7 +62,7 @@ async function handler(
|
|||||||
return {
|
return {
|
||||||
chatId,
|
chatId,
|
||||||
appId,
|
appId,
|
||||||
title: chat?.title || i18nT('chat:new_chat'),
|
title: chat?.title,
|
||||||
userAvatar: undefined,
|
userAvatar: undefined,
|
||||||
variables: chat?.variables || {},
|
variables: chat?.variables || {},
|
||||||
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),
|
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),
|
||||||
|
@@ -18,11 +18,9 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
|||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
||||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
let { chatId, shareId, outLinkUid } = req.query as InitOutLinkChatProps;
|
let { chatId, shareId, outLinkUid } = req.query as InitOutLinkChatProps;
|
||||||
|
|
||||||
// auth link permission
|
// auth link permission
|
||||||
@@ -70,7 +68,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
data: {
|
data: {
|
||||||
chatId,
|
chatId,
|
||||||
appId: app._id,
|
appId: app._id,
|
||||||
title: chat?.title || i18nT('chat:new_chat'),
|
title: chat?.title,
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
userAvatar: tmb?.userId?.avatar,
|
userAvatar: tmb?.userId?.avatar,
|
||||||
variables: chat?.variables || {},
|
variables: chat?.variables || {},
|
||||||
@@ -94,14 +92,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
responseLimit: '10mb'
|
responseLimit: '10mb'
|
||||||
|
@@ -18,12 +18,9 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
|||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
import { transformPreviewHistories } from '@/global/core/chat/utils';
|
||||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
let { teamId, appId, chatId, teamToken } = req.query as InitTeamChatProps;
|
let { teamId, appId, chatId, teamToken } = req.query as InitTeamChatProps;
|
||||||
|
|
||||||
if (!teamId || !appId || !teamToken) {
|
if (!teamId || !appId || !teamToken) {
|
||||||
@@ -73,7 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
data: {
|
data: {
|
||||||
chatId,
|
chatId,
|
||||||
appId,
|
appId,
|
||||||
title: chat?.title || i18nT('chat:new_chat'),
|
title: chat?.title,
|
||||||
userAvatar: team?.avatar,
|
userAvatar: team?.avatar,
|
||||||
variables: chat?.variables || {},
|
variables: chat?.variables || {},
|
||||||
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),
|
history: app.type === AppTypeEnum.plugin ? histories : transformPreviewHistories(histories),
|
||||||
@@ -96,14 +93,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
responseLimit: '10mb'
|
responseLimit: '10mb'
|
||||||
|
@@ -18,7 +18,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|||||||
datasetId: string;
|
datasetId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!datasetId || !global.pgClient) {
|
if (!datasetId) {
|
||||||
return Promise.reject(CommonErrEnum.missingParams);
|
return Promise.reject(CommonErrEnum.missingParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -89,7 +89,7 @@ function HelperLinesRenderer({ horizontal, vertical }: HelperLinesProps) {
|
|||||||
drawCross(node.right * transform[2] + transform[0], y, 5 * zoom);
|
drawCross(node.right * transform[2] + transform[0], y, 5 * zoom);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [width, height, transform, horizontal, vertical]);
|
}, [width, height, transform, horizontal, vertical, zoom]);
|
||||||
|
|
||||||
return <canvas ref={canvasRef} style={canvasStyle} />;
|
return <canvas ref={canvasRef} style={canvasStyle} />;
|
||||||
}
|
}
|
||||||
|
@@ -3,14 +3,6 @@ import { WorkflowContext } from '../../context';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { Node, NodePositionChange, XYPosition } from 'reactflow';
|
|
||||||
import { THelperLine } from '@fastgpt/global/core/workflow/type';
|
|
||||||
|
|
||||||
type GetHelperLinesResult = {
|
|
||||||
horizontal?: THelperLine;
|
|
||||||
vertical?: THelperLine;
|
|
||||||
snapPosition: Partial<XYPosition>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useWorkflowUtils = () => {
|
export const useWorkflowUtils = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -40,235 +32,8 @@ export const useWorkflowUtils = () => {
|
|||||||
[nodeList]
|
[nodeList]
|
||||||
);
|
);
|
||||||
|
|
||||||
const getHelperLines = (
|
|
||||||
change: NodePositionChange,
|
|
||||||
nodes: Node[],
|
|
||||||
distance = 8
|
|
||||||
): GetHelperLinesResult => {
|
|
||||||
const nodeA = nodes.find((node) => node.id === change.id);
|
|
||||||
|
|
||||||
if (!nodeA || !change.position) {
|
|
||||||
return {
|
return {
|
||||||
horizontal: undefined,
|
computedNewNodeName
|
||||||
vertical: undefined,
|
|
||||||
snapPosition: { x: undefined, y: undefined }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeABounds = {
|
|
||||||
left: change.position.x,
|
|
||||||
right: change.position.x + (nodeA.width ?? 0),
|
|
||||||
top: change.position.y,
|
|
||||||
bottom: change.position.y + (nodeA.height ?? 0),
|
|
||||||
width: nodeA.width ?? 0,
|
|
||||||
height: nodeA.height ?? 0,
|
|
||||||
centerX: change.position.x + (nodeA.width ?? 0) / 2,
|
|
||||||
centerY: change.position.y + (nodeA.height ?? 0) / 2
|
|
||||||
};
|
|
||||||
|
|
||||||
let horizontalDistance = distance;
|
|
||||||
let verticalDistance = distance;
|
|
||||||
|
|
||||||
return nodes
|
|
||||||
.filter((node) => node.id !== nodeA.id)
|
|
||||||
.reduce<GetHelperLinesResult>(
|
|
||||||
(result, nodeB) => {
|
|
||||||
if (!result.vertical) {
|
|
||||||
result.vertical = {
|
|
||||||
position: nodeABounds.centerX,
|
|
||||||
nodes: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.horizontal) {
|
|
||||||
result.horizontal = {
|
|
||||||
position: nodeABounds.centerY,
|
|
||||||
nodes: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeBBounds = {
|
|
||||||
left: nodeB.position.x,
|
|
||||||
right: nodeB.position.x + (nodeB.width ?? 0),
|
|
||||||
top: nodeB.position.y,
|
|
||||||
bottom: nodeB.position.y + (nodeB.height ?? 0),
|
|
||||||
width: nodeB.width ?? 0,
|
|
||||||
height: nodeB.height ?? 0,
|
|
||||||
centerX: nodeB.position.x + (nodeB.width ?? 0) / 2,
|
|
||||||
centerY: nodeB.position.y + (nodeB.height ?? 0) / 2
|
|
||||||
};
|
|
||||||
|
|
||||||
const distanceLeftLeft = Math.abs(nodeABounds.left - nodeBBounds.left);
|
|
||||||
const distanceRightRight = Math.abs(nodeABounds.right - nodeBBounds.right);
|
|
||||||
const distanceLeftRight = Math.abs(nodeABounds.left - nodeBBounds.right);
|
|
||||||
const distanceRightLeft = Math.abs(nodeABounds.right - nodeBBounds.left);
|
|
||||||
const distanceTopTop = Math.abs(nodeABounds.top - nodeBBounds.top);
|
|
||||||
const distanceBottomTop = Math.abs(nodeABounds.bottom - nodeBBounds.top);
|
|
||||||
const distanceBottomBottom = Math.abs(nodeABounds.bottom - nodeBBounds.bottom);
|
|
||||||
const distanceTopBottom = Math.abs(nodeABounds.top - nodeBBounds.bottom);
|
|
||||||
const distanceCenterXCenterX = Math.abs(nodeABounds.centerX - nodeBBounds.centerX);
|
|
||||||
const distanceCenterYCenterY = Math.abs(nodeABounds.centerY - nodeBBounds.centerY);
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A |
|
|
||||||
// |___________|
|
|
||||||
// |
|
|
||||||
// |
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | B |
|
|
||||||
// |___________|
|
|
||||||
if (distanceLeftLeft < verticalDistance) {
|
|
||||||
result.snapPosition.x = nodeBBounds.left;
|
|
||||||
result.vertical.position = nodeBBounds.left;
|
|
||||||
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
verticalDistance = distanceLeftLeft;
|
|
||||||
} else if (distanceLeftLeft === verticalDistance) {
|
|
||||||
result.vertical.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A |
|
|
||||||
// |___________|
|
|
||||||
// |
|
|
||||||
// |
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | B |
|
|
||||||
// |___________|
|
|
||||||
if (distanceRightRight < verticalDistance) {
|
|
||||||
result.snapPosition.x = nodeBBounds.right - nodeABounds.width;
|
|
||||||
result.vertical.position = nodeBBounds.right;
|
|
||||||
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
verticalDistance = distanceRightRight;
|
|
||||||
} else if (distanceRightRight === verticalDistance) {
|
|
||||||
result.vertical.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A |
|
|
||||||
// |___________|
|
|
||||||
// |
|
|
||||||
// |
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | B |
|
|
||||||
// |___________|
|
|
||||||
if (distanceLeftRight < verticalDistance) {
|
|
||||||
result.snapPosition.x = nodeBBounds.right;
|
|
||||||
result.vertical.position = nodeBBounds.right;
|
|
||||||
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
verticalDistance = distanceLeftRight;
|
|
||||||
} else if (distanceLeftRight === verticalDistance) {
|
|
||||||
result.vertical.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A |
|
|
||||||
// |___________|
|
|
||||||
// |
|
|
||||||
// |
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | B |
|
|
||||||
// |___________|
|
|
||||||
if (distanceRightLeft < verticalDistance) {
|
|
||||||
result.snapPosition.x = nodeBBounds.left - nodeABounds.width;
|
|
||||||
result.vertical.position = nodeBBounds.left;
|
|
||||||
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
verticalDistance = distanceRightLeft;
|
|
||||||
} else if (distanceRightLeft === verticalDistance) {
|
|
||||||
result.vertical.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A | | B |
|
|
||||||
// |___________| |___________|
|
|
||||||
if (distanceTopTop < horizontalDistance) {
|
|
||||||
result.snapPosition.y = nodeBBounds.top;
|
|
||||||
result.horizontal.position = nodeBBounds.top;
|
|
||||||
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
horizontalDistance = distanceTopTop;
|
|
||||||
} else if (distanceTopTop === horizontalDistance) {
|
|
||||||
result.horizontal.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A |
|
|
||||||
// |___________|_________________
|
|
||||||
// | |
|
|
||||||
// | B |
|
|
||||||
// |___________|
|
|
||||||
if (distanceBottomTop < horizontalDistance) {
|
|
||||||
result.snapPosition.y = nodeBBounds.top - nodeABounds.height;
|
|
||||||
result.horizontal.position = nodeBBounds.top;
|
|
||||||
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
horizontalDistance = distanceBottomTop;
|
|
||||||
} else if (distanceBottomTop === horizontalDistance) {
|
|
||||||
result.horizontal.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A | | B |
|
|
||||||
// |___________|_____|___________|
|
|
||||||
if (distanceBottomBottom < horizontalDistance) {
|
|
||||||
result.snapPosition.y = nodeBBounds.bottom - nodeABounds.height;
|
|
||||||
result.horizontal.position = nodeBBounds.bottom;
|
|
||||||
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
horizontalDistance = distanceBottomBottom;
|
|
||||||
} else if (distanceBottomBottom === horizontalDistance) {
|
|
||||||
result.horizontal.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | B |
|
|
||||||
// | |
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
|
||||||
// | A |
|
|
||||||
// |___________|
|
|
||||||
if (distanceTopBottom < horizontalDistance) {
|
|
||||||
result.snapPosition.y = nodeBBounds.bottom;
|
|
||||||
result.horizontal.position = nodeBBounds.bottom;
|
|
||||||
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
horizontalDistance = distanceTopBottom;
|
|
||||||
} else if (distanceTopBottom === horizontalDistance) {
|
|
||||||
result.horizontal.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A |
|
|
||||||
// |___________|
|
|
||||||
// |
|
|
||||||
// |
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | B |
|
|
||||||
// |___________|
|
|
||||||
if (distanceCenterXCenterX < verticalDistance) {
|
|
||||||
result.snapPosition.x = nodeBBounds.centerX - nodeABounds.width / 2;
|
|
||||||
result.vertical.position = nodeBBounds.centerX;
|
|
||||||
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
verticalDistance = distanceCenterXCenterX;
|
|
||||||
} else if (distanceCenterXCenterX === verticalDistance) {
|
|
||||||
result.vertical.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// |‾‾‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾‾‾‾‾‾‾|
|
|
||||||
// | A |----| B |
|
|
||||||
// |___________| |___________|
|
|
||||||
if (distanceCenterYCenterY < horizontalDistance) {
|
|
||||||
result.snapPosition.y = nodeBBounds.centerY - nodeABounds.height / 2;
|
|
||||||
result.horizontal.position = nodeBBounds.centerY;
|
|
||||||
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
|
||||||
horizontalDistance = distanceCenterYCenterY;
|
|
||||||
} else if (distanceCenterYCenterY === horizontalDistance) {
|
|
||||||
result.horizontal.nodes.push(nodeBBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
{ snapPosition: { x: undefined, y: undefined } } as GetHelperLinesResult
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
computedNewNodeName,
|
|
||||||
getHelperLines
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Connection,
|
Connection,
|
||||||
NodeChange,
|
NodeChange,
|
||||||
@@ -7,7 +7,9 @@ import {
|
|||||||
EdgeChange,
|
EdgeChange,
|
||||||
Edge,
|
Edge,
|
||||||
applyNodeChanges,
|
applyNodeChanges,
|
||||||
Node
|
Node,
|
||||||
|
NodePositionChange,
|
||||||
|
XYPosition
|
||||||
} from 'reactflow';
|
} from 'reactflow';
|
||||||
import { EDGE_TYPE } from '@fastgpt/global/core/workflow/node/constant';
|
import { EDGE_TYPE } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import 'reactflow/dist/style.css';
|
import 'reactflow/dist/style.css';
|
||||||
@@ -17,7 +19,242 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
|||||||
import { useKeyboard } from './useKeyboard';
|
import { useKeyboard } from './useKeyboard';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '../../context';
|
import { WorkflowContext } from '../../context';
|
||||||
import { useWorkflowUtils } from './useUtils';
|
import { THelperLine } from '@fastgpt/global/core/workflow/type';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compute helper lines for snapping nodes to each other
|
||||||
|
Refer: https://reactflow.dev/examples/interaction/helper-lines
|
||||||
|
*/
|
||||||
|
type GetHelperLinesResult = {
|
||||||
|
horizontal?: THelperLine;
|
||||||
|
vertical?: THelperLine;
|
||||||
|
snapPosition: Partial<XYPosition>;
|
||||||
|
};
|
||||||
|
const computeHelperLines = (
|
||||||
|
change: NodePositionChange,
|
||||||
|
nodes: Node[],
|
||||||
|
distance = 8 // distance to snap
|
||||||
|
): GetHelperLinesResult => {
|
||||||
|
const nodeA = nodes.find((node) => node.id === change.id);
|
||||||
|
|
||||||
|
if (!nodeA || !change.position) {
|
||||||
|
return {
|
||||||
|
horizontal: undefined,
|
||||||
|
vertical: undefined,
|
||||||
|
snapPosition: { x: undefined, y: undefined }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeABounds = {
|
||||||
|
left: change.position.x,
|
||||||
|
right: change.position.x + (nodeA.width ?? 0),
|
||||||
|
top: change.position.y,
|
||||||
|
bottom: change.position.y + (nodeA.height ?? 0),
|
||||||
|
width: nodeA.width ?? 0,
|
||||||
|
height: nodeA.height ?? 0,
|
||||||
|
centerX: change.position.x + (nodeA.width ?? 0) / 2,
|
||||||
|
centerY: change.position.y + (nodeA.height ?? 0) / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
let horizontalDistance = distance;
|
||||||
|
let verticalDistance = distance;
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
.filter((node) => node.id !== nodeA.id)
|
||||||
|
.reduce<GetHelperLinesResult>(
|
||||||
|
(result, nodeB) => {
|
||||||
|
if (!result.vertical) {
|
||||||
|
result.vertical = {
|
||||||
|
position: nodeABounds.centerX,
|
||||||
|
nodes: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.horizontal) {
|
||||||
|
result.horizontal = {
|
||||||
|
position: nodeABounds.centerY,
|
||||||
|
nodes: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeBBounds = {
|
||||||
|
left: nodeB.position.x,
|
||||||
|
right: nodeB.position.x + (nodeB.width ?? 0),
|
||||||
|
top: nodeB.position.y,
|
||||||
|
bottom: nodeB.position.y + (nodeB.height ?? 0),
|
||||||
|
width: nodeB.width ?? 0,
|
||||||
|
height: nodeB.height ?? 0,
|
||||||
|
centerX: nodeB.position.x + (nodeB.width ?? 0) / 2,
|
||||||
|
centerY: nodeB.position.y + (nodeB.height ?? 0) / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const distanceLeftLeft = Math.abs(nodeABounds.left - nodeBBounds.left);
|
||||||
|
const distanceRightRight = Math.abs(nodeABounds.right - nodeBBounds.right);
|
||||||
|
const distanceLeftRight = Math.abs(nodeABounds.left - nodeBBounds.right);
|
||||||
|
const distanceRightLeft = Math.abs(nodeABounds.right - nodeBBounds.left);
|
||||||
|
const distanceTopTop = Math.abs(nodeABounds.top - nodeBBounds.top);
|
||||||
|
const distanceBottomTop = Math.abs(nodeABounds.bottom - nodeBBounds.top);
|
||||||
|
const distanceBottomBottom = Math.abs(nodeABounds.bottom - nodeBBounds.bottom);
|
||||||
|
const distanceTopBottom = Math.abs(nodeABounds.top - nodeBBounds.bottom);
|
||||||
|
const distanceCenterXCenterX = Math.abs(nodeABounds.centerX - nodeBBounds.centerX);
|
||||||
|
const distanceCenterYCenterY = Math.abs(nodeABounds.centerY - nodeBBounds.centerY);
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceLeftLeft < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.left;
|
||||||
|
result.vertical.position = nodeBBounds.left;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceLeftLeft;
|
||||||
|
} else if (distanceLeftLeft === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceRightRight < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.right - nodeABounds.width;
|
||||||
|
result.vertical.position = nodeBBounds.right;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceRightRight;
|
||||||
|
} else if (distanceRightRight === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceLeftRight < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.right;
|
||||||
|
result.vertical.position = nodeBBounds.right;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceLeftRight;
|
||||||
|
} else if (distanceLeftRight === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceRightLeft < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.left - nodeABounds.width;
|
||||||
|
result.vertical.position = nodeBBounds.left;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceRightLeft;
|
||||||
|
} else if (distanceRightLeft === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A | | B |
|
||||||
|
// |___________| |___________|
|
||||||
|
if (distanceTopTop < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.top;
|
||||||
|
result.horizontal.position = nodeBBounds.top;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceTopTop;
|
||||||
|
} else if (distanceTopTop === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|_________________
|
||||||
|
// | |
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceBottomTop < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.top - nodeABounds.height;
|
||||||
|
result.horizontal.position = nodeBBounds.top;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceBottomTop;
|
||||||
|
} else if (distanceBottomTop === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A | | B |
|
||||||
|
// |___________|_____|___________|
|
||||||
|
if (distanceBottomBottom < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.bottom - nodeABounds.height;
|
||||||
|
result.horizontal.position = nodeBBounds.bottom;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceBottomBottom;
|
||||||
|
} else if (distanceBottomBottom === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// | |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
if (distanceTopBottom < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.bottom;
|
||||||
|
result.horizontal.position = nodeBBounds.bottom;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceTopBottom;
|
||||||
|
} else if (distanceTopBottom === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceCenterXCenterX < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.centerX - nodeABounds.width / 2;
|
||||||
|
result.vertical.position = nodeBBounds.centerX;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceCenterXCenterX;
|
||||||
|
} else if (distanceCenterXCenterX === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |----| B |
|
||||||
|
// |___________| |___________|
|
||||||
|
if (distanceCenterYCenterY < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.centerY - nodeABounds.height / 2;
|
||||||
|
result.horizontal.position = nodeBBounds.centerY;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceCenterYCenterY;
|
||||||
|
} else if (distanceCenterYCenterY === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
{ snapPosition: { x: undefined, y: undefined } } as GetHelperLinesResult
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const useWorkflow = () => {
|
export const useWorkflow = () => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@@ -35,38 +272,56 @@ export const useWorkflow = () => {
|
|||||||
onNodesChange,
|
onNodesChange,
|
||||||
setEdges,
|
setEdges,
|
||||||
onEdgesChange,
|
onEdgesChange,
|
||||||
setHoverEdgeId,
|
setHoverEdgeId
|
||||||
setHelperLineHorizontal,
|
|
||||||
setHelperLineVertical
|
|
||||||
} = useContextSelector(WorkflowContext, (v) => v);
|
} = useContextSelector(WorkflowContext, (v) => v);
|
||||||
|
|
||||||
const { getHelperLines } = useWorkflowUtils();
|
/* helper line */
|
||||||
|
const [helperLineHorizontal, setHelperLineHorizontal] = useState<THelperLine>();
|
||||||
|
const [helperLineVertical, setHelperLineVertical] = useState<THelperLine>();
|
||||||
|
|
||||||
const customApplyNodeChanges = useCallback((changes: NodeChange[], nodes: Node[]): Node[] => {
|
const customApplyNodeChanges = (changes: NodeChange[], nodes: Node[]): Node[] => {
|
||||||
setHelperLineHorizontal(undefined);
|
const positionChange =
|
||||||
setHelperLineVertical(undefined);
|
changes[0].type === 'position' && changes[0].dragging ? changes[0] : undefined;
|
||||||
|
|
||||||
if (
|
if (changes.length === 1 && positionChange?.position) {
|
||||||
changes.length === 1 &&
|
// 只判断,3000px 内的 nodes,并按从近到远的顺序排序
|
||||||
changes[0].type === 'position' &&
|
const filterNodes = nodes
|
||||||
changes[0].dragging &&
|
.filter((node) => {
|
||||||
changes[0].position
|
if (!positionChange.position) return false;
|
||||||
) {
|
|
||||||
const helperLines = getHelperLines(changes[0], nodes);
|
|
||||||
|
|
||||||
changes[0].position.x = helperLines.snapPosition.x ?? changes[0].position.x;
|
return (
|
||||||
changes[0].position.y = helperLines.snapPosition.y ?? changes[0].position.y;
|
Math.abs(node.position.x - positionChange.position.x) <= 3000 &&
|
||||||
|
Math.abs(node.position.y - positionChange.position.y) <= 3000
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (!positionChange.position) return 0;
|
||||||
|
return (
|
||||||
|
Math.abs(a.position.x - positionChange.position.x) +
|
||||||
|
Math.abs(a.position.y - positionChange.position.y) -
|
||||||
|
Math.abs(b.position.x - positionChange.position.x) -
|
||||||
|
Math.abs(b.position.y - positionChange.position.y)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.slice(0, 15);
|
||||||
|
|
||||||
|
const helperLines = computeHelperLines(positionChange, filterNodes);
|
||||||
|
|
||||||
|
positionChange.position.x = helperLines.snapPosition.x ?? positionChange.position.x;
|
||||||
|
positionChange.position.y = helperLines.snapPosition.y ?? positionChange.position.y;
|
||||||
|
|
||||||
setHelperLineHorizontal(helperLines.horizontal);
|
setHelperLineHorizontal(helperLines.horizontal);
|
||||||
setHelperLineVertical(helperLines.vertical);
|
setHelperLineVertical(helperLines.vertical);
|
||||||
|
} else {
|
||||||
|
setHelperLineHorizontal(undefined);
|
||||||
|
setHelperLineVertical(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
return applyNodeChanges(changes, nodes);
|
return applyNodeChanges(changes, nodes);
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
/* node */
|
/* node */
|
||||||
const handleNodesChange = useCallback(
|
const handleNodesChange = (changes: NodeChange[]) => {
|
||||||
(changes: NodeChange[]) => {
|
|
||||||
setNodes((nodes) => customApplyNodeChanges(changes, nodes));
|
setNodes((nodes) => customApplyNodeChanges(changes, nodes));
|
||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
@@ -91,9 +346,7 @@ export const useWorkflow = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onNodesChange(changes);
|
onNodesChange(changes);
|
||||||
},
|
};
|
||||||
[isDowningCtrl, nodes, onNodesChange, onOpenConfirmDeleteNode, setEdges, t, toast]
|
|
||||||
);
|
|
||||||
const handleEdgeChange = useCallback(
|
const handleEdgeChange = useCallback(
|
||||||
(changes: EdgeChange[]) => {
|
(changes: EdgeChange[]) => {
|
||||||
onEdgesChange(changes.filter((change) => change.type !== 'remove'));
|
onEdgesChange(changes.filter((change) => change.type !== 'remove'));
|
||||||
@@ -163,7 +416,11 @@ export const useWorkflow = () => {
|
|||||||
onConnect,
|
onConnect,
|
||||||
customOnConnect,
|
customOnConnect,
|
||||||
onEdgeMouseEnter,
|
onEdgeMouseEnter,
|
||||||
onEdgeMouseLeave
|
onEdgeMouseLeave,
|
||||||
|
helperLineHorizontal,
|
||||||
|
setHelperLineHorizontal,
|
||||||
|
helperLineVertical,
|
||||||
|
setHelperLineVertical
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -64,8 +64,7 @@ const edgeTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Workflow = () => {
|
const Workflow = () => {
|
||||||
const { nodes, edges, reactFlowWrapper, helperLineHorizontal, helperLineVertical } =
|
const { nodes, edges, reactFlowWrapper } = useContextSelector(WorkflowContext, (v) => v);
|
||||||
useContextSelector(WorkflowContext, (v) => v);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ConfirmDeleteModal,
|
ConfirmDeleteModal,
|
||||||
@@ -75,7 +74,9 @@ const Workflow = () => {
|
|||||||
onConnectEnd,
|
onConnectEnd,
|
||||||
customOnConnect,
|
customOnConnect,
|
||||||
onEdgeMouseEnter,
|
onEdgeMouseEnter,
|
||||||
onEdgeMouseLeave
|
onEdgeMouseLeave,
|
||||||
|
helperLineHorizontal,
|
||||||
|
helperLineVertical
|
||||||
} = useWorkflow();
|
} = useWorkflow();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -85,7 +86,7 @@ const Workflow = () => {
|
|||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlowProvider>
|
<>
|
||||||
<Box
|
<Box
|
||||||
flex={'1 0 0'}
|
flex={'1 0 0'}
|
||||||
h={0}
|
h={0}
|
||||||
@@ -143,11 +144,19 @@ const Workflow = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<ConfirmDeleteModal />
|
<ConfirmDeleteModal />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Render = () => {
|
||||||
|
return (
|
||||||
|
<ReactFlowProvider>
|
||||||
|
<Workflow />
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(Workflow);
|
export default React.memo(Render);
|
||||||
|
|
||||||
const FlowController = React.memo(function FlowController() {
|
const FlowController = React.memo(function FlowController() {
|
||||||
const { fitView } = useReactFlow();
|
const { fitView } = useReactFlow();
|
||||||
|
@@ -48,7 +48,6 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|||||||
import { formatTime2HM, formatTime2YMDHMW } from '@fastgpt/global/common/string/time';
|
import { formatTime2HM, formatTime2YMDHMW } from '@fastgpt/global/common/string/time';
|
||||||
import type { InitProps } from '@/pages/app/detail/components/PublishHistoriesSlider';
|
import type { InitProps } from '@/pages/app/detail/components/PublishHistoriesSlider';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { THelperLine } from '@fastgpt/global/core/workflow/type';
|
|
||||||
|
|
||||||
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
||||||
|
|
||||||
@@ -136,12 +135,6 @@ type WorkflowContextType = {
|
|||||||
historiesDefaultData?: InitProps;
|
historiesDefaultData?: InitProps;
|
||||||
setHistoriesDefaultData: React.Dispatch<React.SetStateAction<undefined | InitProps>>;
|
setHistoriesDefaultData: React.Dispatch<React.SetStateAction<undefined | InitProps>>;
|
||||||
|
|
||||||
// helper line
|
|
||||||
helperLineHorizontal?: THelperLine;
|
|
||||||
setHelperLineHorizontal: React.Dispatch<React.SetStateAction<THelperLine | undefined>>;
|
|
||||||
helperLineVertical?: THelperLine;
|
|
||||||
setHelperLineVertical: React.Dispatch<React.SetStateAction<THelperLine | undefined>>;
|
|
||||||
|
|
||||||
// chat test
|
// chat test
|
||||||
setWorkflowTestData: React.Dispatch<
|
setWorkflowTestData: React.Dispatch<
|
||||||
React.SetStateAction<
|
React.SetStateAction<
|
||||||
@@ -267,14 +260,6 @@ export const WorkflowContext = createContext<WorkflowContextType>({
|
|||||||
setHistoriesDefaultData: function (value: React.SetStateAction<InitProps | undefined>): void {
|
setHistoriesDefaultData: function (value: React.SetStateAction<InitProps | undefined>): void {
|
||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
},
|
},
|
||||||
helperLineHorizontal: undefined,
|
|
||||||
setHelperLineHorizontal: function (value: React.SetStateAction<THelperLine | undefined>): void {
|
|
||||||
throw new Error('Function not implemented.');
|
|
||||||
},
|
|
||||||
helperLineVertical: undefined,
|
|
||||||
setHelperLineVertical: function (value: React.SetStateAction<THelperLine | undefined>): void {
|
|
||||||
throw new Error('Function not implemented.');
|
|
||||||
},
|
|
||||||
getNodeDynamicInputs: function (nodeId: string): FlowNodeInputItemType[] {
|
getNodeDynamicInputs: function (nodeId: string): FlowNodeInputItemType[] {
|
||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
}
|
}
|
||||||
@@ -742,11 +727,6 @@ const WorkflowContextProvider = ({
|
|||||||
/* Version histories */
|
/* Version histories */
|
||||||
const [historiesDefaultData, setHistoriesDefaultData] = useState<InitProps>();
|
const [historiesDefaultData, setHistoriesDefaultData] = useState<InitProps>();
|
||||||
|
|
||||||
/* helper line */
|
|
||||||
const [helperLineHorizontal, setHelperLineHorizontal] = useState<THelperLine | undefined>(
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
const [helperLineVertical, setHelperLineVertical] = useState<THelperLine | undefined>(undefined);
|
|
||||||
/* event bus */
|
/* event bus */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eventBus.on(EventNameEnum.requestWorkflowStore, () => {
|
eventBus.on(EventNameEnum.requestWorkflowStore, () => {
|
||||||
@@ -816,12 +796,6 @@ const WorkflowContextProvider = ({
|
|||||||
historiesDefaultData,
|
historiesDefaultData,
|
||||||
setHistoriesDefaultData,
|
setHistoriesDefaultData,
|
||||||
|
|
||||||
// helper line
|
|
||||||
helperLineHorizontal,
|
|
||||||
setHelperLineHorizontal,
|
|
||||||
helperLineVertical,
|
|
||||||
setHelperLineVertical,
|
|
||||||
|
|
||||||
// chat test
|
// chat test
|
||||||
setWorkflowTestData
|
setWorkflowTestData
|
||||||
};
|
};
|
||||||
|
@@ -36,6 +36,7 @@ const ChatHeader = ({
|
|||||||
apps?: AppListItemType[];
|
apps?: AppListItemType[];
|
||||||
onRouteToAppDetail?: () => void;
|
onRouteToAppDetail?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const isPlugin = chatData.app.type === AppTypeEnum.plugin;
|
const isPlugin = chatData.app.type === AppTypeEnum.plugin;
|
||||||
const { isPc } = useSystem();
|
const { isPc } = useSystem();
|
||||||
|
|
||||||
@@ -50,7 +51,11 @@ const ChatHeader = ({
|
|||||||
>
|
>
|
||||||
{isPc ? (
|
{isPc ? (
|
||||||
<>
|
<>
|
||||||
<PcHeader title={chatData.title} chatModels={chatData.app.chatModels} history={history} />
|
<PcHeader
|
||||||
|
title={chatData.title || t('chat:new_chat')}
|
||||||
|
chatModels={chatData.app.chatModels}
|
||||||
|
history={history}
|
||||||
|
/>
|
||||||
<Box flex={1} />
|
<Box flex={1} />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
@@ -81,7 +81,7 @@ export async function authChatCrud({
|
|||||||
if (permission.isOwner) return { uid: outLinkUid };
|
if (permission.isOwner) return { uid: outLinkUid };
|
||||||
if (String(tmbId) === String(chat.tmbId)) return { uid: outLinkUid };
|
if (String(tmbId) === String(chat.tmbId)) return { uid: outLinkUid };
|
||||||
|
|
||||||
// admin
|
// Admin can manage all chat
|
||||||
if (per === WritePermissionVal && permission.hasManagePer) return { uid: outLinkUid };
|
if (per === WritePermissionVal && permission.hasManagePer) return { uid: outLinkUid };
|
||||||
|
|
||||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||||
|
Reference in New Issue
Block a user