Refactor code structure for improved readability and maintainability

This commit is contained in:
2026-02-22 01:54:55 +08:00
parent 1db31d13e6
commit f3c269dd83
27 changed files with 4689 additions and 123 deletions

32
index.html Normal file
View File

@@ -0,0 +1,32 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/jpg" href="https://kevisual.xiongxiao.me/root/center/panda.jpg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Light Code</title>
<style>
html,
body {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#root {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -61,6 +61,6 @@
"tailwindcss": "^4.2.0",
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.3",
"vite": "v8.0.0-beta.14"
"vite": "v7.3.1"
}
}

4532
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -182,7 +182,7 @@ export const List = () => {
<div className="p-4 w-full h-full">
<div className="flex mb-4">
<Dialog>
<DialogTrigger asChild>
<DialogTrigger>
<Button
onClick={() => {
setShowEditModal(true);

View File

@@ -1,21 +1,27 @@
export const isDev = process.env.NODE_ENV === "development";
const BASE_NAME = isDev ? '' : '/root/center';
// @ts-ignore
export const basename = BASE_NAME;
export const wrapBasename = (path: string) => {
const hasEnd = path.endsWith('/')
let _basename = basename;
if (basename) {
_basename = `${basename}${path}`;
return `${basename}${path}` + (hasEnd ? '' : '/');
} else {
_basename = path;
return path;
}
if (isDev) {
return _basename
}
return !hasEnd ? _basename + '/' : _basename;
}
// 动态计算 basename根据当前 URL 路径
export const getDynamicBasename = (): string => {
const path = window.location.pathname
const [user, key, id] = path.split('/').filter(Boolean)
if (key === 'v1' && id) {
return `/${user}/v1/${id}`
}
// 默认使用构建时的 basename
return basename
}
export const openLink = (path: string, target: string = '_self') => {
if (path.startsWith('http://') || path.startsWith('https://')) {
window.open(path, target);

View File

@@ -102,9 +102,9 @@ export const LayoutUser = () => {
<div className='flex gap-4'>
{items.length > 0 && (
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<DropdownMenuTrigger>
<Button variant='ghost' size='icon'>
<Users />
</Button>

View File

@@ -23,7 +23,7 @@ export const useQuickMenu = () => {
{
title: '应用',
icon: <AppstoreOutlined />,
link: '/apps/',
link: '/apps',
},
// {
// title: '文件',

View File

@@ -1,30 +1,27 @@
'use client';
import { MenuOutlined, SwapOutlined } from '@ant-design/icons';
import { LayoutMenu, useQuickMenu } from './Menu';
import { useLayoutStore, usePlatformStore } from './store';
import { useShallow } from 'zustand/react/shallow';
import { useEffect, useLayoutEffect, useState } from 'react';
import { usePathname } from 'next/navigation';
import { LayoutUser } from './LayoutUser';
import PandaPNG from '@/assets/panda.jpg';
import QRCodePNG from '@/assets/qrcode-8x8.jpg';
import clsx from 'clsx';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { useNavigate, useLocation } from '@tanstack/react-router';
import { QrCode } from 'lucide-react';
export const IconButton = (props: any) => {
return (
<button
<div
className={clsx(
'inline-flex items-center justify-center rounded-md p-2 transition-colors hover:bg-slate-100 disabled:opacity-50 disabled:pointer-events-none',
props.className,
)}
{...props}>
{props.children}
</button>
</div>
);
};
import { QrCode } from 'lucide-react';
import { openLink } from '../basename';
type LayoutMainProps = {
title?: React.ReactNode;
@@ -43,6 +40,8 @@ export const LayoutMain = (props: LayoutMainProps) => {
};
}),
);
const navigate = useNavigate();
const location = useLocation();
const platformStore = usePlatformStore(
useShallow((state) => {
return {
@@ -55,7 +54,6 @@ export const LayoutMain = (props: LayoutMainProps) => {
);
const { isMac, mount, isElectron } = platformStore;
const quickMenu = useQuickMenu();
const pathname = usePathname();
useLayoutEffect(() => {
platformStore.init();
@@ -66,9 +64,9 @@ export const LayoutMain = (props: LayoutMainProps) => {
}, []);
return (
<div className='flex w-full h-full flex-col relative'>
<div className='flex gap-2 text-lg w-full relative'>
<div
className={clsx('layout-menu items-center ', !mount && '!invisible')}
className={clsx('layout-menu items-center w-full', !mount && 'invisible!')}
style={{
cursor: isElectron ? 'move' : 'default',
}}>
@@ -77,16 +75,18 @@ export const LayoutMain = (props: LayoutMainProps) => {
<div className='text-xl font-bold '>{props.title}</div>
<div className='flex items-center gap-2 text-sm '>
{quickMenu.map((item, index) => {
const isActive = pathname === item.link;
const isActive = location.pathname === item.link;
console.log('isActive', location, item.link, isActive);
return (
<div
key={index}
className={clsx('flex items-center gap-2 px-1', isActive && 'border border-white')}
onClick={() => {
openLink(item.link, '_self');
console.log('navigate to', item.link);
navigate({ to: item.link })
}}>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger >
<div className='cursor-pointer'>{item.icon}</div>
</TooltipTrigger>
<TooltipContent>{item.title}</TooltipContent>
@@ -103,7 +103,7 @@ export const LayoutMain = (props: LayoutMainProps) => {
</IconButton>
<div className='absolute hidden group-hover:flex bg-white p-2 border shadow-md top-10 -left-15 w-40 z-[9999] flex-col items-center justify-center rounded-md'>
<img src={QRCodePNG.src} alt='QR Code' />
<img src={QRCodePNG} alt='QR Code' />
<div className='text-sm text-black'></div>
</div>
</div>
@@ -111,7 +111,7 @@ export const LayoutMain = (props: LayoutMainProps) => {
{menuStore.me?.type === 'org' && (
<div>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<IconButton
onClick={() => {
menuStore.switchOrg('', 'user');
@@ -127,7 +127,7 @@ export const LayoutMain = (props: LayoutMainProps) => {
{menuStore.me?.avatar ? (
<img className='w-8 h-8 rounded-full' src={menuStore.me?.avatar} alt='avatar' />
) : (
<img className='w-8 h-8 rounded-full' src={PandaPNG.src} alt='avatar' />
<img className='w-8 h-8 rounded-full' src={PandaPNG} alt='avatar' />
)}
</div>
<div className='cursor-pointer' onClick={() => menuStore.setOpenUser(true)}>
@@ -135,13 +135,6 @@ export const LayoutMain = (props: LayoutMainProps) => {
</div>
</div>
</div>
<div
className='flex'
style={{
height: 'calc(100vh - 3rem)',
}}>
{props.children}
</div>
</div>
<LayoutUser />
</div>

View File

@@ -1,32 +1,14 @@
'use client';
import { QueryClient } from '@kevisual/query';
import { QueryLoginBrowser } from '@kevisual/api/login';
import { toast } from 'sonner';
import { Query } from '@kevisual/query';
import { QueryLoginBrowser } from '@kevisual/api/query-login'
import { useContextKey } from '@kevisual/context';
export const query = useContextKey('query', new Query({
url: '/api/router',
}));
// Only create instances in browser environment
const isBrowser = typeof window !== 'undefined';
export const queryClient = useContextKey('queryClient', new Query({
url: '/client/router',
}));
export const query = isBrowser ? new QueryClient({}) : {} as QueryClient;
export const queryLogin = isBrowser
? new QueryLoginBrowser({
query: query as any,
})
: {} as QueryLoginBrowser;
if (isBrowser) {
(query as any).afterResponse = async (res, ctx) => {
const newRes = await queryLogin.run401Action(res, ctx, {
afterAlso401: () => {},
afterCheck: (res: any) => {
if (res.code === 200) {
toast.success('刷新登陆信息');
setTimeout(() => {
window.location.reload();
}, 2000);
}
},
});
return newRes as any;
};
}
export const queryLogin = useContextKey('queryLogin', new QueryLoginBrowser({
query: query
}));

View File

@@ -19,7 +19,7 @@ export const AIEditorLink = (props: Props) => {
);
return (
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'

View File

@@ -147,7 +147,7 @@ export const AppVersionList = () => {
<div className='w-full h-full flex bg-slate-100'>
<div className='p-2 bg-white'>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -165,7 +165,7 @@ export const AppVersionList = () => {
<div className='grow h-full relative'>
<div className='absolute top-2 left-4'>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -193,7 +193,7 @@ export const AppVersionList = () => {
<span className='font-medium'>{item.version}</span>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<div className={clsx('rounded-full w-4 h-4', color)}></div>
</TooltipTrigger>
<TooltipContent>{isPublish ? 'published' : ''}</TooltipContent>
@@ -201,7 +201,7 @@ export const AppVersionList = () => {
</div>
<div className='mt-4 flex gap-1'>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -215,7 +215,7 @@ export const AppVersionList = () => {
<TooltipContent>Delete</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -228,7 +228,7 @@ export const AppVersionList = () => {
<TooltipContent>使</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -248,7 +248,7 @@ export const AppVersionList = () => {
</Tooltip>
<AIEditorLink pathname={item.key + '/' + item.version} />
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -274,7 +274,7 @@ export const AppVersionList = () => {
<div className='bg-white p-2 w-[600px] h-full flex flex-col'>
<div className='header flex items-center gap-2'>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -336,7 +336,7 @@ export const AppVersionFile = () => {
Files
<FileUpload />
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button variant='outline' size='sm' onClick={onDetect}>
</Button>

View File

@@ -31,7 +31,7 @@ export function DatePicker({ className, value, onChange }: DatePickerProps) {
return (
<Popover>
<PopoverTrigger asChild>
<PopoverTrigger>
<Button
variant={"outline"}
className={cn(

View File

@@ -27,7 +27,7 @@ const LabelWithTooltip = ({ label, tips }: { label: string; tips?: string }) =>
<label className="text-sm font-medium">{label}</label>
{tips && (
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button variant="ghost" size="icon" className="h-4 w-4 p-0">
<HelpCircle size={16} />
</Button>

View File

@@ -209,8 +209,10 @@ const ShareModal = () => {
<Label></Label>
<Select
value={runtime[0] || ''}
onValueChange={(val: string) => {
setRuntime((prev) => (prev.includes(val) ? prev.filter((v) => v !== val) : [...prev, val]));
onValueChange={(val) => {
if (typeof val === 'string') {
setRuntime((prev) => (prev.includes(val) ? prev.filter((v) => v !== val) : [...prev, val]));
}
}}
>
<SelectTrigger>
@@ -278,7 +280,7 @@ export const List = () => {
<div className='w-full h-full flex bg-slate-100'>
<div className='p-2 h-full bg-white flex flex-col gap-2'>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<IconButton
sx={{
padding: '8px',
@@ -293,7 +295,7 @@ export const List = () => {
<TooltipContent></TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<IconButton
sx={{
padding: '8px',
@@ -320,7 +322,7 @@ export const List = () => {
<div className='w-[300px] bg-white rounded-lg border border-slate-200 shadow-sm p-4 relative' key={item.id}>
<div className='flex font-bold justify-between mb-3' onClick={() => { }}>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<div>
{item.title} <i className='text-xs text-gray-400'>{item.key}</i>
</div>
@@ -332,7 +334,7 @@ export const List = () => {
</Tooltip>
<div>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<div className={`${isRunning ? 'bg-green-500' : 'bg-red-500'} w-4 h-4 rounded-full`}></div>
</TooltipTrigger>
<TooltipContent>{isRunning ? '网页可正常访问' : '网页被关闭'}</TooltipContent>
@@ -341,7 +343,7 @@ export const List = () => {
</div>
<div className='flex flex-col gap-2 mb-16'>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<div
className='text-xs cursor-copy'
onClick={() => {
@@ -364,7 +366,7 @@ export const List = () => {
</div>
<div className='mt-4 pt-3 border-t border-slate-100 flex gap-1 absolute bottom-0 left-0 right-0 px-4 pb-4 bg-white rounded-b-lg'>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -379,7 +381,7 @@ export const List = () => {
<TooltipContent></TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -395,7 +397,7 @@ export const List = () => {
<TooltipContent></TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -410,7 +412,7 @@ export const List = () => {
<TooltipContent className="whitespace-pre-wrap">{iText.share.tips}</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -423,7 +425,7 @@ export const List = () => {
<TooltipContent></TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -458,7 +460,7 @@ export const List = () => {
</Tooltip>
<AIEditorLink pathname={item.key} />
<Tooltip>
<TooltipTrigger asChild>
<TooltipTrigger>
<Button
variant='ghost'
size='icon'
@@ -487,8 +489,4 @@ export const List = () => {
);
};
export default () => {
return <LayoutMain>
<List />
</LayoutMain>
};
export default List;

View File

@@ -89,7 +89,7 @@ export function Autocomplate({
className={cn("flex-1 font-mono", className)}
/>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<PopoverTrigger>
<Button
variant="outline"
role="combobox"

View File

@@ -89,7 +89,7 @@ const TableList = () => {
</Button>
<Popover>
<PopoverTrigger asChild>
<PopoverTrigger>
<Button
variant="destructive"
size="sm">

View File

@@ -182,7 +182,7 @@ export const List = () => {
<div className="p-4 w-full h-full">
<div className="flex mb-4">
<Dialog>
<DialogTrigger asChild>
<DialogTrigger>
<Button
onClick={() => {
setShowEditModal(true);

View File

@@ -164,7 +164,7 @@ export const List = () => {
<div className="p-4 w-full h-full">
<div className="flex mb-4">
<Dialog>
<DialogTrigger asChild>
<DialogTrigger>
<Button
onClick={() => {
setShowEdit(true);

View File

@@ -173,7 +173,7 @@ export const List = () => {
<div className="p-4 w-full h-full">
<div className="flex mb-4">
<Dialog>
<DialogTrigger asChild>
<DialogTrigger>
<Button
onClick={() => {
setShowEdit(true);

View File

@@ -102,7 +102,7 @@ export const UserDrawer = ({ open, onOpenChange }: UserDrawerProps) => {
<DrawerContent className="h-full !max-w-xl ml-auto">
<DrawerHeader className="flex flex-row items-center justify-between">
<DrawerTitle></DrawerTitle>
<DrawerClose asChild>
<DrawerClose>
<Button variant="ghost" size="icon-sm">
<X className="w-4 h-4" />
</Button>

View File

@@ -74,7 +74,7 @@ const TableList = () => {
</Button>
<Popover>
<PopoverTrigger asChild>
<PopoverTrigger>
<Button
variant="destructive"
size="sm">

View File

@@ -1,13 +1,9 @@
import { LayoutMain } from "@/modules/layout";
export default function Home() {
return (
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<LayoutMain>
<iframe src="/root/router-studio" className="w-full border-0" style={{
height: 'calc(100vh - 48px)'
}}/>
</LayoutMain>
<iframe src="/root/router-studio" className="w-full border-0" style={{
height: 'calc(100vh - 48px)'
}} />
</div>
);
}

View File

@@ -166,7 +166,7 @@ const FormModal = () => {
defaultValue=""
render={({ field }) => (
<Popover>
<PopoverTrigger asChild>
<PopoverTrigger>
<Button
variant="outline"
className={cn(

View File

@@ -70,7 +70,7 @@ const TableList = () => {
open={openPopover === user.id}
onOpenChange={(open) => setOpenPopover(open ? user.id : null)}
>
<PopoverTrigger asChild>
<PopoverTrigger>
<Button
variant="destructive"
size="sm"
@@ -221,6 +221,4 @@ export const List = () => {
);
};
export default () => {
return <LayoutMain><List /></LayoutMain>;
}
export default <List />

View File

@@ -9,8 +9,14 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as AppsRouteImport } from './routes/apps'
import { Route as IndexRouteImport } from './routes/index'
const AppsRoute = AppsRouteImport.update({
id: '/apps',
path: '/apps',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
@@ -19,28 +25,39 @@ const IndexRoute = IndexRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/apps': typeof AppsRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/apps': typeof AppsRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/apps': typeof AppsRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/'
fullPaths: '/' | '/apps'
fileRoutesByTo: FileRoutesByTo
to: '/'
id: '__root__' | '/'
to: '/' | '/apps'
id: '__root__' | '/' | '/apps'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AppsRoute: typeof AppsRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/apps': {
id: '/apps'
path: '/apps'
fullPath: '/apps'
preLoaderRoute: typeof AppsRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
@@ -53,6 +70,7 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AppsRoute: AppsRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)

View File

@@ -3,14 +3,15 @@ import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import { Toaster } from '@/components/ui/sonner'
import { AuthProvider } from '@/pages/auth'
import { TooltipProvider } from '@/components/ui/tooltip'
import { LayoutMain } from '@/modules/layout'
export const Route = createRootRoute({
component: RootComponent,
})
function RootComponent() {
return (
<div className='h-full overflow-hidden'>
<div className='h-full w-full overflow-hidden'>
{/*
<div className="p-2 flex gap-2 text-lg">
<Link
to="/"
@@ -22,7 +23,8 @@ function RootComponent() {
Home
</Link>
</div>
<hr />
<hr /> */}
<LayoutMain />
<AuthProvider>
<TooltipProvider>
<main className='h-[calc(100%-4rem)] overflow-auto scrollbar'>

9
src/routes/apps.tsx Normal file
View File

@@ -0,0 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import App from '@/pages/apps/page'
export const Route = createFileRoute('/apps')({
component: RouteComponent,
})
function RouteComponent() {
return <App />;
}