mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-20 10:45:52 +00:00
perf: workflow code (#2492)
* perf: workflow code * perf: converpoints ui * perf: account page ui
This commit is contained in:
@@ -28,17 +28,20 @@ STORE_LOG_LEVEL=warn
|
|||||||
1. 新增 - 模板市场
|
1. 新增 - 模板市场
|
||||||
2. 新增 - 工作流节点拖动自动对齐吸附
|
2. 新增 - 工作流节点拖动自动对齐吸附
|
||||||
3. 新增 - 用户选择节点(Debug 模式暂未支持)
|
3. 新增 - 用户选择节点(Debug 模式暂未支持)
|
||||||
4. 商业版新增 - 飞书机器人接入
|
4. 新增 - 工作流撤销和重做
|
||||||
5. 商业版新增 - 公众号接入接入
|
5. 新增 - 工作流本次编辑记录,取代自动保存
|
||||||
6. 商业版新增 - 自助开票申请
|
6. 新增 - 工作流版本支持重命名
|
||||||
7. 商业版新增 - SSO 定制
|
7. 商业版新增 - 飞书机器人接入
|
||||||
8. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
|
8. 商业版新增 - 公众号接入接入
|
||||||
9. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
|
9. 商业版新增 - 自助开票申请
|
||||||
10. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
|
10. 商业版新增 - SSO 定制
|
||||||
11. 优化 - 知识库列表 UI。
|
11. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
|
||||||
12. 优化 - 支持无网络配置情况下运行。
|
12. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
|
||||||
13. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
|
13. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
|
||||||
14. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
|
14. 优化 - 知识库列表 UI。
|
||||||
15. 修复 - 选择 Milvus 部署时,无法导出知识库。
|
15. 优化 - 支持无网络配置情况下运行。
|
||||||
16. 修复 - 创建 APP 副本,无法复制系统配置。
|
16. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
|
||||||
17. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
|
17. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
|
||||||
|
18. 修复 - 选择 Milvus 部署时,无法导出知识库。
|
||||||
|
19. 修复 - 创建 APP 副本,无法复制系统配置。
|
||||||
|
20. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { ModalBody, Box, Button, VStack, HStack, Link } from '@chakra-ui/react';
|
import { ModalBody, Box, Button, VStack, HStack, Link } from '@chakra-ui/react';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
@@ -6,22 +6,33 @@ import Icon from '@fastgpt/web/components/common/Icon';
|
|||||||
import Tag from '@fastgpt/web/components/common/Tag';
|
import Tag from '@fastgpt/web/components/common/Tag';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { balanceConversion } from '@/web/support/wallet/bill/api';
|
import { balanceConversion } from '@/web/support/wallet/bill/api';
|
||||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
|
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
||||||
|
import { SUB_EXTRA_POINT_RATE } from '@fastgpt/global/support/wallet/bill/constants';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
const ConversionModal = ({
|
const ConversionModal = ({
|
||||||
onClose,
|
onClose,
|
||||||
balance,
|
|
||||||
tokens,
|
|
||||||
onOpenContact
|
onOpenContact
|
||||||
}: {
|
}: {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
balance: string;
|
|
||||||
tokens: string;
|
|
||||||
onOpenContact: () => void;
|
onOpenContact: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { userInfo } = useUserStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const points = useMemo(() => {
|
||||||
|
if (!userInfo?.team?.balance) return 0;
|
||||||
|
const balance = formatStorePrice2Read(userInfo?.team?.balance);
|
||||||
|
|
||||||
|
return Math.ceil((balance / 15) * SUB_EXTRA_POINT_RATE);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const { runAsync: onConvert, loading } = useRequest2(balanceConversion, {
|
const { runAsync: onConvert, loading } = useRequest2(balanceConversion, {
|
||||||
|
onSuccess() {
|
||||||
|
router.reload();
|
||||||
|
},
|
||||||
successToast: t('user:bill.convert_success'),
|
successToast: t('user:bill.convert_success'),
|
||||||
errorToast: t('user:bill.convert_error')
|
errorToast: t('user:bill.convert_error')
|
||||||
});
|
});
|
||||||
@@ -35,7 +46,6 @@ const ConversionModal = ({
|
|||||||
title={t('user:bill.use_balance')}
|
title={t('user:bill.use_balance')}
|
||||||
>
|
>
|
||||||
<ModalBody maxW={'450px'}>
|
<ModalBody maxW={'450px'}>
|
||||||
{loading && <Loading />}
|
|
||||||
<VStack px="2.25" gap={2} pb="6">
|
<VStack px="2.25" gap={2} pb="6">
|
||||||
<HStack px="4" py="2" color="primary.600" bgColor="primary.50" borderRadius="md">
|
<HStack px="4" py="2" color="primary.600" bgColor="primary.50" borderRadius="md">
|
||||||
<Icon name="common/info" w="1rem" mr="1" />
|
<Icon name="common/info" w="1rem" mr="1" />
|
||||||
@@ -43,10 +53,10 @@ const ConversionModal = ({
|
|||||||
</HStack>
|
</HStack>
|
||||||
<VStack mt={6}>
|
<VStack mt={6}>
|
||||||
<Box fontSize={'sm'} color="myGray.600" fontWeight="500">
|
<Box fontSize={'sm'} color="myGray.600" fontWeight="500">
|
||||||
{t('user:bill.price')}
|
当前积分价格
|
||||||
</Box>
|
</Box>
|
||||||
<Box fontSize={'xl'} fontWeight={'700'} color="myGray.900">
|
<Box fontSize={'xl'} fontWeight={'700'} color="myGray.900">
|
||||||
¥15/1000 {t('user:bill.tokens')}
|
¥15/1000 {t('user:bill.tokens')}/月
|
||||||
</Box>
|
</Box>
|
||||||
</VStack>
|
</VStack>
|
||||||
<VStack mt={6}>
|
<VStack mt={6}>
|
||||||
@@ -54,7 +64,7 @@ const ConversionModal = ({
|
|||||||
{t('user:bill.balance')}
|
{t('user:bill.balance')}
|
||||||
</Box>
|
</Box>
|
||||||
<Box fontSize={'xl'} fontWeight={'700'} color="myGray.900">
|
<Box fontSize={'xl'} fontWeight={'700'} color="myGray.900">
|
||||||
¥{balance}
|
¥{formatStorePrice2Read(userInfo?.team?.balance)?.toFixed(2)}
|
||||||
</Box>
|
</Box>
|
||||||
</VStack>
|
</VStack>
|
||||||
<VStack mt={6}>
|
<VStack mt={6}>
|
||||||
@@ -62,9 +72,9 @@ const ConversionModal = ({
|
|||||||
{t('user:bill.you_can_convert')}
|
{t('user:bill.you_can_convert')}
|
||||||
</Box>
|
</Box>
|
||||||
<Box fontSize={'xl'} fontWeight={'700'} color="myGray.900">
|
<Box fontSize={'xl'} fontWeight={'700'} color="myGray.900">
|
||||||
{tokens} {t('user:bill.tokens')}
|
{points} {t('user:bill.tokens')}
|
||||||
</Box>
|
</Box>
|
||||||
<Tag>{t('user:bill.token_expire_1year')}</Tag>
|
<Tag fontSize={'lg'}>{t('user:bill.token_expire_1year')}</Tag>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
<VStack mt="6">
|
<VStack mt="6">
|
||||||
@@ -74,6 +84,7 @@ const ConversionModal = ({
|
|||||||
fontSize={'sm'}
|
fontSize={'sm'}
|
||||||
minW={'10rem'}
|
minW={'10rem'}
|
||||||
onClick={onConvert}
|
onClick={onConvert}
|
||||||
|
isLoading={loading}
|
||||||
>
|
>
|
||||||
{t('user:bill.conversion')}
|
{t('user:bill.conversion')}
|
||||||
</Button>
|
</Button>
|
@@ -305,22 +305,18 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
|||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
<strong>{formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}</strong> 元
|
<strong>{formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}</strong> 元
|
||||||
</Box>
|
</Box>
|
||||||
{userInfo?.permission.hasManagePer && !!standardPlan && (
|
{/* TODO:暂时隐藏 */}
|
||||||
|
{/* {userInfo?.permission.hasManagePer && !!standardPlan && (
|
||||||
<Button variant={'primary'} size={'sm'} ml={5} onClick={onOpenConversionModal}>
|
<Button variant={'primary'} size={'sm'} ml={5} onClick={onOpenConversionModal}>
|
||||||
{t('user:bill.conversion')}
|
{t('user:bill.conversion')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)} */}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
{isOpenConversionModal && (
|
{isOpenConversionModal && (
|
||||||
<ConversionModal
|
<ConversionModal onClose={onCloseConversionModal} onOpenContact={onOpenContact} />
|
||||||
onClose={onCloseConversionModal}
|
|
||||||
balance={formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}
|
|
||||||
tokens={String((userInfo?.team?.balance ?? 0) / 15 / 10)}
|
|
||||||
onOpenContact={onOpenContact}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />}
|
{isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />}
|
||||||
{isOpenUpdateNotification && <UpdateNotification onClose={onCloseUpdateNotification} />}
|
{isOpenUpdateNotification && <UpdateNotification onClose={onCloseUpdateNotification} />}
|
@@ -21,6 +21,7 @@ import { subTypeMap, standardSubLevelMap } from '@fastgpt/global/support/wallet/
|
|||||||
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
|
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
|
||||||
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
|
|
||||||
const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
|
const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { Loading } = useLoading();
|
const { Loading } = useLoading();
|
||||||
@@ -33,6 +34,7 @@ const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
maxW={['90vw', '1200px']}
|
maxW={['90vw', '1200px']}
|
||||||
iconSrc="modal/teamPlans"
|
iconSrc="modal/teamPlans"
|
||||||
title={t('common:support.wallet.Standard Plan Detail')}
|
title={t('common:support.wallet.Standard Plan Detail')}
|
||||||
|
isCentered
|
||||||
>
|
>
|
||||||
<ModalCloseButton onClick={onClose} />
|
<ModalCloseButton onClick={onClose} />
|
||||||
<ModalBody>
|
<ModalBody>
|
@@ -8,7 +8,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
|||||||
import PageContainer from '@/components/PageContainer';
|
import PageContainer from '@/components/PageContainer';
|
||||||
import SideTabs from '@/components/SideTabs';
|
import SideTabs from '@/components/SideTabs';
|
||||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
import UserInfo from './components/Info';
|
import UserInfo from './components/Info/index';
|
||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import Script from 'next/script';
|
import Script from 'next/script';
|
||||||
|
@@ -38,7 +38,8 @@ const WorkflowEdit = () => {
|
|||||||
cloneDeep({
|
cloneDeep({
|
||||||
nodes: appDetail.modules || [],
|
nodes: appDetail.modules || [],
|
||||||
edges: appDetail.edges || []
|
edges: appDetail.edges || []
|
||||||
})
|
}),
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -61,6 +61,9 @@ const Header = () => {
|
|||||||
} = useContextSelector(WorkflowContext, (v) => v);
|
} = useContextSelector(WorkflowContext, (v) => v);
|
||||||
|
|
||||||
const isPublished = useMemo(() => {
|
const isPublished = useMemo(() => {
|
||||||
|
/*
|
||||||
|
Find the last saved snapshot in the past and future snapshots
|
||||||
|
*/
|
||||||
const savedSnapshot =
|
const savedSnapshot =
|
||||||
future.findLast((snapshot) => snapshot.isSaved) || past.find((snapshot) => snapshot.isSaved);
|
future.findLast((snapshot) => snapshot.isSaved) || past.find((snapshot) => snapshot.isSaved);
|
||||||
|
|
||||||
@@ -97,6 +100,7 @@ const Header = () => {
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
version: 'v2'
|
version: 'v2'
|
||||||
});
|
});
|
||||||
|
// Mark the current snapshot as saved
|
||||||
setPast((prevPast) =>
|
setPast((prevPast) =>
|
||||||
prevPast.map((item, index) =>
|
prevPast.map((item, index) =>
|
||||||
index === prevPast.length - 1
|
index === prevPast.length - 1
|
||||||
|
@@ -424,7 +424,7 @@ const WorkflowContextProvider = ({
|
|||||||
const onChangeNode = useMemoizedFn((props: FlowNodeChangeProps) => {
|
const onChangeNode = useMemoizedFn((props: FlowNodeChangeProps) => {
|
||||||
const { nodeId, type } = props;
|
const { nodeId, type } = props;
|
||||||
setNodes((nodes) => {
|
setNodes((nodes) => {
|
||||||
const newNodes = nodes.map((node) => {
|
return nodes.map((node) => {
|
||||||
if (node.id !== nodeId) return node;
|
if (node.id !== nodeId) return node;
|
||||||
|
|
||||||
const updateObj = cloneDeep(node.data);
|
const updateObj = cloneDeep(node.data);
|
||||||
@@ -490,7 +490,6 @@ const WorkflowContextProvider = ({
|
|||||||
data: updateObj
|
data: updateObj
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return newNodes;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const getNodeDynamicInputs = useCallback(
|
const getNodeDynamicInputs = useCallback(
|
||||||
@@ -532,6 +531,13 @@ const WorkflowContextProvider = ({
|
|||||||
|
|
||||||
const initData = useMemoizedFn(
|
const initData = useMemoizedFn(
|
||||||
async (e: Parameters<WorkflowContextType['initData']>[0], isInit?: boolean) => {
|
async (e: Parameters<WorkflowContextType['initData']>[0], isInit?: boolean) => {
|
||||||
|
/*
|
||||||
|
Refresh web page, load init
|
||||||
|
*/
|
||||||
|
if (isInit && past.length > 0) {
|
||||||
|
return resetSnapshot(past[0]);
|
||||||
|
}
|
||||||
|
|
||||||
setNodes(e.nodes?.map((item) => storeNode2FlowNode({ item })) || []);
|
setNodes(e.nodes?.map((item) => storeNode2FlowNode({ item })) || []);
|
||||||
setEdges(e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || []);
|
setEdges(e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || []);
|
||||||
|
|
||||||
@@ -544,19 +550,15 @@ const WorkflowContextProvider = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If it is the initial data, save the initial snapshot
|
// If it is the initial data, save the initial snapshot
|
||||||
if (!isInit) return;
|
if (isInit) {
|
||||||
// If it has been initialized, it will not be saved
|
saveSnapshot({
|
||||||
if (past.length > 0) {
|
pastNodes: e.nodes?.map((item) => storeNode2FlowNode({ item })) || [],
|
||||||
resetSnapshot(past[0]);
|
pastEdges: e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || [],
|
||||||
return;
|
customTitle: t(`app:app.version_initial`),
|
||||||
|
chatConfig: appDetail.chatConfig,
|
||||||
|
isSaved: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
saveSnapshot({
|
|
||||||
pastNodes: e.nodes?.map((item) => storeNode2FlowNode({ item })) || [],
|
|
||||||
pastEdges: e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || [],
|
|
||||||
customTitle: t(`app:app.version_initial`),
|
|
||||||
chatConfig: appDetail.chatConfig,
|
|
||||||
isSaved: true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user