feat: phone slider

This commit is contained in:
archer
2023-07-26 11:59:12 +08:00
parent 248be38939
commit ffdef41bf2
24 changed files with 275 additions and 166 deletions

View File

@@ -35,7 +35,7 @@ const map = {
apikey: require('./icons/apikey.svg').default,
save: require('./icons/save.svg').default,
minus: require('./icons/minus.svg').default,
chatLight: require('./icons/light/chat.svg').default,
chat: require('./icons/light/chat.svg').default,
chatFill: require('./icons/fill/chat.svg').default,
clear: require('./icons/light/clear.svg').default,
apiLight: require('./icons/light/appApi.svg').default,

View File

@@ -26,7 +26,7 @@ const Navbar = ({ unread }: { unread: number }) => {
() => [
{
label: t('navbar.Chat'),
icon: 'chatLight',
icon: 'chat',
activeIcon: 'chatFill',
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
activeLink: ['/chat']

View File

@@ -14,7 +14,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => {
() => [
{
label: t('navbar.Chat'),
icon: 'chatLight',
icon: 'chat',
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
activeLink: ['/chat'],
unread: 0

View File

@@ -1,6 +1,7 @@
import React, { useMemo } from 'react';
import { Box, Grid, useTheme } from '@chakra-ui/react';
import { Box, Grid } from '@chakra-ui/react';
import type { GridProps } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
// @ts-ignore
interface Props extends GridProps {
@@ -11,7 +12,7 @@ interface Props extends GridProps {
}
const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
const theme = useTheme();
const { t } = useTranslation();
const sizeMap = useMemo(() => {
switch (size) {
case 'sm':
@@ -47,14 +48,14 @@ const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
<Box
key={item.id}
py={sizeMap.inlineP}
borderRadius={'sm'}
textAlign={'center'}
borderBottom={'2px solid transparent'}
{...(activeId === item.id
? {
boxShadow: '0px 2px 2px rgba(137, 156, 171, 0.25)',
backgroundImage: theme.lgColor.primary2,
color: 'white',
cursor: 'default'
color: 'myBlue.700',
cursor: 'default',
fontWeight: 'bold',
borderBottomColor: 'myBlue.700'
}
: {
cursor: 'pointer'
@@ -64,7 +65,7 @@ const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
onChange(item.id);
}}
>
{item.label}
{t(item.label) || item.label}
</Box>
))}
</Grid>

View File

@@ -11,8 +11,9 @@ import {
} from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
export const useConfirm = ({ title = 'Warning', content }: { title?: string; content: string }) => {
export const useConfirm = (props: { title?: string; content: string }) => {
const { t } = useTranslation();
const { title = t('Warning'), content } = props;
const { isOpen, onOpen, onClose } = useDisclosure();
const cancelRef = useRef(null);
@@ -29,13 +30,13 @@ export const useConfirm = ({ title = 'Warning', content }: { title?: string; con
},
[onOpen]
),
ConfirmChild: useCallback(
ConfirmModal: useCallback(
() => (
<AlertDialog isOpen={isOpen} leastDestructiveRef={cancelRef} onClose={onClose}>
<AlertDialogOverlay>
<AlertDialogContent maxW={'min(90vw,400px)'}>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
{t(title)}
{title}
</AlertDialogHeader>
<AlertDialogBody>{content}</AlertDialogBody>

View File

@@ -39,7 +39,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
{ icon: 'loginoutLight', label: '登出', id: TabEnum.loginout, Component: () => <></> }
]);
const { openConfirm, ConfirmChild } = useConfirm({
const { openConfirm, ConfirmModal } = useConfirm({
content: '确认退出登录?'
});
@@ -111,7 +111,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
{currentTab === TabEnum.inform && <InformTable />}
</Box>
</Flex>
<ConfirmChild />
<ConfirmModal />
</PageContainer>
);
};

View File

@@ -347,7 +347,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
<MyTooltip label={'测试对话'}>
<IconButton
mr={6}
icon={<MyIcon name={'chatLight'} w={['14px', '16px']} />}
icon={<MyIcon name={'chat'} w={['14px', '16px']} />}
borderRadius={'lg'}
aria-label={'save'}
variant={'base'}

View File

@@ -52,6 +52,7 @@ import MyTooltip from '@/components/MyTooltip';
import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon';
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
import { useTranslation } from 'react-i18next';
import { getSpecialModule } from '@/components/ChatBox/utils';
import { addVariable } from '../VariableEditModal';
@@ -63,6 +64,7 @@ const InfoModal = dynamic(() => import('../InfoModal'));
const Settings = ({ appId }: { appId: string }) => {
const theme = useTheme();
const router = useRouter();
const { t } = useTranslation();
const { toast } = useToast();
const { appDetail, updateAppDetail, loadKbList, myKbList } = useUserStore();
const { isPc } = useGlobalStore();
@@ -72,9 +74,11 @@ const Settings = ({ appId }: { appId: string }) => {
const [refresh, setRefresh] = useState(false);
const { openConfirm, ConfirmChild } = useConfirm({
title: '警告',
content: '保存后将会覆盖高级编排配置,请确保该应用未使用高级编排功能。'
const { openConfirm: openConfirmSave, ConfirmModal: ConfirmSaveModal } = useConfirm({
content: t('app.Confirm Save App Tip')
});
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
content: t('app.Confirm Del App Tip')
});
const { register, setValue, getValues, reset, handleSubmit, control } = useForm<EditFormType>({
defaultValues: getDefaultAppForm()
@@ -227,7 +231,7 @@ const Settings = ({ appId }: { appId: string }) => {
color: 'red.600'
}}
isLoading={isLoading}
onClick={openConfirm(handleDelModel)}
onClick={openConfirmDel(handleDelModel)}
/>
</Flex>
<Box
@@ -243,7 +247,7 @@ const Settings = ({ appId }: { appId: string }) => {
<Button
size={['sm', 'md']}
variant={'base'}
leftIcon={<MyIcon name={'chatLight'} w={'16px'} />}
leftIcon={<MyIcon name={'chat'} w={'16px'} />}
onClick={() => router.push(`/chat?appId=${appId}`)}
>
@@ -286,7 +290,7 @@ const Settings = ({ appId }: { appId: string }) => {
isLoading={isSaving}
fontSize={'sm'}
size={['sm', 'md']}
onClick={openConfirm(handleSubmit((data) => onSubmitSave(data)))}
onClick={openConfirmSave(handleSubmit((data) => onSubmitSave(data)))}
>
{isPc ? '保存并预览' : '保存'}
</Button>
@@ -494,7 +498,8 @@ const Settings = ({ appId }: { appId: string }) => {
/>
</Box>
<ConfirmChild />
<ConfirmSaveModal />
<ConfirmDelModal />
{settingAppInfo && (
<InfoModal defaultApp={settingAppInfo} onClose={() => setSettingAppInfo(undefined)} />
)}

View File

@@ -59,7 +59,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
{ label: '高级编排', id: TabEnum.adEdit, icon: 'settingLight' },
{ label: '链接分享', id: TabEnum.share, icon: 'shareLight' },
{ label: 'API访问', id: TabEnum.API, icon: 'apiLight' },
{ label: '立即对话', id: 'startChat', icon: 'chatLight' }
{ label: '立即对话', id: 'startChat', icon: 'chat' }
],
[]
);

View File

@@ -34,7 +34,7 @@ const MyApps = () => {
const theme = useTheme();
const router = useRouter();
const { myApps, loadMyApps } = useUserStore();
const { openConfirm, ConfirmChild } = useConfirm({
const { openConfirm, ConfirmModal } = useConfirm({
title: '删除提示',
content: '确认删除该应用所有信息?'
});
@@ -53,7 +53,7 @@ const MyApps = () => {
title: '删除成功',
status: 'success'
});
loadMyApps();
loadMyApps(true);
} catch (err: any) {
toast({
title: err?.message || '删除失败',
@@ -65,7 +65,7 @@ const MyApps = () => {
);
/* 加载模型 */
useQuery(['loadModels'], loadMyApps, {
useQuery(['loadModels'], () => loadMyApps(true), {
refetchOnMount: true
});
@@ -147,7 +147,7 @@ const MyApps = () => {
size={'sm'}
icon={
<MyTooltip label={'去聊天'}>
<MyIcon name={'chatLight'} w={'14px'} />
<MyIcon name={'chat'} w={'14px'} />
</MyTooltip>
}
variant={'base'}
@@ -165,8 +165,10 @@ const MyApps = () => {
</Card>
))}
</Grid>
<ConfirmChild />
{isOpenCreateModal && <CreateModal onClose={onCloseCreateModal} onSuccess={loadMyApps} />}
<ConfirmModal />
{isOpenCreateModal && (
<CreateModal onClose={onCloseCreateModal} onSuccess={() => loadMyApps(true)} />
)}
</PageContainer>
);
};

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useMemo, useState } from 'react';
import {
Box,
Button,
@@ -18,6 +18,9 @@ import MyTooltip from '@/components/MyTooltip';
import MyIcon from '@/components/Icon';
import { useTranslation } from 'react-i18next';
import { useConfirm } from '@/hooks/useConfirm';
import Tabs from '@/components/Tabs';
import { useUserStore } from '@/store/user';
import { useQuery } from '@tanstack/react-query';
type HistoryItemType = {
id: string;
@@ -26,6 +29,11 @@ type HistoryItemType = {
top?: boolean;
};
enum TabEnum {
'app' = 'app',
'history' = 'history'
}
const ChatHistorySlider = ({
appId,
appName,
@@ -36,7 +44,8 @@ const ChatHistorySlider = ({
onDelHistory,
onClearHistory,
onSetHistoryTop,
onSetCustomTitle
onSetCustomTitle,
onClose
}: {
appId?: string;
appName: string;
@@ -48,25 +57,41 @@ const ChatHistorySlider = ({
onClearHistory: () => void;
onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void;
onSetCustomTitle?: (e: { chatId: string; title: string }) => void;
onClose: () => void;
}) => {
const theme = useTheme();
const router = useRouter();
const { t } = useTranslation();
const { isPc } = useGlobalStore();
const { myApps, loadMyApps, userInfo } = useUserStore();
const [currentTab, setCurrentTab] = useState<`${TabEnum}`>(TabEnum.history);
// custom title edit
const { onOpenModal, EditModal: EditTitleModal } = useEditInfo({
title: '自定义历史记录标题',
placeholder: '如果设置为空,会自动跟随聊天记录。'
});
const { openConfirm, ConfirmChild } = useConfirm({
const { openConfirm, ConfirmModal } = useConfirm({
content: t('chat.Confirm to clear history')
});
const concatHistory = useMemo<HistoryItemType[]>(
() => (!activeChatId ? [{ id: activeChatId, title: '新对话' }].concat(history) : history),
() =>
!activeChatId ? [{ id: activeChatId, title: t('chat.New Chat') }].concat(history) : history,
[activeChatId, history]
);
const isShare = useMemo(() => !appId || !userInfo, [appId, userInfo]);
useQuery(['init'], () => {
if (isShare) {
setCurrentTab(TabEnum.history);
return null;
}
return loadMyApps(false);
});
return (
<Flex
position={'relative'}
@@ -100,130 +125,183 @@ const ChatHistorySlider = ({
</Flex>
</MyTooltip>
)}
{/* btn */}
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5}>
{/* menu */}
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
{!isPc && !isShare && (
<Tabs
w={'120px'}
mr={2}
list={[
{ label: 'App', id: TabEnum.app },
{ label: 'chat.History', id: TabEnum.history }
]}
activeId={currentTab}
onChange={(e) => setCurrentTab(e as `${TabEnum}`)}
/>
)}
<Button
variant={'base'}
flex={1}
h={'100%'}
color={'myBlue.700'}
borderRadius={'xl'}
leftIcon={<MyIcon name={'edit'} w={'16px'} />}
leftIcon={<MyIcon name={'chat'} w={'16px'} />}
overflow={'hidden'}
onClick={() => onChangeChat()}
>
{t('chat.New Chat')}
</Button>
<IconButton
ml={3}
h={'100%'}
variant={'base'}
aria-label={''}
borderRadius={'xl'}
onClick={openConfirm(onClearHistory)}
>
<MyIcon name={'clear'} w={'16px'} />
</IconButton>
{(isPc || isShare) && (
<IconButton
ml={3}
h={'100%'}
variant={'base'}
aria-label={''}
borderRadius={'xl'}
onClick={openConfirm(onClearHistory)}
>
<MyIcon name={'clear'} w={'16px'} />
</IconButton>
)}
</Flex>
{/* chat history */}
<Box flex={'1 0 0'} h={0} px={[2, 5]} overflow={'overlay'}>
{concatHistory.map((item, i) => (
<Flex
position={'relative'}
key={item.id || `${i}`}
alignItems={'center'}
py={3}
px={4}
cursor={'pointer'}
userSelect={'none'}
borderRadius={'lg'}
mb={2}
_hover={{
bg: 'myGray.100',
'& .more': {
display: 'block'
}
}}
bg={item.top ? '#E6F6F6 !important' : ''}
{...(item.id === activeChatId
? {
backgroundColor: 'myBlue.100 !important',
color: 'myBlue.700'
}
: {
onClick: () => {
onChangeChat(item.id);
{/* chat history */}
{(currentTab === TabEnum.history || isPc) && (
<>
{concatHistory.map((item, i) => (
<Flex
position={'relative'}
key={item.id || `${i}`}
alignItems={'center'}
py={3}
px={4}
cursor={'pointer'}
userSelect={'none'}
borderRadius={'lg'}
mb={2}
_hover={{
bg: 'myGray.100',
'& .more': {
display: 'block'
}
})}
>
<MyIcon name={item.id === activeChatId ? 'chatFill' : 'chatLight'} w={'16px'} />
<Box flex={'1 0 0'} ml={3} className="textEllipsis">
{item.customTitle || item.title}
</Box>
{!!item.id && (
<Box className="more" display={['block', 'none']}>
<Menu autoSelect={false} isLazy offset={[0, 5]}>
<MenuButton
_hover={{ bg: 'white' }}
cursor={'pointer'}
borderRadius={'md'}
onClick={(e) => {
e.stopPropagation();
}}
>
<MyIcon name={'more'} w={'14px'} p={1} />
</MenuButton>
<MenuList color={'myGray.700'} minW={`90px !important`}>
{onSetHistoryTop && (
<MenuItem
}}
bg={item.top ? '#E6F6F6 !important' : ''}
{...(item.id === activeChatId
? {
backgroundColor: 'myBlue.100 !important',
color: 'myBlue.700'
}
: {
onClick: () => {
onChangeChat(item.id);
}
})}
>
<MyIcon name={item.id === activeChatId ? 'chatFill' : 'chat'} w={'16px'} />
<Box flex={'1 0 0'} ml={3} className="textEllipsis">
{item.customTitle || item.title}
</Box>
{!!item.id && (
<Box className="more" display={['block', 'none']}>
<Menu autoSelect={false} isLazy offset={[0, 5]}>
<MenuButton
_hover={{ bg: 'white' }}
cursor={'pointer'}
borderRadius={'md'}
onClick={(e) => {
e.stopPropagation();
onSetHistoryTop({ chatId: item.id, top: !item.top });
}}
>
<MyIcon mr={2} name={'setTop'} w={'16px'}></MyIcon>
{item.top ? '取消置顶' : '置顶'}
</MenuItem>
)}
{onSetCustomTitle && (
<MenuItem
onClick={(e) => {
e.stopPropagation();
onOpenModal({
defaultVal: item.customTitle || item.title,
onSuccess: (e) =>
onSetCustomTitle({
chatId: item.id,
title: e
})
});
}}
>
<MyIcon mr={2} name={'customTitle'} w={'16px'}></MyIcon>
</MenuItem>
)}
<MenuItem
_hover={{ color: 'red.500' }}
onClick={(e) => {
e.stopPropagation();
onDelHistory(item.id);
if (item.id === activeChatId) {
onChangeChat();
}
}}
>
<MyIcon mr={2} name={'delete'} w={'16px'}></MyIcon>
</MenuItem>
</MenuList>
</Menu>
</Box>
)}
</Flex>
))}
<MyIcon name={'more'} w={'14px'} p={1} />
</MenuButton>
<MenuList color={'myGray.700'} minW={`90px !important`}>
{onSetHistoryTop && (
<MenuItem
onClick={(e) => {
e.stopPropagation();
onSetHistoryTop({ chatId: item.id, top: !item.top });
}}
>
<MyIcon mr={2} name={'setTop'} w={'16px'}></MyIcon>
{item.top ? '取消置顶' : '置顶'}
</MenuItem>
)}
{onSetCustomTitle && (
<MenuItem
onClick={(e) => {
e.stopPropagation();
onOpenModal({
defaultVal: item.customTitle || item.title,
onSuccess: (e) =>
onSetCustomTitle({
chatId: item.id,
title: e
})
});
}}
>
<MyIcon mr={2} name={'customTitle'} w={'16px'}></MyIcon>
</MenuItem>
)}
<MenuItem
_hover={{ color: 'red.500' }}
onClick={(e) => {
e.stopPropagation();
onDelHistory(item.id);
if (item.id === activeChatId) {
onChangeChat();
}
}}
>
<MyIcon mr={2} name={'delete'} w={'16px'}></MyIcon>
</MenuItem>
</MenuList>
</Menu>
</Box>
)}
</Flex>
))}
</>
)}
{currentTab === TabEnum.app && !isPc && (
<>
{myApps.map((item) => (
<Flex
key={item._id}
py={2}
px={3}
mb={3}
borderRadius={'lg'}
alignItems={'center'}
{...(item._id === appId
? {
backgroundColor: 'myBlue.100 !important',
color: 'myBlue.700'
}
: {
onClick: () => {
router.replace({
query: {
appId: item._id
}
});
onClose();
}
})}
>
<Avatar src={item.avatar} w={'24px'} />
<Box ml={2} className={'textEllipsis'}>
{item.name}
</Box>
</Flex>
))}
</>
)}
</Box>
{!isPc && appId && (
@@ -245,11 +323,11 @@ const ChatHistorySlider = ({
borderRadius={'50%'}
aria-label={''}
/>
{t('chat.Exit Chat')}
</Flex>
)}
<EditTitleModal />
<ConfirmChild />
<ConfirmModal />
</Flex>
);
};

View File

@@ -10,7 +10,7 @@ const SliderApps = ({ appId }: { appId: string }) => {
const router = useRouter();
const { myApps, loadMyApps } = useUserStore();
useQuery(['loadModels'], loadMyApps);
useQuery(['loadModels'], () => loadMyApps(false));
return (
<>

View File

@@ -10,7 +10,7 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => {
const router = useRouter();
const menuList = useRef([
{
icon: 'chatLight',
icon: 'chat',
label: '新对话',
onClick: () => {
router.push({

View File

@@ -258,7 +258,13 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
return isPc || !appId ? (
<SideBar>{children}</SideBar>
) : (
<Drawer isOpen={isOpenSlider} placement="left" size={'xs'} onClose={onCloseSlider}>
<Drawer
isOpen={isOpenSlider}
placement="left"
autoFocus={false}
size={'xs'}
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'250px'}>{children}</DrawerContent>
</Drawer>
@@ -269,6 +275,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
appName={chatData.app.name}
appAvatar={chatData.app.avatar}
activeChatId={chatId}
onClose={onCloseSlider}
history={history.map((item, i) => ({
id: item.chatId,
title: item.title,

View File

@@ -151,7 +151,13 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
return isPc ? (
<SideBar>{children}</SideBar>
) : (
<Drawer isOpen={isOpenSlider} placement="left" size={'xs'} onClose={onCloseSlider}>
<Drawer
isOpen={isOpenSlider}
placement="left"
autoFocus={false}
size={'xs'}
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'250px'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
{children}
@@ -167,6 +173,7 @@ const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) =>
id: item.chatId,
title: item.title
}))}
onClose={onCloseSlider}
onChangeChat={(chatId) => {
console.log(chatId);

View File

@@ -69,7 +69,7 @@ const ChunkImport = ({ kbId }: { kbId: string }) => {
return formatPrice(files.reduce((sum, file) => sum + file.tokens, 0) * unitPrice);
}, [files, unitPrice]);
const { openConfirm, ConfirmChild } = useConfirm({
const { openConfirm, ConfirmModal } = useConfirm({
content: `该任务无法终止,需要一定时间生成索引,请确认导入。如果余额不足,未完成的任务会被暂停,充值后可继续进行。`
});
@@ -455,7 +455,7 @@ const ChunkImport = ({ kbId }: { kbId: string }) => {
)}
</Box>
)}
<ConfirmChild />
<ConfirmModal />
</Box>
);
};

View File

@@ -39,7 +39,7 @@ const CsvImport = ({ kbId }: { kbId: string }) => {
);
const emptyFiles = useMemo(() => files.length === 0, [files]);
const { openConfirm, ConfirmChild } = useConfirm({
const { openConfirm, ConfirmModal } = useConfirm({
content: `该任务无法终止,需要一定时间生成索引,请确认导入。如果余额不足,未完成的任务会被暂停,充值后可继续进行。`
});
@@ -234,7 +234,7 @@ const CsvImport = ({ kbId }: { kbId: string }) => {
</Box>
</Box>
)}
<ConfirmChild />
<ConfirmModal />
</Box>
);
};

View File

@@ -71,7 +71,7 @@ const QAImport = ({ kbId }: { kbId: string }) => {
return formatPrice(files.reduce((sum, file) => sum + file.tokens, 0) * unitPrice * 1.3);
}, [files, unitPrice]);
const { openConfirm, ConfirmChild } = useConfirm({
const { openConfirm, ConfirmModal } = useConfirm({
content: `该任务无法终止!导入后会自动调用大模型生成问答对,会有一些细节丢失,请确认!如果余额不足,未完成的任务会被暂停。`
});
@@ -446,7 +446,7 @@ const QAImport = ({ kbId }: { kbId: string }) => {
)}
</Box>
)}
<ConfirmChild />
<ConfirmModal />
</Box>
);
};

View File

@@ -40,7 +40,7 @@ const Info = (
const [btnLoading, setBtnLoading] = useState(false);
const [refresh, setRefresh] = useState(false);
const { openConfirm, ConfirmChild } = useConfirm({
const { openConfirm, ConfirmModal } = useConfirm({
content: '确认删除该知识库?数据将无法恢复,请确认!'
});
@@ -251,7 +251,7 @@ const Info = (
/>
</Flex>
<File onSelect={onSelectFile} />
<ConfirmChild />
<ConfirmModal />
</Box>
);
};

View File

@@ -18,7 +18,7 @@ const Kb = () => {
const theme = useTheme();
const router = useRouter();
const { toast } = useToast();
const { openConfirm, ConfirmChild } = useConfirm({
const { openConfirm, ConfirmModal } = useConfirm({
title: '删除提示',
content: '确认删除该知识库?'
});
@@ -152,7 +152,7 @@ const Kb = () => {
</Box>
</Flex>
)}
<ConfirmChild />
<ConfirmModal />
</PageContainer>
);
};

View File

@@ -19,7 +19,7 @@ type State = {
updateUserInfo: (user: UserUpdateParams) => void;
myApps: AppListItemType[];
myCollectionApps: AppListItemType[];
loadMyApps: () => Promise<AppListItemType[]>;
loadMyApps: (init?: boolean) => Promise<AppListItemType[]>;
appDetail: AppSchema;
loadAppDetail: (id: string, init?: boolean) => Promise<AppSchema>;
updateAppDetail(appId: string, data: AppUpdateParams): Promise<void>;
@@ -63,7 +63,8 @@ export const useUserStore = create<State>()(
},
myApps: [],
myCollectionApps: [],
async loadMyApps() {
async loadMyApps(init = true) {
if (get().myApps.length > 0 && !init) return [];
const res = await getMyModels();
set((state) => {
state.myApps = res;

View File

@@ -1,13 +1,10 @@
import 'i18next';
import common from '../../public/locales/en/common.json';
interface I18nNamespaces {
common: typeof common;
}
declare module 'i18next' {
interface CustomTypeOptions {
defaultNS: 'common';
resources: I18nNamespaces;
interface Resources {
[key: string]: {
[key: string]: string;
};
}
}