Commit 761e35c2 by Archer Committed by GitHub

4.8.10 workflow perf (#2596)

* perf: run plugin variables init

* perf: init free plan

* perf: dataset data ui

* perf: workflow theme

* perf: plugin input modal ui

* perf: workflow dispatch

* fix: account ui

* feat: 4810 doc
parent 5ebe0017
...@@ -55,30 +55,32 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \ ...@@ -55,30 +55,32 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
6. 新增 - 工作流版本支持重命名 6. 新增 - 工作流版本支持重命名
7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。 7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。
8. 新增 - 插件增加使用说明配置。 8. 新增 - 插件增加使用说明配置。
9. 商业版新增 - 飞书机器人接入 9. 新增 - 工作流导出导入,支持直接导出和导入 JSON 文件,便于交流。
10. 商业版新增 - 公众号接入接入 10. 商业版新增 - 飞书机器人接入
11. 商业版新增 - 自助开票申请 11. 商业版新增 - 公众号接入接入
12. 商业版新增 - SSO 定制 12. 商业版新增 - 自助开票申请
13. 优化 - SSE 响应优化。 13. 商业版新增 - SSO 定制
14. 优化 - 无 SSL 证书情况下,优化复制。 14. 优化 - 工作流循环校验,避免 skip 循环空转。同时支持分支完全并发执行。
15. 优化 - 单选框打开后自动滚动到选中的位置。 15. 优化 - SSE 响应优化。
16. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。 16. 优化 - 无 SSL 证书情况下,优化复制。
17. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。 17. 优化 - 单选框打开后自动滚动到选中的位置。
18. 优化 - 最新 React Markdown 组件,支持 Base64 图片。 18. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
19. 优化 - 知识库列表 UI。 19. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
20. 优化 - 知识库详情页 UI。 20. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
21. 优化 - 支持无网络配置情况下运行。 21. 优化 - 知识库列表 UI。
22. 优化 - 部分全局变量,增加数据类型约束。 22. 优化 - 知识库详情页 UI。
23. 修复 - 全局变量 key 可能重复。 23. 优化 - 支持无网络配置情况下运行。
24. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。 24. 优化 - 部分全局变量,增加数据类型约束。
25. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。 25. 修复 - 全局变量 key 可能重复。
26. 修复 - 选择 Milvus 部署时,无法导出知识库。 26. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
27. 修复 - 创建 APP 副本,无法复制系统配置。 27. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
28. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。 28. 修复 - 选择 Milvus 部署时,无法导出知识库。
29. 修复 - 内容提取的数据类型与输出数据类型未一致。 29. 修复 - 创建 APP 副本,无法复制系统配置。
30. 修复 - 工作流运行时间统计错误。 30. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
31. 修复 - stream 模式下,工具调用有可能出现 undefined 31. 修复 - 内容提取的数据类型与输出数据类型未一致。
32. 修复 - 全局变量在 API 中无法持久化。 32. 修复 - 工作流运行时间统计错误。
33. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题) 33. 修复 - stream 模式下,工具调用有可能出现 undefined
34. 修复 - 知识库标签重复加载。 34. 修复 - 全局变量在 API 中无法持久化。
35. 修复 - Debug 模式下,循环调用边问题。 35. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
36. 修复 - 知识库标签重复加载。
37. 修复 - Debug 模式下,循环调用边问题。
...@@ -26,8 +26,8 @@ export type ChatDispatchProps = { ...@@ -26,8 +26,8 @@ export type ChatDispatchProps = {
res?: NextApiResponse; res?: NextApiResponse;
requestOrigin?: string; requestOrigin?: string;
mode: 'test' | 'chat' | 'debug'; mode: 'test' | 'chat' | 'debug';
teamId: string; teamId: string; // App teamId
tmbId: string; tmbId: string; // App tmbId
user: UserModelSchema; user: UserModelSchema;
app: AppDetailType | AppSchema; app: AppDetailType | AppSchema;
chatId?: string; chatId?: string;
......
...@@ -63,7 +63,7 @@ import { ...@@ -63,7 +63,7 @@ import {
InteractiveNodeResponseItemType, InteractiveNodeResponseItemType,
UserSelectInteractive UserSelectInteractive
} from '@fastgpt/global/core/workflow/template/system/userSelect/type'; } from '@fastgpt/global/core/workflow/template/system/userSelect/type';
import { dispatchRunAppNode } from './agent/runApp'; import { dispatchRunAppNode } from './plugin/runApp';
const callbackMap: Record<FlowNodeTypeEnum, Function> = { const callbackMap: Record<FlowNodeTypeEnum, Function> = {
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart, [FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
...@@ -186,7 +186,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -186,7 +186,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
function nodeOutput( function nodeOutput(
node: RuntimeNodeItemType, node: RuntimeNodeItemType,
result: Record<string, any> = {} result: Record<string, any> = {}
): RuntimeNodeItemType[] { ): {
nextStepActiveNodes: RuntimeNodeItemType[];
nextStepSkipNodes: RuntimeNodeItemType[];
} {
pushStore(node, result); pushStore(node, result);
// Assign the output value to the next node // Assign the output value to the next node
...@@ -211,16 +214,32 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -211,16 +214,32 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
} }
}); });
const nextStepNodes = runtimeNodes.filter((node) => { const nextStepActiveNodes: RuntimeNodeItemType[] = [];
return targetEdges.some((item) => item.target === node.nodeId); const nextStepSkipNodes: RuntimeNodeItemType[] = [];
runtimeNodes.forEach((node) => {
if (targetEdges.some((item) => item.target === node.nodeId && item.status === 'active')) {
nextStepActiveNodes.push(node);
}
if (targetEdges.some((item) => item.target === node.nodeId && item.status === 'skipped')) {
nextStepSkipNodes.push(node);
}
}); });
if (props.mode === 'debug') { if (props.mode === 'debug') {
debugNextStepRunNodes = debugNextStepRunNodes.concat(nextStepNodes); debugNextStepRunNodes = debugNextStepRunNodes.concat([
return []; ...nextStepActiveNodes,
...nextStepSkipNodes
]);
return {
nextStepActiveNodes: [],
nextStepSkipNodes: []
};
} }
return nextStepNodes; return {
nextStepActiveNodes,
nextStepSkipNodes
};
} }
/* Have interactive result, computed edges and node outputs */ /* Have interactive result, computed edges and node outputs */
...@@ -281,69 +300,82 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -281,69 +300,82 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
}); });
} }
/* Check node run/skip or wait */ /* Check node run/skip or wait */
function checkNodeCanRun(nodes: RuntimeNodeItemType[] = []): Promise<any> { async function checkNodeCanRun(
return Promise.all( node: RuntimeNodeItemType,
nodes.map(async (node) => { skippedNodeIdList = new Set<string>()
const status = checkNodeRunStatus({ ): Promise<RuntimeNodeItemType[]> {
node, if (res?.closed || props.maxRunTimes <= 0) return [];
runtimeEdges // Thread avoidance
}); await surrenderProcess();
if (res?.closed || props.maxRunTimes <= 0) return; addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id });
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id }); // Get node run status by edges
const status = checkNodeRunStatus({
node,
runtimeEdges
});
const nodeRunResult = await (() => {
if (status === 'run') {
props.maxRunTimes--;
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
return nodeRunWithActive(node);
}
if (status === 'skip' && !skippedNodeIdList.has(node.nodeId)) {
props.maxRunTimes -= 0.1;
skippedNodeIdList.add(node.nodeId);
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
return nodeRunWithSkip(node);
}
})();
// Thread avoidance if (!nodeRunResult) return [];
await surrenderProcess();
if (status === 'run') { // Update the node output at the end of the run and get the next nodes
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`); let { nextStepActiveNodes, nextStepSkipNodes } = nodeOutput(
return nodeRunWithActive(node); nodeRunResult.node,
} nodeRunResult.result
if (status === 'skip') { );
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`); // Remove repeat nodes(Make sure that the node is only executed once)
return nodeRunWithSkip(node); nextStepActiveNodes = nextStepActiveNodes.filter(
} (node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
);
nextStepSkipNodes = nextStepSkipNodes.filter(
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
);
return; // In the current version, only one interactive node is allowed at the same time
}) const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
).then((result) => { if (interactiveResponse) {
props.maxRunTimes--; chatAssistantResponse.push(
handleInteractiveResult({
const flat = result.flat().filter(Boolean) as unknown as { entryNodeIds: [nodeRunResult.node.nodeId],
node: RuntimeNodeItemType; interactiveResponse
runStatus: 'run' | 'skip'; })
result: Record<string, any>;
}[];
// If there are no running nodes, the workflow is complete
if (flat.length === 0) return;
// Update the node output at the end of the run and get the next nodes
const nextNodes = flat.map((item) => nodeOutput(item.node, item.result)).flat();
// Remove repeat nodes(Make sure that the node is only executed once)
const filterNextNodes = nextNodes.filter(
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
); );
return [];
}
// In the current version, only one interactive node is allowed at the same time // Run next nodes(先运行 run 的,再运行 skip 的)
const haveInteractiveResponse = flat const nextStepActiveNodesResults = (
.map((response) => { await Promise.all(nextStepActiveNodes.map((node) => checkNodeCanRun(node)))
const interactiveResponse = response.result?.[DispatchNodeResponseKeyEnum.interactive]; ).flat();
if (interactiveResponse) {
chatAssistantResponse.push(
handleInteractiveResult({
entryNodeIds: [response.node.nodeId],
interactiveResponse
})
);
return 1;
}
})
.filter(Boolean);
if (haveInteractiveResponse.length > 0) return;
return checkNodeCanRun(filterNextNodes); // 如果已经 active 运行过,不再执行 skip(active 中有闭环)
}); nextStepSkipNodes = nextStepSkipNodes.filter(
(node) => !nextStepActiveNodesResults.some((item) => item.nodeId === node.nodeId)
);
const nextStepSkipNodesResults = (
await Promise.all(nextStepSkipNodes.map((node) => checkNodeCanRun(node, skippedNodeIdList)))
).flat();
return [
...nextStepActiveNodes,
...nextStepSkipNodes,
...nextStepActiveNodesResults,
...nextStepSkipNodesResults
];
} }
/* Inject data into module input */ /* Inject data into module input */
function getNodeRunParams(node: RuntimeNodeItemType) { function getNodeRunParams(node: RuntimeNodeItemType) {
...@@ -396,7 +428,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -396,7 +428,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
return params; return params;
} }
async function nodeRunWithActive(node: RuntimeNodeItemType) { async function nodeRunWithActive(node: RuntimeNodeItemType): Promise<{
node: RuntimeNodeItemType;
runStatus: 'run';
result: Record<string, any>;
}> {
// push run status messages // push run status messages
if (node.showStatus) { if (node.showStatus) {
props.workflowStreamResponse?.({ props.workflowStreamResponse?.({
...@@ -465,8 +501,12 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -465,8 +501,12 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
} }
}; };
} }
async function nodeRunWithSkip(node: RuntimeNodeItemType) { async function nodeRunWithSkip(node: RuntimeNodeItemType): Promise<{
// 其后所有target的节点,都设置为skip node: RuntimeNodeItemType;
runStatus: 'skip';
result: Record<string, any>;
}> {
// Set target edges status to skipped
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId); const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
nodeRunAfterHook(node); nodeRunAfterHook(node);
...@@ -486,7 +526,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons ...@@ -486,7 +526,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
// runtimeNodes.forEach((item) => { // runtimeNodes.forEach((item) => {
// item.isEntry = false; // item.isEntry = false;
// }); // });
await checkNodeCanRun(entryNodes); await Promise.all(entryNodes.map((node) => checkNodeCanRun(node)));
// focus try to run pluginOutput // focus try to run pluginOutput
const pluginOutputModule = runtimeNodes.find( const pluginOutputModule = runtimeNodes.find(
......
...@@ -64,7 +64,11 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi ...@@ -64,7 +64,11 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({ const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
...props, ...props,
variables: filterSystemVariables(props.variables),
variables: {
...filterSystemVariables(props.variables),
appId: String(plugin.id)
},
runtimeNodes, runtimeNodes,
runtimeEdges: initWorkflowEdgeStatus(plugin.edges) runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
}); });
......
...@@ -50,7 +50,7 @@ export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => { ...@@ -50,7 +50,7 @@ export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
}; };
}; };
export const initTeamStandardPlan2Free = async ({ export const initTeamFreePlan = async ({
teamId, teamId,
session session
}: { }: {
...@@ -59,23 +59,28 @@ export const initTeamStandardPlan2Free = async ({ ...@@ -59,23 +59,28 @@ export const initTeamStandardPlan2Free = async ({
}) => { }) => {
const freePoints = global?.subPlans?.standard?.[StandardSubLevelEnum.free]?.totalPoints || 100; const freePoints = global?.subPlans?.standard?.[StandardSubLevelEnum.free]?.totalPoints || 100;
const teamStandardSub = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard }); const freePlan = await MongoTeamSub.findOne({
teamId,
if (teamStandardSub) { type: SubTypeEnum.standard,
teamStandardSub.currentMode = SubModeEnum.month; currentSubLevel: StandardSubLevelEnum.free
teamStandardSub.nextMode = SubModeEnum.month; });
teamStandardSub.startTime = new Date();
teamStandardSub.expiredTime = addMonths(new Date(), 1); // Reset one month free plan
if (freePlan) {
teamStandardSub.currentSubLevel = StandardSubLevelEnum.free; freePlan.currentMode = SubModeEnum.month;
teamStandardSub.nextSubLevel = StandardSubLevelEnum.free; freePlan.nextMode = SubModeEnum.month;
freePlan.startTime = new Date();
teamStandardSub.totalPoints = freePoints; freePlan.expiredTime = addMonths(new Date(), 1);
teamStandardSub.surplusPoints =
teamStandardSub.surplusPoints && teamStandardSub.surplusPoints < 0 freePlan.currentSubLevel = StandardSubLevelEnum.free;
? teamStandardSub.surplusPoints + freePoints freePlan.nextSubLevel = StandardSubLevelEnum.free;
freePlan.totalPoints = freePoints;
freePlan.surplusPoints =
freePlan.surplusPoints && freePlan.surplusPoints < 0
? freePlan.surplusPoints + freePoints
: freePoints; : freePoints;
return teamStandardSub.save({ session }); return freePlan.save({ session });
} }
return MongoTeamSub.create( return MongoTeamSub.create(
...@@ -123,13 +128,14 @@ export const getTeamPlanStatus = async ({ ...@@ -123,13 +128,14 @@ export const getTeamPlanStatus = async ({
// Free user, first login after expiration. The free subscription plan will be reset // Free user, first login after expiration. The free subscription plan will be reset
if ( if (
standardPlan && (standardPlan &&
standardPlan.expiredTime && standardPlan.expiredTime &&
standardPlan.currentSubLevel === StandardSubLevelEnum.free && standardPlan.currentSubLevel === StandardSubLevelEnum.free &&
dayjs(standardPlan.expiredTime).isBefore(new Date()) dayjs(standardPlan.expiredTime).isBefore(new Date())) ||
teamStandardPlans.length === 0
) { ) {
console.log('Init free stand plan', { teamId }); console.log('Init free stand plan', { teamId });
await initTeamStandardPlan2Free({ teamId }); await initTeamFreePlan({ teamId });
return getTeamPlanStatus({ teamId }); return getTeamPlanStatus({ teamId });
} }
......
...@@ -33,7 +33,7 @@ export type Props = { ...@@ -33,7 +33,7 @@ export type Props = {
icon?: IconNameType | string; icon?: IconNameType | string;
label: string | React.ReactNode; label: string | React.ReactNode;
description?: string; description?: string;
onClick: () => any; onClick?: () => any;
}[]; }[];
}[]; }[];
}; };
...@@ -170,8 +170,10 @@ const MyMenu = ({ ...@@ -170,8 +170,10 @@ const MyMenu = ({
{...menuItemStyles} {...menuItemStyles}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsOpen(false); if (child.onClick) {
child.onClick && child.onClick(); setIsOpen(false);
child.onClick();
}
}} }}
color={child.isActive ? 'primary.700' : 'myGray.600'} color={child.isActive ? 'primary.700' : 'myGray.600'}
whiteSpace={'pre-wrap'} whiteSpace={'pre-wrap'}
......
...@@ -105,7 +105,7 @@ const MultipleRowSelect = ({ ...@@ -105,7 +105,7 @@ const MultipleRowSelect = ({
justifyContent={'space-between'} justifyContent={'space-between'}
width={'100%'} width={'100%'}
rightIcon={<ChevronDownIcon />} rightIcon={<ChevronDownIcon />}
variant={'whiteFlow'} variant={'whiteBase'}
_active={{ _active={{
transform: 'none' transform: 'none'
}} }}
......
...@@ -194,28 +194,6 @@ const Button = defineStyleConfig({ ...@@ -194,28 +194,6 @@ const Button = defineStyleConfig({
color: 'myGray.600 !important' color: 'myGray.600 !important'
} }
}, },
whiteFlow: {
color: 'myGray.600',
border: '1px solid',
borderColor: 'myGray.200',
height: '40px',
bg: 'white',
px: '12px',
py: '0',
borderRadius: '6px',
transition: 'background 0.1s',
_hover: {
color: 'primary.600',
background: 'primary.1',
borderColor: 'primary.300'
},
_active: {
color: 'primary.600'
},
_disabled: {
color: 'myGray.600 !important'
}
},
whiteDanger: { whiteDanger: {
color: 'myGray.600', color: 'myGray.600',
border: '1px solid', border: '1px solid',
......
...@@ -68,7 +68,7 @@ const SettingLLMModel = ({ ...@@ -68,7 +68,7 @@ const SettingLLMModel = ({
<Button <Button
w={'100%'} w={'100%'}
justifyContent={'flex-start'} justifyContent={'flex-start'}
variant={'whiteFlow'} variant={'whiteBase'}
bg={bg} bg={bg}
_active={{ _active={{
transform: 'none' transform: 'none'
......
...@@ -67,9 +67,9 @@ const Account = () => { ...@@ -67,9 +67,9 @@ const Account = () => {
return ( return (
<> <>
<Box py={[3, '28px']} maxW={['95vw', '1080px']} px={[5, 10]} mx={'auto'}> <Box py={[3, '28px']} px={[5, 10]} mx={'auto'}>
{isPc ? ( {isPc ? (
<Flex justifyContent={'center'}> <Flex justifyContent={'center'} maxW={'1080px'}>
<Box flex={'0 0 330px'}> <Box flex={'0 0 330px'}>
<MyInfo onOpenContact={onOpenContact} /> <MyInfo onOpenContact={onOpenContact} />
<Box mt={9}> <Box mt={9}>
...@@ -77,7 +77,7 @@ const Account = () => { ...@@ -77,7 +77,7 @@ const Account = () => {
</Box> </Box>
</Box> </Box>
{!!standardPlan && ( {!!standardPlan && (
<Box ml={'45px'} flex={'1 0 0'} maxW={'600px'}> <Box ml={'45px'} flex={'1'} maxW={'600px'}>
<PlanUsage /> <PlanUsage />
</Box> </Box>
)} )}
...@@ -437,7 +437,7 @@ const PlanUsage = () => { ...@@ -437,7 +437,7 @@ const PlanUsage = () => {
borderColor={'borderColor.low'} borderColor={'borderColor.low'}
borderRadius={'md'} borderRadius={'md'}
> >
<Flex px={[5, 7]} py={[3, 6]} whiteSpace={'nowrap'}> <Flex px={[5, 7]} pt={[3, 6]}>
<Box flex={'1 0 0'}> <Box flex={'1 0 0'}>
<Box color={'myGray.600'} fontSize="sm"> <Box color={'myGray.600'} fontSize="sm">
{t('common:support.wallet.subscription.Current plan')} {t('common:support.wallet.subscription.Current plan')}
...@@ -445,24 +445,26 @@ const PlanUsage = () => { ...@@ -445,24 +445,26 @@ const PlanUsage = () => {
<Box fontWeight={'bold'} fontSize="lg"> <Box fontWeight={'bold'} fontSize="lg">
{t(planName as any)} {t(planName as any)}
</Box> </Box>
{isFreeTeam ? (
<>
<Box mt="2" color={'#485264'} fontSize="sm">
{t('common:info.free_plan')}
</Box>
</>
) : (
<Flex mt="2" color={'#485264'} fontSize="xs">
<Box>{t('common:support.wallet.Plan expired time')}:</Box>
<Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box>
</Flex>
)}
</Box> </Box>
<Button onClick={() => router.push('/price')} w={'8rem'} size="sm"> <Button onClick={() => router.push('/price')} w={'8rem'} size="sm">
{t('common:support.wallet.subscription.Upgrade plan')} {t('common:support.wallet.subscription.Upgrade plan')}
</Button> </Button>
</Flex> </Flex>
<Box px={[5, 7]} pb={[3, 6]}>
{isFreeTeam ? (
<>
<Box mt="2" color={'#485264'} fontSize="sm">
{t('common:info.free_plan')}
</Box>
</>
) : (
<Flex mt="2" color={'#485264'} fontSize="xs">
<Box>{t('common:support.wallet.Plan expired time')}:</Box>
<Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box>
</Flex>
)}
</Box>
<Box py={3} borderTopWidth={'1px'} borderTopColor={'borderColor.base'}> <Box py={3} borderTopWidth={'1px'} borderTopColor={'borderColor.base'}>
<Box py={[0, 3]} px={[5, 7]} overflow={'auto'}> <Box py={[0, 3]} px={[5, 7]} overflow={'auto'}>
<StandardPlanContentList <StandardPlanContentList
...@@ -479,7 +481,7 @@ const PlanUsage = () => { ...@@ -479,7 +481,7 @@ const PlanUsage = () => {
borderColor={'borderColor.low'} borderColor={'borderColor.low'}
borderRadius={'md'} borderRadius={'md'}
px={[5, 10]} px={[5, 10]}
pt={[2, 4]} pt={4}
pb={[4, 7]} pb={[4, 7]}
> >
<Flex> <Flex>
......
...@@ -180,12 +180,11 @@ const UsageTable = () => { ...@@ -180,12 +180,11 @@ const UsageTable = () => {
))} ))}
</Tbody> </Tbody>
</Table> </Table>
{!isLoading && usages.length === 0 && (
<EmptyTip text={t('common:user.no_usage_records')}></EmptyTip>
)}
</TableContainer> </TableContainer>
{!isLoading && usages.length === 0 && (
<EmptyTip text={t('common:user.no_usage_records')}></EmptyTip>
)}
<Loading loading={isLoading} fixed={false} /> <Loading loading={isLoading} fixed={false} />
{!!usageDetail && ( {!!usageDetail && (
<UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} /> <UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} />
......
...@@ -125,7 +125,7 @@ const Header = () => { ...@@ -125,7 +125,7 @@ const Header = () => {
try { try {
localStorage.removeItem(`${appDetail._id}-past`); localStorage.removeItem(`${appDetail._id}-past`);
localStorage.removeItem(`${appDetail._id}-future`); localStorage.removeItem(`${appDetail._id}-future`);
router.push('/app/list'); router.back();
} catch (error) {} } catch (error) {}
}, [appDetail._id, router]); }, [appDetail._id, router]);
......
...@@ -15,7 +15,7 @@ const RouteTab = () => { ...@@ -15,7 +15,7 @@ const RouteTab = () => {
const setCurrentTab = useCallback( const setCurrentTab = useCallback(
(tab: TabEnum) => { (tab: TabEnum) => {
router.push({ router.replace({
query: { query: {
...router.query, ...router.query,
currentTab: tab currentTab: tab
...@@ -41,7 +41,7 @@ const RouteTab = () => { ...@@ -41,7 +41,7 @@ const RouteTab = () => {
] ]
: []) : [])
], ],
[appDetail.permission.hasManagePer, appT] [appDetail.permission.hasManagePer, appDetail.type, appT]
); );
return ( return (
......
...@@ -125,7 +125,7 @@ const Header = () => { ...@@ -125,7 +125,7 @@ const Header = () => {
try { try {
localStorage.removeItem(`${appDetail._id}-past`); localStorage.removeItem(`${appDetail._id}-past`);
localStorage.removeItem(`${appDetail._id}-future`); localStorage.removeItem(`${appDetail._id}-future`);
router.push('/app/list'); router.back();
} catch (error) {} } catch (error) {}
}, [appDetail._id, router]); }, [appDetail._id, router]);
......
...@@ -74,8 +74,7 @@ const AppCard = ({ ...@@ -74,8 +74,7 @@ const AppCard = ({
label: ExportPopover({ label: ExportPopover({
chatConfig: appDetail.chatConfig, chatConfig: appDetail.chatConfig,
appName: appDetail.name appName: appDetail.name
}), })
onClick: () => {}
} }
] ]
} }
...@@ -192,7 +191,7 @@ function ExportPopover({ ...@@ -192,7 +191,7 @@ function ExportPopover({
const { t } = useTranslation(); const { t } = useTranslation();
const { copyData } = useCopyData(); const { copyData } = useCopyData();
const { flowData2StoreDataAndCheck } = useContextSelector(WorkflowContext, (v) => v); const { flowData2StoreDataAndCheck } = useContextSelector(WorkflowContext, (v) => v);
const data = flowData2StoreDataAndCheck();
const onExportWorkflow = useCallback(async () => { const onExportWorkflow = useCallback(async () => {
const data = flowData2StoreDataAndCheck(); const data = flowData2StoreDataAndCheck();
if (data) { if (data) {
...@@ -251,7 +250,10 @@ function ExportPopover({ ...@@ -251,7 +250,10 @@ function ExportPopover({
}} }}
borderRadius={'xs'} borderRadius={'xs'}
onClick={() => { onClick={() => {
const data = flowData2StoreDataAndCheck();
if (!data) return; if (!data) return;
fileDownload({ fileDownload({
text: JSON.stringify( text: JSON.stringify(
{ {
......
...@@ -312,6 +312,7 @@ const FieldEditModal = ({ ...@@ -312,6 +312,7 @@ const FieldEditModal = ({
title={isEdit ? t('workflow:edit_input') : t('workflow:add_new_input')} title={isEdit ? t('workflow:edit_input') : t('workflow:add_new_input')}
maxW={['90vw', '1028px']} maxW={['90vw', '1028px']}
w={'100%'} w={'100%'}
isCentered
> >
<Flex h={'560px'}> <Flex h={'560px'}>
<Stack gap={4} p={8}> <Stack gap={4} p={8}>
......
...@@ -50,7 +50,7 @@ const SelectAppRender = ({ item, nodeId }: RenderInputProps) => { ...@@ -50,7 +50,7 @@ const SelectAppRender = ({ item, nodeId }: RenderInputProps) => {
<> <>
<Box onClick={onOpenSelectApp}> <Box onClick={onOpenSelectApp}>
{!value ? ( {!value ? (
<Button variant={'whiteFlow'} w={'100%'}> <Button variant={'whiteBase'} w={'100%'}>
{t('common:core.module.Select app')} {t('common:core.module.Select app')}
</Button> </Button>
) : ( ) : (
...@@ -58,7 +58,7 @@ const SelectAppRender = ({ item, nodeId }: RenderInputProps) => { ...@@ -58,7 +58,7 @@ const SelectAppRender = ({ item, nodeId }: RenderInputProps) => {
isLoading={loading} isLoading={loading}
w={'100%'} w={'100%'}
justifyContent={loading ? 'center' : 'flex-start'} justifyContent={loading ? 'center' : 'flex-start'}
variant={'whiteFlow'} variant={'whiteBase'}
leftIcon={<Avatar src={appDetail?.avatar} w={6} />} leftIcon={<Avatar src={appDetail?.avatar} w={6} />}
> >
{appDetail?.name} {appDetail?.name}
......
...@@ -179,7 +179,7 @@ const DataCard = () => { ...@@ -179,7 +179,7 @@ const DataCard = () => {
<Flex align={'center'} color={'myGray.500'}> <Flex align={'center'} color={'myGray.500'}>
<MyIcon name="common/list" mr={2} w={'18px'} /> <MyIcon name="common/list" mr={2} w={'18px'} />
<Box as={'span'} fontSize={['sm', '14px']} fontWeight={'500'}> <Box as={'span'} fontSize={['sm', '14px']} fontWeight={'500'}>
{t('core.dataset.data.Total Amount', { total })} {t('common:core.dataset.data.Total Amount', { total })}
</Box> </Box>
</Flex> </Flex>
<Box flex={1} mr={1} /> <Box flex={1} mr={1} />
...@@ -204,7 +204,7 @@ const DataCard = () => { ...@@ -204,7 +204,7 @@ const DataCard = () => {
/> />
</Flex> </Flex>
{/* data */} {/* data */}
<Box flex={'1 0 0'} overflow={'auto'} px={5}> <Box flex={'1 0 0'} overflow={'auto'} px={5} pb={5}>
<Flex flexDir={'column'} gap={2}> <Flex flexDir={'column'} gap={2}>
{datasetDataList.map((item, index) => ( {datasetDataList.map((item, index) => (
<Card <Card
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment