4.8.5 perf (#1854)

* update yml

* perf: chat slider

* perf: workflow name

* i18n

* fix: ts

* fix: ts
This commit is contained in:
Archer
2024-06-26 12:51:36 +08:00
committed by GitHub
parent 93b44fc8f5
commit e247545afa
15 changed files with 136 additions and 33 deletions

View File

@@ -53,7 +53,8 @@ curl --location --request POST 'https://{{host}}/api/admin/init/485' \
7. 优化 - 问答拆分/手动录入,当有`a`字段时,自动将`q`作为补充索引。 7. 优化 - 问答拆分/手动录入,当有`a`字段时,自动将`q`作为补充索引。
8. 优化 - 对话框页面代码 8. 优化 - 对话框页面代码
9. 修复 - SSR渲染 9. 修复 - SSR渲染
10. 修复 - 定时任务无法实际关闭 10. 优化 - 工作流新节点自动增加序号名
11. 修复 - 输入引导特殊字符导致正则报错 11. 修复 - 定时任务无法实际关闭
12. 修复 - 文件包含特殊字符`%`,且为转义时会导致页面崩溃 12. 修复 - 输入引导特殊字符导致正则报错
13. 修复 - 自定义输入选择知识库引用时页面崩溃 13. 修复 - 文件包含特殊字符`%`,且为转义时会导致页面崩溃
14. 修复 - 自定义输入选择知识库引用时页面崩溃

View File

@@ -179,6 +179,7 @@ services:
oneapi: oneapi:
container_name: oneapi container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest image: ghcr.io/songquanpeng/one-api:latest
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/one-api:v0.6.6 # 阿里云
ports: ports:
- 3001:3000 - 3001:3000
depends_on: depends_on:

View File

@@ -136,6 +136,7 @@ services:
oneapi: oneapi:
container_name: oneapi container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest image: ghcr.io/songquanpeng/one-api:latest
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/one-api:v0.6.6 # 阿里云
ports: ports:
- 3001:3000 - 3001:3000
depends_on: depends_on:

View File

@@ -117,6 +117,7 @@ services:
oneapi: oneapi:
container_name: oneapi container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest image: ghcr.io/songquanpeng/one-api:latest
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/one-api:v0.6.6 # 阿里云
ports: ports:
- 3001:3000 - 3001:3000
depends_on: depends_on:

View File

@@ -596,8 +596,7 @@
"success": "开始同步" "success": "开始同步"
} }
}, },
"training": { "training": {}
}
}, },
"data": { "data": {
"Auxiliary Data": "辅助数据", "Auxiliary Data": "辅助数据",
@@ -1461,7 +1460,7 @@
"Upgrade plan": "升级套餐", "Upgrade plan": "升级套餐",
"function": { "function": {
"History store": "{{amount}} 天对话记录保留", "History store": "{{amount}} 天对话记录保留",
"Max app": "{{amount}} 个应用", "Max app": "{{amount}} 个应用&插件",
"Max dataset": "{{amount}} 个知识库", "Max dataset": "{{amount}} 个知识库",
"Max dataset size": "{{amount}} 组知识库索引", "Max dataset size": "{{amount}} 组知识库索引",
"Max members": "{{amount}} 个团队成员", "Max members": "{{amount}} 个团队成员",

View File

@@ -136,7 +136,7 @@ const SelectOneResource = ({
</Flex> </Flex>
)} )}
<Avatar ml={index !== 0 ? '0.5rem' : 0} src={item.avatar} w={'1.25rem'} /> <Avatar ml={index !== 0 ? '0.5rem' : 0} src={item.avatar} w={'1.25rem'} />
<Box fontSize={['md', 'sm']} ml={2}> <Box fontSize={['md', 'sm']} ml={2} className="textEllipsis">
{item.name} {item.name}
</Box> </Box>
</Flex> </Flex>

View File

@@ -87,7 +87,7 @@ const AppCard = () => {
> >
{appDetail.intro || t('core.app.tip.Add a intro to app')} {appDetail.intro || t('core.app.tip.Add a intro to app')}
</Box> </Box>
<HStack> <HStack alignItems={'flex-end'}>
<Button <Button
size={['sm', 'md']} size={['sm', 'md']}
variant={'whitePrimary'} variant={'whitePrimary'}
@@ -155,7 +155,7 @@ const AppCard = () => {
colorSchema="gray" colorSchema="gray"
onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)} onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)}
> >
<PermissionIconText defaultPermission={appDetail.defaultPermission} fontSize={'md'} /> <PermissionIconText defaultPermission={appDetail.defaultPermission} />
</MyTag> </MyTag>
</HStack> </HStack>
</Box> </Box>

View File

@@ -29,6 +29,7 @@ import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import FolderPath from '@/components/common/folder/Path'; import FolderPath from '@/components/common/folder/Path';
import { getAppFolderPath } from '@/web/core/app/api/app'; import { getAppFolderPath } from '@/web/core/app/api/app';
import { useWorkflowUtils } from './hooks/useUtils';
type ModuleTemplateListProps = { type ModuleTemplateListProps = {
isOpen: boolean; isOpen: boolean;
@@ -59,6 +60,13 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
WorkflowContext, WorkflowContext,
(v) => v (v) => v
); );
const [pluginBuffer, setPluginBuffer] = useState<{
systemPlugin: FlowNodeTemplateType[];
teamPlugin: FlowNodeTemplateType[];
}>({
[TemplateTypeEnum.systemPlugin]: [],
[TemplateTypeEnum.teamPlugin]: []
});
const [templateType, setTemplateType] = useState(TemplateTypeEnum.basic); const [templateType, setTemplateType] = useState(TemplateTypeEnum.basic);
@@ -85,14 +93,34 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
}); });
} }
if (templateType === TemplateTypeEnum.systemPlugin) { if (templateType === TemplateTypeEnum.systemPlugin) {
return getSystemPlugTemplates(); if (pluginBuffer.systemPlugin.length === 0) {
return getSystemPlugTemplates().then((res) => {
setPluginBuffer((state) => ({
...state,
systemPlugin: res
}));
return res;
});
} else {
return pluginBuffer.systemPlugin;
}
} }
if (templateType === TemplateTypeEnum.teamPlugin) { if (templateType === TemplateTypeEnum.teamPlugin) {
return getTeamPlugTemplates({ if (pluginBuffer.teamPlugin.length === 0) {
parentId, return getTeamPlugTemplates({
searchKey, parentId,
type: [AppTypeEnum.folder, AppTypeEnum.httpPlugin, AppTypeEnum.plugin] searchKey,
}); type: [AppTypeEnum.folder, AppTypeEnum.httpPlugin, AppTypeEnum.plugin]
}).then((res) => {
setPluginBuffer((state) => ({
...state,
teamPlugin: res
}));
return res;
});
} else {
return pluginBuffer.teamPlugin;
}
} }
return []; return [];
}, },
@@ -240,6 +268,7 @@ const RenderList = React.memo(function RenderList({
const { toast } = useToast(); const { toast } = useToast();
const reactFlowWrapper = useContextSelector(WorkflowContext, (v) => v.reactFlowWrapper); const reactFlowWrapper = useContextSelector(WorkflowContext, (v) => v.reactFlowWrapper);
const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes); const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes);
const { computedNewNodeName } = useWorkflowUtils();
const formatTemplates = useMemo<nodeTemplateListType>(() => { const formatTemplates = useMemo<nodeTemplateListType>(() => {
const copy: nodeTemplateListType = JSON.parse(JSON.stringify(workflowNodeTemplateList(t))); const copy: nodeTemplateListType = JSON.parse(JSON.stringify(workflowNodeTemplateList(t)));
@@ -266,6 +295,8 @@ const RenderList = React.memo(function RenderList({
setLoading(false); setLoading(false);
return res; return res;
} }
// base node
return { ...template }; return { ...template };
} catch (e) { } catch (e) {
toast({ toast({
@@ -284,7 +315,11 @@ const RenderList = React.memo(function RenderList({
const node = nodeTemplate2FlowNode({ const node = nodeTemplate2FlowNode({
template: { template: {
...templateNode, ...templateNode,
name: t(templateNode.name), name: computedNewNodeName({
templateName: t(templateNode.name),
flowNodeType: templateNode.flowNodeType,
pluginId: templateNode.pluginId
}),
intro: t(templateNode.intro || '') intro: t(templateNode.intro || '')
}, },
position: { x: mouseX, y: mouseY - 20 }, position: { x: mouseX, y: mouseY - 20 },
@@ -301,7 +336,7 @@ const RenderList = React.memo(function RenderList({
.concat(node) .concat(node)
); );
}, },
[reactFlowWrapper, setLoading, setNodes, t, toast, x, y, zoom] [computedNewNodeName, reactFlowWrapper, setLoading, setNodes, t, toast, x, y, zoom]
); );
const Render = useMemo(() => { const Render = useMemo(() => {

View File

@@ -6,11 +6,13 @@ import { Node } from 'reactflow';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext, getWorkflowStore } from '../../context'; import { WorkflowContext, getWorkflowStore } from '../../context';
import { useWorkflowUtils } from './useUtils';
export const useKeyboard = () => { export const useKeyboard = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { setNodes, onSaveWorkflow } = useContextSelector(WorkflowContext, (v) => v); const { setNodes, onSaveWorkflow } = useContextSelector(WorkflowContext, (v) => v);
const { copyData } = useCopyData(); const { copyData } = useCopyData();
const { computedNewNodeName } = useWorkflowUtils();
const [isDowningCtrl, setIsDowningCtrl] = useState(false); const [isDowningCtrl, setIsDowningCtrl] = useState(false);
@@ -56,6 +58,11 @@ export const useKeyboard = () => {
id: nodeId, id: nodeId,
data: { data: {
...item.data, ...item.data,
name: computedNewNodeName({
templateName: item.data?.name || '',
flowNodeType: item.data?.flowNodeType || '',
pluginId: item.data?.pluginId
}),
nodeId nodeId
}, },
position: { position: {
@@ -75,7 +82,7 @@ export const useKeyboard = () => {
.concat(newNodes) .concat(newNodes)
); );
} catch (error) {} } catch (error) {}
}, [hasInputtingElement, setNodes]); }, [computedNewNodeName, hasInputtingElement, setNodes]);
const handleKeyDown = useCallback( const handleKeyDown = useCallback(
(event: KeyboardEvent) => { (event: KeyboardEvent) => {

View File

@@ -0,0 +1,42 @@
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../context';
import { useTranslation } from 'next-i18next';
import { useCallback } from 'react';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
export const useWorkflowUtils = () => {
const { t } = useTranslation();
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const computedNewNodeName = useCallback(
({
templateName,
flowNodeType,
pluginId
}: {
templateName: string;
flowNodeType: FlowNodeTypeEnum;
pluginId?: string;
}) => {
const nodeLength = nodeList.filter((node) => {
if (node.flowNodeType === flowNodeType) {
if (node.flowNodeType === FlowNodeTypeEnum.pluginModule) {
return node.pluginId === pluginId;
} else {
return true;
}
}
}).length;
return nodeLength > 0 ? `${templateName}#${nodeLength + 1}` : templateName;
},
[nodeList]
);
return {
computedNewNodeName
};
};
export default function Dom() {
return <></>;
}

View File

@@ -27,6 +27,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useWorkflowUtils } from '../../hooks/useUtils';
type Props = FlowNodeItemType & { type Props = FlowNodeItemType & {
children?: React.ReactNode | React.ReactNode[] | string; children?: React.ReactNode | React.ReactNode[] | string;
@@ -292,6 +293,7 @@ const MenuRender = React.memo(function MenuRender({
const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes); const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes);
const setEdges = useContextSelector(WorkflowContext, (v) => v.setEdges); const setEdges = useContextSelector(WorkflowContext, (v) => v.setEdges);
const { computedNewNodeName } = useWorkflowUtils();
const onCopyNode = useCallback( const onCopyNode = useCallback(
(nodeId: string) => { (nodeId: string) => {
@@ -300,7 +302,11 @@ const MenuRender = React.memo(function MenuRender({
if (!node) return state; if (!node) return state;
const template = { const template = {
avatar: node.data.avatar, avatar: node.data.avatar,
name: node.data.name, name: computedNewNodeName({
templateName: node.data.name,
flowNodeType: node.data.flowNodeType,
pluginId: node.data.pluginId
}),
intro: node.data.intro, intro: node.data.intro,
flowNodeType: node.data.flowNodeType, flowNodeType: node.data.flowNodeType,
inputs: node.data.inputs, inputs: node.data.inputs,
@@ -323,12 +329,13 @@ const MenuRender = React.memo(function MenuRender({
inputs: template.inputs, inputs: template.inputs,
outputs: template.outputs, outputs: template.outputs,
version: template.version version: template.version
} },
selected: true
}) })
); );
}); });
}, },
[setNodes] [computedNewNodeName, setNodes]
); );
const onDelNode = useCallback( const onDelNode = useCallback(
(nodeId: string) => { (nodeId: string) => {

View File

@@ -36,6 +36,8 @@ const ConfigPerModal = dynamic(() => import('@/components/support/permission/Con
import type { EditHttpPluginProps } from './HttpPluginEditModal'; import type { EditHttpPluginProps } from './HttpPluginEditModal';
import { postCopyApp } from '@/web/core/app/api/app'; import { postCopyApp } from '@/web/core/app/api/app';
import { getTeamMembers } from '@/web/support/user/team/api'; import { getTeamMembers } from '@/web/support/user/team/api';
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
const HttpEditModal = dynamic(() => import('./HttpPluginEditModal')); const HttpEditModal = dynamic(() => import('./HttpPluginEditModal'));
@@ -43,6 +45,8 @@ const ListItem = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appT } = useI18n(); const { appT } = useI18n();
const router = useRouter(); const router = useRouter();
const { isPc } = useSystem();
const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail, appType } = const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail, appType } =
useContextSelector(AppListContext, (v) => v); useContextSelector(AppListContext, (v) => v);
const [loadingAppId, setLoadingAppId] = useState<string>(); const [loadingAppId, setLoadingAppId] = useState<string>();
@@ -175,9 +179,6 @@ const ListItem = () => {
isFolder: app.type === AppTypeEnum.folder isFolder: app.type === AppTypeEnum.folder
})} })}
> >
{/* <Box position={'absolute'} top={3.5} right={0}>
<AppTypeTag type={app.type} />
</Box> */}
<HStack> <HStack>
<Avatar src={app.avatar} borderRadius={'md'} w={'1.5rem'} /> <Avatar src={app.avatar} borderRadius={'md'} w={'1.5rem'} />
<Box flex={'1 0 0'} fontSize={'1.125rem'}> <Box flex={'1 0 0'} fontSize={'1.125rem'}>
@@ -188,7 +189,7 @@ const ListItem = () => {
</Box> </Box>
</HStack> </HStack>
<Box <Box
flex={'1 0 80px'} flex={['1 0 60px', '1 0 80px']}
mt={3} mt={3}
pr={8} pr={8}
textAlign={'justify'} textAlign={'justify'}
@@ -219,10 +220,12 @@ const ListItem = () => {
</HStack> </HStack>
<HStack> <HStack>
{/* <HStack spacing={0.5} className="time"> {isPc && (
<MyIcon name={'history'} w={'0.85rem'} /> <HStack spacing={0.5} className="time">
<Box>{formatTimeToChatTime(app.updateTime)}</Box> <MyIcon name={'history'} w={'0.85rem'} />
</HStack> */} <Box>{formatTimeToChatTime(app.updateTime)}</Box>
</HStack>
)}
{app.permission.hasManagePer && ( {app.permission.hasManagePer && (
<Box className="more" display={['', 'none']}> <Box className="more" display={['', 'none']}>
<MyMenu <MyMenu

View File

@@ -144,7 +144,7 @@ const MyApps = () => {
gap={5} gap={5}
display={'flex'} display={'flex'}
alignItems={'center'} alignItems={'center'}
fontSize={'md'} fontSize={['sm', 'md']}
onChange={(e) => { onChange={(e) => {
router.push({ router.push({
query: { query: {

View File

@@ -159,7 +159,10 @@ const ChatHistorySlider = ({
{!isPc && appId && ( {!isPc && appId && (
<LightRowTabs<TabEnum> <LightRowTabs<TabEnum>
flex={'1 0 0'} flex={'1 0 0'}
mr={2} mr={1}
inlineStyles={{
px: 1
}}
list={[ list={[
{ label: t('core.chat.Recent use'), value: TabEnum.recently }, { label: t('core.chat.Recent use'), value: TabEnum.recently },
{ label: t('App'), value: TabEnum.app }, { label: t('App'), value: TabEnum.app },

View File

@@ -58,9 +58,11 @@ export const nodeTemplate2FlowNode = ({
}; };
}; };
export const storeNode2FlowNode = ({ export const storeNode2FlowNode = ({
item: storeNode item: storeNode,
selected = false
}: { }: {
item: StoreNodeItemType; item: StoreNodeItemType;
selected?: boolean;
}): Node<FlowNodeItemType> => { }): Node<FlowNodeItemType> => {
// init some static data // init some static data
const template = const template =
@@ -101,6 +103,7 @@ export const storeNode2FlowNode = ({
id: storeNode.nodeId, id: storeNode.nodeId,
type: storeNode.flowNodeType, type: storeNode.flowNodeType,
data: moduleItem, data: moduleItem,
selected,
position: storeNode.position || { x: 0, y: 0 } position: storeNode.position || { x: 0, y: 0 }
}; };
}; };