mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-03-02 00:30:25 +00:00
feat: 新版webui
This commit is contained in:
132
napcat.webui/src/layouts/default.tsx
Normal file
132
napcat.webui/src/layouts/default.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import { BreadcrumbItem, Breadcrumbs } from '@heroui/breadcrumbs'
|
||||
import { Button } from '@heroui/button'
|
||||
import { useLocalStorage } from '@uidotdev/usehooks'
|
||||
import clsx from 'clsx'
|
||||
import { motion } from 'motion/react'
|
||||
import { useEffect, useMemo, useRef } from 'react'
|
||||
import { ErrorBoundary } from 'react-error-boundary'
|
||||
import { MdMenu, MdMenuOpen } from 'react-icons/md'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import key from '@/const/key'
|
||||
|
||||
import errorFallbackRender from '@/components/error_fallback'
|
||||
// import PageLoading from "@/components/Loading/PageLoading";
|
||||
import SideBar from '@/components/sidebar'
|
||||
|
||||
import useAuth from '@/hooks/auth'
|
||||
|
||||
import type { MenuItem } from '@/config/site'
|
||||
import { siteConfig } from '@/config/site'
|
||||
import QQManager from '@/controllers/qq_manager'
|
||||
|
||||
const menus: MenuItem[] = siteConfig.navItems
|
||||
|
||||
const findTitle = (menus: MenuItem[], pathname: string): string[] => {
|
||||
const paths: string[] = []
|
||||
|
||||
if (pathname) {
|
||||
for (const item of menus) {
|
||||
if (item.href === pathname) {
|
||||
paths.push(item.label)
|
||||
} else if (item.items) {
|
||||
const title = findTitle(item.items, pathname)
|
||||
|
||||
if (title.length > 0) {
|
||||
paths.push(item.label)
|
||||
paths.push(...title)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const location = useLocation()
|
||||
const contentRef = useRef<HTMLDivElement>(null)
|
||||
const [openSideBar, setOpenSideBar] = useLocalStorage(key.sideBarOpen, true)
|
||||
const [b64img] = useLocalStorage(key.backgroundImage, '')
|
||||
const navigate = useNavigate()
|
||||
const { isAuth } = useAuth()
|
||||
const checkIsQQLogin = async () => {
|
||||
try {
|
||||
const result = await QQManager.checkQQLoginStatus()
|
||||
if (!result.isLogin) {
|
||||
if (isAuth) {
|
||||
navigate('/qq_login', { replace: true })
|
||||
} else {
|
||||
navigate('/web_login', { replace: true })
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
navigate('/web_login', { replace: true })
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
contentRef?.current?.scrollTo?.({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}, [location.pathname])
|
||||
useEffect(() => {
|
||||
if (isAuth) {
|
||||
checkIsQQLogin()
|
||||
}
|
||||
}, [isAuth])
|
||||
const title = useMemo(() => {
|
||||
return findTitle(menus, location.pathname)
|
||||
}, [location.pathname])
|
||||
return (
|
||||
<div
|
||||
className="h-screen relative flex bg-danger-50 dark:bg-black"
|
||||
style={{
|
||||
backgroundImage: `url(${b64img})`,
|
||||
backgroundSize: 'cover'
|
||||
}}
|
||||
>
|
||||
<SideBar items={menus} open={openSideBar} />
|
||||
<div
|
||||
ref={contentRef}
|
||||
className={clsx(
|
||||
'overflow-y-auto relative flex-1 rounded-md m-1 bg-content1 pb-10 md:pb-0',
|
||||
openSideBar ? 'ml-0' : 'ml-1',
|
||||
!b64img && 'shadow-inner',
|
||||
b64img && '!bg-opacity-50 backdrop-blur-none dark:bg-background'
|
||||
)}
|
||||
>
|
||||
<div className="h-10 flex items-center hm-medium text-xl sticky top-2 left-0 backdrop-blur-lg z-20 shadow-sm bg-background dark:bg-background shadow-danger-50 dark:shadow-danger-100 m-2 rounded-full !bg-opacity-50">
|
||||
<motion.div
|
||||
className={clsx(
|
||||
'mr-1 ease-in-out ml-0 md:relative',
|
||||
openSideBar && 'pl-2 absolute',
|
||||
'md:!ml-0 md:pl-0'
|
||||
)}
|
||||
transition={{ type: 'spring', stiffness: 150, damping: 15 }}
|
||||
initial={{ marginLeft: 0 }}
|
||||
animate={{ marginLeft: openSideBar ? '15rem' : 0 }}
|
||||
>
|
||||
<Button
|
||||
isIconOnly
|
||||
radius="full"
|
||||
variant="light"
|
||||
onPress={() => setOpenSideBar(!openSideBar)}
|
||||
>
|
||||
{openSideBar ? <MdMenuOpen size={24} /> : <MdMenu size={24} />}
|
||||
</Button>
|
||||
</motion.div>
|
||||
<Breadcrumbs isDisabled size="lg">
|
||||
{title?.map((item, index) => (
|
||||
<BreadcrumbItem key={index}>{item}</BreadcrumbItem>
|
||||
))}
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
<ErrorBoundary fallbackRender={errorFallbackRender}>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
25
napcat.webui/src/layouts/pure.tsx
Normal file
25
napcat.webui/src/layouts/pure.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Link } from '@heroui/link'
|
||||
import { Tooltip } from '@heroui/tooltip'
|
||||
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
|
||||
export default function PureLayout({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div className="relative flex flex-col h-screen">
|
||||
<div className="absolute right-0 top-0 p-2">
|
||||
<Tooltip content="查看WebUI源码" placement="left" showArrow>
|
||||
<Link isExternal href="https://github.com/bietiaop/NextNapCatWebUI">
|
||||
<GithubIcon className="text-default-900 hover:text-default-600 w-10 h-10 hover:drop-shadow-lg transition-all" />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<main className="flex-grow w-full flex flex-col justify-center items-center">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user