perf: app edit
This commit is contained in:
parent
c16e2b8dd6
commit
35718b1f26
@ -1 +1 @@
|
|||||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1683436459815" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1278" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M377.04830749 512.02677874l395.88826112-382.68981248c23.33363769-22.59430969 23.33363769-59.16132466 0-81.68344804-23.33247261-22.5582171-61.18836224-22.5582171-84.52083485 0L250.30081877 471.19378659c-23.29754397 22.5582171-23.29754397 59.14385977 0 81.63105451l438.11491385 423.52280349c11.70233003 11.27968995 26.99767353 16.91837099 42.29534607 16.91837098 15.29883648 0 30.59418112-5.63984498 42.22548878-16.95446471 23.33363769-22.5582171 23.33363769-59.07283854 0-81.63105451L377.04830749 512.02677874" p-id="1279"></path></svg>
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1683436459815" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1278" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M377.04830749 512.02677874l395.88826112-382.68981248c23.33363769-22.59430969 23.33363769-59.16132466 0-81.68344804-23.33247261-22.5582171-61.18836224-22.5582171-84.52083485 0L250.30081877 471.19378659c-23.29754397 22.5582171-23.29754397 59.14385977 0 81.63105451l438.11491385 423.52280349c11.70233003 11.27968995 26.99767353 16.91837099 42.29534607 16.91837098 15.29883648 0 30.59418112-5.63984498 42.22548878-16.95446471 23.33363769-22.5582171 23.33363769-59.07283854 0-81.63105451L377.04830749 512.02677874" p-id="1279"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 866 B After Width: | Height: | Size: 844 B |
@ -371,7 +371,11 @@ export const EmptyModule: FlowModuleTemplateType = {
|
|||||||
export const ModuleTemplates = [
|
export const ModuleTemplates = [
|
||||||
{
|
{
|
||||||
label: '输入模块',
|
label: '输入模块',
|
||||||
list: [UserInputModule, HistoryModule, VariableModule, UserGuideModule]
|
list: [UserInputModule, HistoryModule]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '引导模块',
|
||||||
|
list: [UserGuideModule, VariableModule]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '内容生成',
|
label: '内容生成',
|
||||||
|
|||||||
@ -1,21 +1,53 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { ModuleTemplates } from '@/constants/flow/ModuleTemplate';
|
import { ModuleTemplates } from '@/constants/flow/ModuleTemplate';
|
||||||
import { FlowModuleTemplateType } from '@/types/flow';
|
import { FlowModuleTemplateType } from '@/types/flow';
|
||||||
import type { XYPosition } from 'reactflow';
|
import type { Node, XYPosition } from 'reactflow';
|
||||||
import { useGlobalStore } from '@/store/global';
|
import { useGlobalStore } from '@/store/global';
|
||||||
|
import type { AppModuleItemType } from '@/types/app';
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
|
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||||
|
|
||||||
const ModuleTemplateList = ({
|
const ModuleTemplateList = ({
|
||||||
|
nodes,
|
||||||
isOpen,
|
isOpen,
|
||||||
onAddNode,
|
onAddNode,
|
||||||
onClose
|
onClose
|
||||||
}: {
|
}: {
|
||||||
|
nodes?: Node<AppModuleItemType>[];
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onAddNode: (e: { template: FlowModuleTemplateType; position: XYPosition }) => void;
|
onAddNode: (e: { template: FlowModuleTemplateType; position: XYPosition }) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { isPc } = useGlobalStore();
|
const { isPc } = useGlobalStore();
|
||||||
|
|
||||||
|
const filterTemplates = useMemo(() => {
|
||||||
|
const guideModulesIndex = ModuleTemplates.findIndex((item) => item.label === '引导模块');
|
||||||
|
const guideModule: {
|
||||||
|
label: string;
|
||||||
|
list: FlowModuleTemplateType[];
|
||||||
|
} = JSON.parse(JSON.stringify(ModuleTemplates[guideModulesIndex]));
|
||||||
|
|
||||||
|
if (nodes?.find((item) => item.type === FlowModuleTypeEnum.userGuide)) {
|
||||||
|
const index = guideModule.list.findIndex(
|
||||||
|
(item) => item.flowType === FlowModuleTypeEnum.userGuide
|
||||||
|
);
|
||||||
|
guideModule.list.splice(index, 1);
|
||||||
|
}
|
||||||
|
if (nodes?.find((item) => item.type === FlowModuleTypeEnum.variable)) {
|
||||||
|
const index = guideModule.list.findIndex(
|
||||||
|
(item) => item.flowType === FlowModuleTypeEnum.variable
|
||||||
|
);
|
||||||
|
guideModule.list.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...ModuleTemplates.slice(0, guideModulesIndex),
|
||||||
|
guideModule,
|
||||||
|
...ModuleTemplates.slice(guideModulesIndex + 1)
|
||||||
|
];
|
||||||
|
}, [nodes]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
@ -49,7 +81,7 @@ const ModuleTemplateList = ({
|
|||||||
</Box>
|
</Box>
|
||||||
<Box flex={'1 0 0'} overflow={'overlay'}>
|
<Box flex={'1 0 0'} overflow={'overlay'}>
|
||||||
<Box w={['100%', '330px']} mx={'auto'}>
|
<Box w={['100%', '330px']} mx={'auto'}>
|
||||||
{ModuleTemplates.map((item) =>
|
{filterTemplates.map((item) =>
|
||||||
item.list.map((item) => (
|
item.list.map((item) => (
|
||||||
<Flex
|
<Flex
|
||||||
key={item.name}
|
key={item.name}
|
||||||
@ -68,11 +100,11 @@ const ModuleTemplateList = ({
|
|||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (isPc) return;
|
if (isPc) return;
|
||||||
|
onClose();
|
||||||
onAddNode({
|
onAddNode({
|
||||||
template: item,
|
template: item,
|
||||||
position: { x: e.clientX, y: e.clientY }
|
position: { x: e.clientX, y: e.clientY }
|
||||||
});
|
});
|
||||||
onClose();
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Avatar src={item.logo} w={'34px'} objectFit={'contain'} borderRadius={'0'} />
|
<Avatar src={item.logo} w={'34px'} objectFit={'contain'} borderRadius={'0'} />
|
||||||
|
|||||||
@ -95,12 +95,12 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
|||||||
const { x, y, zoom } = useViewport();
|
const { x, y, zoom } = useViewport();
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState<FlowModuleItemType>([]);
|
const [nodes, setNodes, onNodesChange] = useNodesState<FlowModuleItemType>([]);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
const [loaded, setLoaded] = useState(false);
|
|
||||||
const {
|
const {
|
||||||
isOpen: isOpenTemplate,
|
isOpen: isOpenTemplate,
|
||||||
onOpen: onOpenTemplate,
|
onOpen: onOpenTemplate,
|
||||||
onClose: onCloseTemplate
|
onClose: onCloseTemplate
|
||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
const [testModules, setTestModules] = useState<AppModuleItemType[]>();
|
const [testModules, setTestModules] = useState<AppModuleItemType[]>();
|
||||||
|
|
||||||
const onFixView = useCallback(() => {
|
const onFixView = useCallback(() => {
|
||||||
@ -111,6 +111,48 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const flow2AppModules = useCallback(() => {
|
||||||
|
const modules: AppModuleItemType[] = nodes.map((item) => ({
|
||||||
|
moduleId: item.data.moduleId,
|
||||||
|
position: item.position,
|
||||||
|
flowType: item.data.flowType,
|
||||||
|
inputs: item.data.inputs.map((item) => ({
|
||||||
|
key: item.key,
|
||||||
|
value: item.value,
|
||||||
|
connected: item.type !== FlowInputItemTypeEnum.target
|
||||||
|
})),
|
||||||
|
outputs: item.data.outputs.map((item) => ({
|
||||||
|
key: item.key,
|
||||||
|
targets: [] as FlowOutputTargetItemType[]
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
// update inputs and outputs
|
||||||
|
modules.forEach((module) => {
|
||||||
|
module.inputs.forEach((input) => {
|
||||||
|
input.connected =
|
||||||
|
input.connected ||
|
||||||
|
!!edges.find(
|
||||||
|
(edge) => edge.target === module.moduleId && edge.targetHandle === input.key
|
||||||
|
);
|
||||||
|
});
|
||||||
|
module.outputs.forEach((output) => {
|
||||||
|
output.targets = edges
|
||||||
|
.filter(
|
||||||
|
(edge) =>
|
||||||
|
edge.source === module.moduleId &&
|
||||||
|
edge.sourceHandle === output.key &&
|
||||||
|
edge.targetHandle
|
||||||
|
)
|
||||||
|
.map((edge) => ({
|
||||||
|
moduleId: edge.target,
|
||||||
|
key: edge.targetHandle || ''
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return modules;
|
||||||
|
}, [edges, nodes]);
|
||||||
|
|
||||||
const onChangeNode = useCallback(
|
const onChangeNode = useCallback(
|
||||||
({ moduleId, key, type = 'inputs', value, valueKey = 'value' }: FlowModuleItemChangeProps) => {
|
({ moduleId, key, type = 'inputs', value, valueKey = 'value' }: FlowModuleItemChangeProps) => {
|
||||||
setNodes((nodes) =>
|
setNodes((nodes) =>
|
||||||
@ -176,48 +218,6 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
|||||||
},
|
},
|
||||||
[onChangeNode, onDelNode, setNodes, x, y, zoom]
|
[onChangeNode, onDelNode, setNodes, x, y, zoom]
|
||||||
);
|
);
|
||||||
const flow2AppModules = useCallback(() => {
|
|
||||||
const modules: AppModuleItemType[] = nodes.map((item) => ({
|
|
||||||
moduleId: item.data.moduleId,
|
|
||||||
position: item.position,
|
|
||||||
flowType: item.data.flowType,
|
|
||||||
inputs: item.data.inputs.map((item) => ({
|
|
||||||
key: item.key,
|
|
||||||
value: item.value,
|
|
||||||
connected: item.type !== FlowInputItemTypeEnum.target
|
|
||||||
})),
|
|
||||||
outputs: item.data.outputs.map((item) => ({
|
|
||||||
key: item.key,
|
|
||||||
targets: [] as FlowOutputTargetItemType[]
|
|
||||||
}))
|
|
||||||
}));
|
|
||||||
|
|
||||||
// update inputs and outputs
|
|
||||||
modules.forEach((module) => {
|
|
||||||
module.inputs.forEach((input) => {
|
|
||||||
input.connected =
|
|
||||||
input.connected ||
|
|
||||||
!!edges.find(
|
|
||||||
(edge) => edge.target === module.moduleId && edge.targetHandle === input.key
|
|
||||||
);
|
|
||||||
});
|
|
||||||
module.outputs.forEach((output) => {
|
|
||||||
output.targets = edges
|
|
||||||
.filter(
|
|
||||||
(edge) =>
|
|
||||||
edge.source === module.moduleId &&
|
|
||||||
edge.sourceHandle === output.key &&
|
|
||||||
edge.targetHandle
|
|
||||||
)
|
|
||||||
.map((edge) => ({
|
|
||||||
moduleId: edge.target,
|
|
||||||
key: edge.targetHandle || ''
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return modules;
|
|
||||||
}, [edges, nodes]);
|
|
||||||
|
|
||||||
const onDelConnect = useCallback(
|
const onDelConnect = useCallback(
|
||||||
(id: string) => {
|
(id: string) => {
|
||||||
setEdges((state) => state.filter((item) => item.id !== id));
|
setEdges((state) => state.filter((item) => item.id !== id));
|
||||||
@ -274,7 +274,6 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
setLoaded(true);
|
|
||||||
onFixView();
|
onFixView();
|
||||||
},
|
},
|
||||||
[onDelConnect, setEdges, setNodes, onFixView, onChangeNode, onDelNode]
|
[onDelConnect, setEdges, setNodes, onFixView, onChangeNode, onDelNode]
|
||||||
@ -296,10 +295,10 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
|||||||
>
|
>
|
||||||
{fullScreen ? (
|
{fullScreen ? (
|
||||||
<>
|
<>
|
||||||
<MyTooltip label={'取消全屏'} offset={[10, 10]}>
|
<MyTooltip label={'返回'} offset={[10, 10]}>
|
||||||
<IconButton
|
<IconButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
icon={<MyIcon name={'fullScreenLight'} w={['14px', '16px']} />}
|
icon={<MyIcon name={'back'} w={'14px'} />}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
borderColor={'myGray.300'}
|
borderColor={'myGray.300'}
|
||||||
variant={'base'}
|
variant={'base'}
|
||||||
@ -425,7 +424,12 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
|||||||
<Controls position={'bottom-right'} style={{ display: 'flex' }} showInteractive={false} />
|
<Controls position={'bottom-right'} style={{ display: 'flex' }} showInteractive={false} />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
|
|
||||||
<TemplateList isOpen={isOpenTemplate} onAddNode={onAddNode} onClose={onCloseTemplate} />
|
<TemplateList
|
||||||
|
isOpen={isOpenTemplate}
|
||||||
|
nodes={nodes}
|
||||||
|
onAddNode={onAddNode}
|
||||||
|
onClose={onCloseTemplate}
|
||||||
|
/>
|
||||||
<ChatTest
|
<ChatTest
|
||||||
ref={ChatTestRef}
|
ref={ChatTestRef}
|
||||||
modules={testModules}
|
modules={testModules}
|
||||||
|
|||||||
@ -40,6 +40,10 @@ import { useRequest } from '@/hooks/useRequest';
|
|||||||
import { useConfirm } from '@/hooks/useConfirm';
|
import { useConfirm } from '@/hooks/useConfirm';
|
||||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||||
import { streamFetch } from '@/api/fetch';
|
import { streamFetch } from '@/api/fetch';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { useToast } from '@/hooks/useToast';
|
||||||
|
import { AppSchema } from '@/types/mongoSchema';
|
||||||
|
import { delModelById } from '@/api/app';
|
||||||
|
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import MySelect from '@/components/Select';
|
import MySelect from '@/components/Select';
|
||||||
@ -56,15 +60,17 @@ import { addVariable } from '../VariableEditModal';
|
|||||||
import { KBSelectModal, KbParamsModal } from '../KBSelectModal';
|
import { KBSelectModal, KbParamsModal } from '../KBSelectModal';
|
||||||
|
|
||||||
const VariableEditModal = dynamic(() => import('../VariableEditModal'));
|
const VariableEditModal = dynamic(() => import('../VariableEditModal'));
|
||||||
|
const InfoModal = dynamic(() => import('../InfoModal'));
|
||||||
|
|
||||||
const Settings = ({ appId }: { appId: string }) => {
|
const Settings = ({ appId }: { appId: string }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const router = useRouter();
|
||||||
|
const { toast } = useToast();
|
||||||
const { appDetail, updateAppDetail, loadKbList, myKbList } = useUserStore();
|
const { appDetail, updateAppDetail, loadKbList, myKbList } = useUserStore();
|
||||||
const { isPc } = useGlobalStore();
|
const { isPc } = useGlobalStore();
|
||||||
|
|
||||||
const [editVariable, setEditVariable] = useState<VariableItemType>();
|
const [editVariable, setEditVariable] = useState<VariableItemType>();
|
||||||
|
const [settingAppInfo, setSettingAppInfo] = useState<AppSchema>();
|
||||||
useQuery(['initkb', appId], () => loadKbList());
|
|
||||||
|
|
||||||
const [refresh, setRefresh] = useState(false);
|
const [refresh, setRefresh] = useState(false);
|
||||||
|
|
||||||
@ -117,6 +123,24 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
[myKbList, kbList]
|
[myKbList, kbList]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* 点击删除 */
|
||||||
|
const { mutate: handleDelModel, isLoading } = useRequest({
|
||||||
|
mutationFn: async () => {
|
||||||
|
if (!appDetail) return null;
|
||||||
|
await delModelById(appDetail._id);
|
||||||
|
return 'success';
|
||||||
|
},
|
||||||
|
onSuccess(res) {
|
||||||
|
if (!res) return;
|
||||||
|
toast({
|
||||||
|
title: '删除成功',
|
||||||
|
status: 'success'
|
||||||
|
});
|
||||||
|
router.replace(`/app/list`);
|
||||||
|
},
|
||||||
|
errorToast: '删除失败'
|
||||||
|
});
|
||||||
|
|
||||||
const appModule2Form = useCallback(() => {
|
const appModule2Form = useCallback(() => {
|
||||||
const formVal = appModules2Form(appDetail.modules);
|
const formVal = appModules2Form(appDetail.modules);
|
||||||
reset(formVal);
|
reset(formVal);
|
||||||
@ -139,6 +163,8 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
appModule2Form();
|
appModule2Form();
|
||||||
}, [appModule2Form]);
|
}, [appModule2Form]);
|
||||||
|
|
||||||
|
useQuery(['initkb', appId], () => loadKbList());
|
||||||
|
|
||||||
const BoxStyles: BoxProps = {
|
const BoxStyles: BoxProps = {
|
||||||
bg: 'myWhite.200',
|
bg: 'myWhite.200',
|
||||||
px: 4,
|
px: 4,
|
||||||
@ -163,15 +189,95 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
display={['block', 'flex']}
|
|
||||||
flexDirection={'column'}
|
|
||||||
h={'100%'}
|
h={'100%'}
|
||||||
borderRight={'1.5px solid'}
|
borderRight={'1.5px solid'}
|
||||||
borderColor={'myGray.200'}
|
borderColor={'myGray.200'}
|
||||||
pt={4}
|
p={4}
|
||||||
pl={4}
|
pt={[0, 4]}
|
||||||
|
overflow={'overlay'}
|
||||||
>
|
>
|
||||||
<Flex pr={4} justifyContent={'space-between'}>
|
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
||||||
|
基础信息
|
||||||
|
</Box>
|
||||||
|
{/* basic info */}
|
||||||
|
<Box
|
||||||
|
border={theme.borders.base}
|
||||||
|
borderRadius={'lg'}
|
||||||
|
mt={2}
|
||||||
|
px={5}
|
||||||
|
py={4}
|
||||||
|
bg={'myBlue.100'}
|
||||||
|
position={'relative'}
|
||||||
|
>
|
||||||
|
<Flex alignItems={'center'} py={2}>
|
||||||
|
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} />
|
||||||
|
<Box ml={3} fontWeight={'bold'} fontSize={'lg'}>
|
||||||
|
{appDetail.name}
|
||||||
|
</Box>
|
||||||
|
<IconButton
|
||||||
|
className="delete"
|
||||||
|
position={'absolute'}
|
||||||
|
top={4}
|
||||||
|
right={4}
|
||||||
|
size={'sm'}
|
||||||
|
icon={<MyIcon name={'delete'} w={'14px'} />}
|
||||||
|
variant={'base'}
|
||||||
|
borderRadius={'md'}
|
||||||
|
aria-label={'delete'}
|
||||||
|
_hover={{
|
||||||
|
bg: 'myGray.100',
|
||||||
|
color: 'red.600'
|
||||||
|
}}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onClick={openConfirm(handleDelModel)}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Box
|
||||||
|
flex={1}
|
||||||
|
my={2}
|
||||||
|
className={'textEllipsis3'}
|
||||||
|
wordBreak={'break-all'}
|
||||||
|
color={'myGray.600'}
|
||||||
|
>
|
||||||
|
{appDetail.intro || '快来给应用一个介绍~'}
|
||||||
|
</Box>
|
||||||
|
<Flex>
|
||||||
|
<Button
|
||||||
|
size={['sm', 'md']}
|
||||||
|
variant={'base'}
|
||||||
|
leftIcon={<MyIcon name={'chatLight'} w={'16px'} />}
|
||||||
|
onClick={() => router.push(`/chat?appId=${appId}`)}
|
||||||
|
>
|
||||||
|
对话
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
mx={3}
|
||||||
|
size={['sm', 'md']}
|
||||||
|
variant={'base'}
|
||||||
|
leftIcon={<MyIcon name={'shareLight'} w={'16px'} />}
|
||||||
|
onClick={() => {
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
appId,
|
||||||
|
currentTab: 'share'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
分享
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size={['sm', 'md']}
|
||||||
|
variant={'base'}
|
||||||
|
leftIcon={<MyIcon name={'settingLight'} w={'16px'} />}
|
||||||
|
onClick={() => setSettingAppInfo(appDetail)}
|
||||||
|
>
|
||||||
|
设置
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Flex mt={5} justifyContent={'space-between'} alignItems={'center'}>
|
||||||
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
<Box fontSize={['md', 'xl']} fontWeight={'bold'}>
|
||||||
应用配置
|
应用配置
|
||||||
<MyTooltip label={'仅包含基础功能,复杂 agent 功能请使用高级编排。'} forceShow>
|
<MyTooltip label={'仅包含基础功能,复杂 agent 功能请使用高级编排。'} forceShow>
|
||||||
@ -181,221 +287,219 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
<Button
|
<Button
|
||||||
isLoading={isSaving}
|
isLoading={isSaving}
|
||||||
fontSize={'sm'}
|
fontSize={'sm'}
|
||||||
|
size={['sm', 'md']}
|
||||||
onClick={openConfirm(handleSubmit((data) => onSubmitSave(data)))}
|
onClick={openConfirm(handleSubmit((data) => onSubmitSave(data)))}
|
||||||
>
|
>
|
||||||
{isPc ? '保存并预览' : '保存'}
|
{isPc ? '保存并预览' : '保存'}
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box flex={'1 0 0'} my={4} pr={4} overflowY={'auto'}>
|
|
||||||
{/* variable */}
|
{/* variable */}
|
||||||
<Box {...BoxStyles}>
|
<Box mt={2} {...BoxStyles}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Avatar src={'/imgs/module/variable.png'} objectFit={'contain'} w={'18px'} />
|
<Avatar src={'/imgs/module/variable.png'} objectFit={'contain'} w={'18px'} />
|
||||||
<Box ml={2} flex={1}>
|
<Box ml={2} flex={1}>
|
||||||
变量
|
变量
|
||||||
</Box>
|
|
||||||
<Flex {...BoxBtnStyles} onClick={() => setEditVariable(addVariable())}>
|
|
||||||
+ 新增
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Box
|
|
||||||
mt={2}
|
|
||||||
borderRadius={'lg'}
|
|
||||||
overflow={'hidden'}
|
|
||||||
borderWidth={'1px'}
|
|
||||||
borderBottom="none"
|
|
||||||
>
|
|
||||||
<TableContainer>
|
|
||||||
<Table bg={'white'}>
|
|
||||||
<Thead>
|
|
||||||
<Tr>
|
|
||||||
<Th>变量名</Th>
|
|
||||||
<Th>变量 key</Th>
|
|
||||||
<Th>必填</Th>
|
|
||||||
<Th></Th>
|
|
||||||
</Tr>
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{variables.map((item, index) => (
|
|
||||||
<Tr key={item.id}>
|
|
||||||
<Td>{item.label} </Td>
|
|
||||||
<Td>{item.key}</Td>
|
|
||||||
<Td>{item.required ? '✔' : ''}</Td>
|
|
||||||
<Td>
|
|
||||||
<MyIcon
|
|
||||||
mr={3}
|
|
||||||
name={'settingLight'}
|
|
||||||
w={'16px'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
onClick={() => setEditVariable(item)}
|
|
||||||
/>
|
|
||||||
<MyIcon
|
|
||||||
name={'delete'}
|
|
||||||
w={'16px'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
onClick={() => removeVariable(index)}
|
|
||||||
/>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
<Flex {...BoxBtnStyles} onClick={() => setEditVariable(addVariable())}>
|
||||||
|
+ 新增
|
||||||
<Box mt={5} {...BoxStyles}>
|
|
||||||
<Flex alignItems={'center'}>
|
|
||||||
<Avatar src={'/imgs/module/AI.png'} w={'18px'} />
|
|
||||||
<Box ml={2}>AI 配置</Box>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
</Flex>
|
||||||
<Flex alignItems={'center'} mt={5}>
|
<Box mt={2} borderRadius={'lg'} overflow={'hidden'} borderWidth={'1px'} borderBottom="none">
|
||||||
<Box {...LabelStyles}>对话模型</Box>
|
<TableContainer>
|
||||||
<MySelect
|
<Table bg={'white'}>
|
||||||
width={['100%', '300px']}
|
<Thead>
|
||||||
value={getValues('chatModel.model')}
|
<Tr>
|
||||||
list={chatModelSelectList}
|
<Th>变量名</Th>
|
||||||
onchange={(val: any) => {
|
<Th>变量 key</Th>
|
||||||
setValue('chatModel.model', val);
|
<Th>必填</Th>
|
||||||
const maxToken =
|
<Th></Th>
|
||||||
chatModelList.find((item) => item.model === getValues('chatModel.model'))
|
</Tr>
|
||||||
?.contextMaxToken || 4000;
|
</Thead>
|
||||||
const token = maxToken / 2;
|
<Tbody>
|
||||||
setValue('chatModel.maxToken', token);
|
{variables.map((item, index) => (
|
||||||
setRefresh(!refresh);
|
<Tr key={item.id}>
|
||||||
}}
|
<Td>{item.label} </Td>
|
||||||
/>
|
<Td>{item.key}</Td>
|
||||||
</Flex>
|
<Td>{item.required ? '✔' : ''}</Td>
|
||||||
<Flex alignItems={'center'} my={10}>
|
<Td>
|
||||||
<Box {...LabelStyles}>温度</Box>
|
<MyIcon
|
||||||
<Box flex={1} ml={'10px'}>
|
mr={3}
|
||||||
<MySlider
|
name={'settingLight'}
|
||||||
markList={[
|
w={'16px'}
|
||||||
{ label: '严谨', value: 0 },
|
cursor={'pointer'}
|
||||||
{ label: '发散', value: 10 }
|
onClick={() => setEditVariable(item)}
|
||||||
]}
|
/>
|
||||||
width={'95%'}
|
<MyIcon
|
||||||
min={0}
|
name={'delete'}
|
||||||
max={10}
|
w={'16px'}
|
||||||
value={getValues('chatModel.temperature')}
|
cursor={'pointer'}
|
||||||
onChange={(e) => {
|
onClick={() => removeVariable(index)}
|
||||||
setValue('chatModel.temperature', e);
|
/>
|
||||||
setRefresh(!refresh);
|
</Td>
|
||||||
}}
|
</Tr>
|
||||||
/>
|
))}
|
||||||
</Box>
|
</Tbody>
|
||||||
</Flex>
|
</Table>
|
||||||
<Flex alignItems={'center'} mt={12} mb={10}>
|
</TableContainer>
|
||||||
<Box {...LabelStyles}>回复上限</Box>
|
|
||||||
<Box flex={1} ml={'10px'}>
|
|
||||||
<MySlider
|
|
||||||
markList={[
|
|
||||||
{ label: '100', value: 100 },
|
|
||||||
{ label: `${tokenLimit}`, value: tokenLimit }
|
|
||||||
]}
|
|
||||||
width={'95%'}
|
|
||||||
min={100}
|
|
||||||
max={tokenLimit}
|
|
||||||
step={50}
|
|
||||||
value={getValues('chatModel.maxToken')}
|
|
||||||
onChange={(val) => {
|
|
||||||
setValue('chatModel.maxToken', val);
|
|
||||||
setRefresh(!refresh);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
<Flex mt={10} alignItems={'flex-start'}>
|
|
||||||
<Box {...LabelStyles}>
|
|
||||||
提示词
|
|
||||||
<MyTooltip label={ChatModelSystemTip} forceShow>
|
|
||||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
|
||||||
</MyTooltip>
|
|
||||||
</Box>
|
|
||||||
<Textarea
|
|
||||||
rows={5}
|
|
||||||
placeholder={ChatModelSystemTip}
|
|
||||||
borderColor={'myGray.100'}
|
|
||||||
{...register('chatModel.systemPrompt')}
|
|
||||||
></Textarea>
|
|
||||||
</Flex>
|
|
||||||
<Flex mt={5} alignItems={'flex-start'}>
|
|
||||||
<Box {...LabelStyles}>
|
|
||||||
限定词
|
|
||||||
<MyTooltip label={ChatModelLimitTip} forceShow>
|
|
||||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
|
||||||
</MyTooltip>
|
|
||||||
</Box>
|
|
||||||
<Textarea
|
|
||||||
rows={5}
|
|
||||||
placeholder={ChatModelLimitTip}
|
|
||||||
borderColor={'myGray.100'}
|
|
||||||
{...register('chatModel.limitPrompt')}
|
|
||||||
></Textarea>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* kb */}
|
|
||||||
<Box mt={5} {...BoxStyles}>
|
|
||||||
<Flex alignItems={'center'}>
|
|
||||||
<Flex alignItems={'center'} flex={1}>
|
|
||||||
<Avatar src={'/imgs/module/db.png'} w={'18px'} />
|
|
||||||
<Box ml={2}>知识库</Box>
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems={'center'} mr={3} {...BoxBtnStyles} onClick={onOpenKbSelect}>
|
|
||||||
<SmallAddIcon />
|
|
||||||
选择
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems={'center'} {...BoxBtnStyles} onClick={onOpenKbParams}>
|
|
||||||
<MyIcon name={'edit'} w={'14px'} mr={1} />
|
|
||||||
参数
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}>
|
|
||||||
相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')},
|
|
||||||
空搜索时拒绝回复: {getValues('kb.searchEmptyText') !== '' ? 'true' : 'false'}
|
|
||||||
</Flex>
|
|
||||||
<Grid templateColumns={['1fr', 'repeat(2,1fr)']} my={2} gridGap={[2, 4]}>
|
|
||||||
{selectedKbList.map((item) => (
|
|
||||||
<Flex
|
|
||||||
key={item._id}
|
|
||||||
alignItems={'center'}
|
|
||||||
p={2}
|
|
||||||
bg={'white'}
|
|
||||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
|
||||||
borderRadius={'md'}
|
|
||||||
border={theme.borders.base}
|
|
||||||
>
|
|
||||||
<Avatar src={item.avatar} w={'18px'} mr={1} />
|
|
||||||
<Box flex={'1 0 0'} w={0} className={'textEllipsis'} fontSize={'sm'}>
|
|
||||||
{item.name}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* welcome */}
|
|
||||||
<Box mt={5} {...BoxStyles}>
|
|
||||||
<Flex alignItems={'center'}>
|
|
||||||
<Avatar src={'/imgs/module/userGuide.png'} w={'18px'} />
|
|
||||||
<Box mx={2}>对话开场白</Box>
|
|
||||||
<MyTooltip label={welcomeTextTip} forceShow>
|
|
||||||
<QuestionOutlineIcon />
|
|
||||||
</MyTooltip>
|
|
||||||
</Flex>
|
|
||||||
<Textarea
|
|
||||||
mt={2}
|
|
||||||
rows={5}
|
|
||||||
placeholder={welcomeTextTip}
|
|
||||||
borderColor={'myGray.100'}
|
|
||||||
{...register('guide.welcome.text')}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* model */}
|
||||||
|
<Box mt={5} {...BoxStyles}>
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
<Avatar src={'/imgs/module/AI.png'} w={'18px'} />
|
||||||
|
<Box ml={2}>AI 配置</Box>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex alignItems={'center'} mt={5}>
|
||||||
|
<Box {...LabelStyles}>对话模型</Box>
|
||||||
|
<MySelect
|
||||||
|
width={['100%', '300px']}
|
||||||
|
value={getValues('chatModel.model')}
|
||||||
|
list={chatModelSelectList}
|
||||||
|
onchange={(val: any) => {
|
||||||
|
setValue('chatModel.model', val);
|
||||||
|
const maxToken =
|
||||||
|
chatModelList.find((item) => item.model === getValues('chatModel.model'))
|
||||||
|
?.contextMaxToken || 4000;
|
||||||
|
const token = maxToken / 2;
|
||||||
|
setValue('chatModel.maxToken', token);
|
||||||
|
setRefresh(!refresh);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} my={10}>
|
||||||
|
<Box {...LabelStyles}>温度</Box>
|
||||||
|
<Box flex={1} ml={'10px'}>
|
||||||
|
<MySlider
|
||||||
|
markList={[
|
||||||
|
{ label: '严谨', value: 0 },
|
||||||
|
{ label: '发散', value: 10 }
|
||||||
|
]}
|
||||||
|
width={'95%'}
|
||||||
|
min={0}
|
||||||
|
max={10}
|
||||||
|
value={getValues('chatModel.temperature')}
|
||||||
|
onChange={(e) => {
|
||||||
|
setValue('chatModel.temperature', e);
|
||||||
|
setRefresh(!refresh);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} mt={12} mb={10}>
|
||||||
|
<Box {...LabelStyles}>回复上限</Box>
|
||||||
|
<Box flex={1} ml={'10px'}>
|
||||||
|
<MySlider
|
||||||
|
markList={[
|
||||||
|
{ label: '100', value: 100 },
|
||||||
|
{ label: `${tokenLimit}`, value: tokenLimit }
|
||||||
|
]}
|
||||||
|
width={'95%'}
|
||||||
|
min={100}
|
||||||
|
max={tokenLimit}
|
||||||
|
step={50}
|
||||||
|
value={getValues('chatModel.maxToken')}
|
||||||
|
onChange={(val) => {
|
||||||
|
setValue('chatModel.maxToken', val);
|
||||||
|
setRefresh(!refresh);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
<Flex mt={10} alignItems={'flex-start'}>
|
||||||
|
<Box {...LabelStyles}>
|
||||||
|
提示词
|
||||||
|
<MyTooltip label={ChatModelSystemTip} forceShow>
|
||||||
|
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||||
|
</MyTooltip>
|
||||||
|
</Box>
|
||||||
|
<Textarea
|
||||||
|
rows={5}
|
||||||
|
placeholder={ChatModelSystemTip}
|
||||||
|
borderColor={'myGray.100'}
|
||||||
|
{...register('chatModel.systemPrompt')}
|
||||||
|
></Textarea>
|
||||||
|
</Flex>
|
||||||
|
<Flex mt={5} alignItems={'flex-start'}>
|
||||||
|
<Box {...LabelStyles}>
|
||||||
|
限定词
|
||||||
|
<MyTooltip label={ChatModelLimitTip} forceShow>
|
||||||
|
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||||
|
</MyTooltip>
|
||||||
|
</Box>
|
||||||
|
<Textarea
|
||||||
|
rows={5}
|
||||||
|
placeholder={ChatModelLimitTip}
|
||||||
|
borderColor={'myGray.100'}
|
||||||
|
{...register('chatModel.limitPrompt')}
|
||||||
|
></Textarea>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* kb */}
|
||||||
|
<Box mt={5} {...BoxStyles}>
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
<Flex alignItems={'center'} flex={1}>
|
||||||
|
<Avatar src={'/imgs/module/db.png'} w={'18px'} />
|
||||||
|
<Box ml={2}>知识库</Box>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} mr={3} {...BoxBtnStyles} onClick={onOpenKbSelect}>
|
||||||
|
<SmallAddIcon />
|
||||||
|
选择
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems={'center'} {...BoxBtnStyles} onClick={onOpenKbParams}>
|
||||||
|
<MyIcon name={'edit'} w={'14px'} mr={1} />
|
||||||
|
参数
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}>
|
||||||
|
相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')},
|
||||||
|
空搜索时拒绝回复: {getValues('kb.searchEmptyText') !== '' ? 'true' : 'false'}
|
||||||
|
</Flex>
|
||||||
|
<Grid templateColumns={['1fr', 'repeat(2,1fr)']} my={2} gridGap={[2, 4]}>
|
||||||
|
{selectedKbList.map((item) => (
|
||||||
|
<Flex
|
||||||
|
key={item._id}
|
||||||
|
alignItems={'center'}
|
||||||
|
p={2}
|
||||||
|
bg={'white'}
|
||||||
|
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||||
|
borderRadius={'md'}
|
||||||
|
border={theme.borders.base}
|
||||||
|
>
|
||||||
|
<Avatar src={item.avatar} w={'18px'} mr={1} />
|
||||||
|
<Box flex={'1 0 0'} w={0} className={'textEllipsis'} fontSize={'sm'}>
|
||||||
|
{item.name}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* welcome */}
|
||||||
|
<Box mt={5} {...BoxStyles}>
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
<Avatar src={'/imgs/module/userGuide.png'} w={'18px'} />
|
||||||
|
<Box mx={2}>对话开场白</Box>
|
||||||
|
<MyTooltip label={welcomeTextTip} forceShow>
|
||||||
|
<QuestionOutlineIcon />
|
||||||
|
</MyTooltip>
|
||||||
|
</Flex>
|
||||||
|
<Textarea
|
||||||
|
mt={2}
|
||||||
|
rows={5}
|
||||||
|
placeholder={welcomeTextTip}
|
||||||
|
borderColor={'myGray.100'}
|
||||||
|
{...register('guide.welcome.text')}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<ConfirmChild />
|
<ConfirmChild />
|
||||||
|
{settingAppInfo && (
|
||||||
|
<InfoModal defaultApp={settingAppInfo} onClose={() => setSettingAppInfo(undefined)} />
|
||||||
|
)}
|
||||||
{editVariable && (
|
{editVariable && (
|
||||||
<VariableEditModal
|
<VariableEditModal
|
||||||
defaultVariable={editVariable}
|
defaultVariable={editVariable}
|
||||||
|
|||||||
@ -1,165 +0,0 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
|
||||||
import { Box, Flex, Button, Grid, useTheme, IconButton } from '@chakra-ui/react';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { useUserStore } from '@/store/user';
|
|
||||||
import { useToast } from '@/hooks/useToast';
|
|
||||||
import { useLoading } from '@/hooks/useLoading';
|
|
||||||
import { delModelById } from '@/api/app';
|
|
||||||
import { useConfirm } from '@/hooks/useConfirm';
|
|
||||||
import { AppSchema } from '@/types/mongoSchema';
|
|
||||||
|
|
||||||
import dynamic from 'next/dynamic';
|
|
||||||
import Avatar from '@/components/Avatar';
|
|
||||||
import MyIcon from '@/components/Icon';
|
|
||||||
import TotalUsage from './Charts/TotalUsage';
|
|
||||||
import MyTooltip from '@/components/MyTooltip';
|
|
||||||
|
|
||||||
const InfoModal = dynamic(() => import('./InfoModal'));
|
|
||||||
|
|
||||||
const OverView = ({ appId }: { appId: string }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
const { toast } = useToast();
|
|
||||||
const router = useRouter();
|
|
||||||
const { Loading, setIsLoading } = useLoading();
|
|
||||||
const { appDetail, loadAppDetail } = useUserStore();
|
|
||||||
const { openConfirm, ConfirmChild } = useConfirm({
|
|
||||||
content: '确认删除该应用?'
|
|
||||||
});
|
|
||||||
const [settingAppInfo, setSettingAppInfo] = useState<AppSchema>();
|
|
||||||
|
|
||||||
/* 点击删除 */
|
|
||||||
const handleDelModel = useCallback(async () => {
|
|
||||||
if (!appDetail) return;
|
|
||||||
setIsLoading(true);
|
|
||||||
try {
|
|
||||||
await delModelById(appDetail._id);
|
|
||||||
toast({
|
|
||||||
title: '删除成功',
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
router.replace(`/app/list`);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: err?.message || '删除失败',
|
|
||||||
status: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
}, [appDetail, setIsLoading, toast, router]);
|
|
||||||
|
|
||||||
// load app data
|
|
||||||
useQuery([appId], () => loadAppDetail(appId, true), {
|
|
||||||
enabled: false
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box h={'100%'} display={['block', 'flex']} flexDirection={'column'} position={'relative'}>
|
|
||||||
<Grid
|
|
||||||
gridTemplateColumns={['1fr', 'repeat(2,1fr)']}
|
|
||||||
gridGap={[2, 4, 6]}
|
|
||||||
pt={[0, 7]}
|
|
||||||
px={[3, 5, 8]}
|
|
||||||
>
|
|
||||||
<Box>
|
|
||||||
<Box mb={2} fontSize={['md', 'xl']}>
|
|
||||||
基本信息
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
border={theme.borders.base}
|
|
||||||
borderRadius={'lg'}
|
|
||||||
px={5}
|
|
||||||
py={4}
|
|
||||||
bg={'myBlue.100'}
|
|
||||||
position={'relative'}
|
|
||||||
>
|
|
||||||
<Flex alignItems={'center'} py={2}>
|
|
||||||
<MyTooltip label={'选择头像'}>
|
|
||||||
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} />
|
|
||||||
</MyTooltip>
|
|
||||||
<Box ml={3} fontWeight={'bold'} fontSize={'lg'}>
|
|
||||||
{appDetail.name}
|
|
||||||
</Box>
|
|
||||||
<IconButton
|
|
||||||
className="delete"
|
|
||||||
position={'absolute'}
|
|
||||||
top={4}
|
|
||||||
right={4}
|
|
||||||
size={'sm'}
|
|
||||||
icon={<MyIcon name={'delete'} w={'14px'} />}
|
|
||||||
variant={'base'}
|
|
||||||
borderRadius={'md'}
|
|
||||||
aria-label={'delete'}
|
|
||||||
_hover={{
|
|
||||||
bg: 'myGray.100',
|
|
||||||
color: 'red.600'
|
|
||||||
}}
|
|
||||||
onClick={openConfirm(handleDelModel)}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Box
|
|
||||||
h={'68px'}
|
|
||||||
flex={1}
|
|
||||||
my={2}
|
|
||||||
className={'textEllipsis3'}
|
|
||||||
wordBreak={'break-all'}
|
|
||||||
color={'myGray.600'}
|
|
||||||
>
|
|
||||||
{appDetail.intro || '快来给应用一个介绍~'}
|
|
||||||
</Box>
|
|
||||||
<Flex>
|
|
||||||
<Button
|
|
||||||
size={['sm', 'md']}
|
|
||||||
variant={'base'}
|
|
||||||
leftIcon={<MyIcon name={'chatLight'} w={'16px'} />}
|
|
||||||
onClick={() => router.push(`/chat?appId=${appId}`)}
|
|
||||||
>
|
|
||||||
对话
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
mx={3}
|
|
||||||
size={['sm', 'md']}
|
|
||||||
variant={'base'}
|
|
||||||
leftIcon={<MyIcon name={'shareLight'} w={'16px'} />}
|
|
||||||
onClick={() => {
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
appId,
|
|
||||||
currentTab: 'share'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
分享
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size={['sm', 'md']}
|
|
||||||
variant={'base'}
|
|
||||||
leftIcon={<MyIcon name={'settingLight'} w={'16px'} />}
|
|
||||||
onClick={() => setSettingAppInfo(appDetail)}
|
|
||||||
>
|
|
||||||
设置
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Flex flexDirection={'column'}>
|
|
||||||
<Box mb={2} fontSize={['md', 'xl']}>
|
|
||||||
近 14 日消费
|
|
||||||
</Box>
|
|
||||||
<Box flex={'1 0 0'} boxShadow={theme.shadows.base} borderRadius={'lg'} px={5} py={4}>
|
|
||||||
<TotalUsage appId={appId} />
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{settingAppInfo && (
|
|
||||||
<InfoModal defaultApp={settingAppInfo} onClose={() => setSettingAppInfo(undefined)} />
|
|
||||||
)}
|
|
||||||
<ConfirmChild />
|
|
||||||
<Loading fixed={false} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OverView;
|
|
||||||
@ -9,16 +9,12 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
|
|
||||||
import Tabs from '@/components/Tabs';
|
import Tabs from '@/components/Tabs';
|
||||||
import SideTabs from '@/components/SideTabs';
|
import SideTabs from '@/components/SideTabs';
|
||||||
import OverView from './components/OverView';
|
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
import PageContainer from '@/components/PageContainer';
|
import PageContainer from '@/components/PageContainer';
|
||||||
import Loading from '@/components/Loading';
|
import Loading from '@/components/Loading';
|
||||||
|
import BasicEdit from './components/BasicEdit';
|
||||||
|
|
||||||
const BasicEdit = dynamic(() => import('./components/BasicEdit'), {
|
|
||||||
ssr: false,
|
|
||||||
loading: () => <Loading />
|
|
||||||
});
|
|
||||||
const AdEdit = dynamic(() => import('./components/AdEdit'), {
|
const AdEdit = dynamic(() => import('./components/AdEdit'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
loading: () => <Loading />
|
loading: () => <Loading />
|
||||||
@ -31,7 +27,6 @@ const API = dynamic(() => import('./components/API'), {
|
|||||||
});
|
});
|
||||||
|
|
||||||
enum TabEnum {
|
enum TabEnum {
|
||||||
'overview' = 'overview',
|
|
||||||
'basicEdit' = 'basicEdit',
|
'basicEdit' = 'basicEdit',
|
||||||
'adEdit' = 'adEdit',
|
'adEdit' = 'adEdit',
|
||||||
'share' = 'share',
|
'share' = 'share',
|
||||||
@ -59,8 +54,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
|
|
||||||
const tabList = useMemo(
|
const tabList = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{ label: '概览', id: TabEnum.overview, icon: 'overviewLight' },
|
{ label: '简易配置', id: TabEnum.basicEdit, icon: 'overviewLight' },
|
||||||
{ label: '简易编排', id: TabEnum.basicEdit, icon: 'edit' },
|
|
||||||
{ label: '高级编排', id: TabEnum.adEdit, icon: 'settingLight' },
|
{ label: '高级编排', id: TabEnum.adEdit, icon: 'settingLight' },
|
||||||
{ label: '链接分享', id: TabEnum.share, icon: 'shareLight' },
|
{ label: '链接分享', id: TabEnum.share, icon: 'shareLight' },
|
||||||
{ label: 'API访问', id: TabEnum.API, icon: 'apiLight' },
|
{ label: 'API访问', id: TabEnum.API, icon: 'apiLight' },
|
||||||
@ -174,13 +168,12 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flex={'1 0 0'} h={[0, '100%']} overflow={['overlay', '']}>
|
<Box flex={'1 0 0'} h={[0, '100%']} overflow={['overlay', '']}>
|
||||||
{currentTab === TabEnum.overview && <OverView appId={appId} />}
|
|
||||||
{currentTab === TabEnum.basicEdit && <BasicEdit appId={appId} />}
|
{currentTab === TabEnum.basicEdit && <BasicEdit appId={appId} />}
|
||||||
{currentTab === TabEnum.adEdit && appDetail && (
|
{currentTab === TabEnum.adEdit && appDetail && (
|
||||||
<AdEdit
|
<AdEdit
|
||||||
app={appDetail}
|
app={appDetail}
|
||||||
fullScreen={true}
|
fullScreen={true}
|
||||||
onFullScreen={() => setCurrentTab(TabEnum.overview)}
|
onFullScreen={() => setCurrentTab(TabEnum.basicEdit)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{currentTab === TabEnum.API && <API appId={appId} />}
|
{currentTab === TabEnum.API && <API appId={appId} />}
|
||||||
@ -192,7 +185,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function getServerSideProps(context: any) {
|
export async function getServerSideProps(context: any) {
|
||||||
const currentTab = context?.query?.currentTab || TabEnum.overview;
|
const currentTab = context?.query?.currentTab || TabEnum.basicEdit;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: { currentTab }
|
props: { currentTab }
|
||||||
|
|||||||
@ -21,7 +21,7 @@ const SliderApps = ({ appId }: { appId: string }) => {
|
|||||||
px={3}
|
px={3}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
_hover={{ bg: 'myGray.200' }}
|
_hover={{ bg: 'myGray.200' }}
|
||||||
onClick={() => router.push('/app/list')}
|
onClick={() => router.back()}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
mr={3}
|
mr={3}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user