feat: 更新版本号,添加登录功能和路由支持

This commit is contained in:
2026-02-24 18:17:34 +08:00
parent dd8d5b7341
commit 01ff3dde14
10 changed files with 228 additions and 68 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "cli-center",
"private": true,
"version": "0.0.1",
"version": "0.0.2",
"type": "module",
"basename": "/root/cli-center",
"scripts": {
@@ -9,7 +9,7 @@
"build": "vite build",
"preview": "vite preview",
"ui": "bunx shadcn@latest add ",
"pub": "envision deploy ./dist -k cli-center -v 0.0.1 -y y -u"
"pub": "envision deploy ./dist -k cli-center -v 0.0.2 -y y -u"
},
"files": [
"dist"
@@ -18,10 +18,11 @@
"license": "MIT",
"dependencies": {
"@base-ui/react": "^1.2.0",
"@kevisual/api": "^0.0.59",
"@kevisual/api": "^0.0.60",
"@kevisual/context": "^0.0.8",
"@kevisual/kv-login": "^0.1.15",
"@kevisual/router": "0.0.84",
"@tanstack/react-router": "^1.162.6",
"@tanstack/react-router": "^1.162.8",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
@@ -45,8 +46,8 @@
"@kevisual/query": "0.0.49",
"@kevisual/types": "^0.0.12",
"@tailwindcss/vite": "^4.2.1",
"@tanstack/react-router-devtools": "^1.162.6",
"@tanstack/router-plugin": "^1.162.6",
"@tanstack/react-router-devtools": "^1.162.8",
"@tanstack/router-plugin": "^1.162.8",
"@types/node": "^25.3.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",

56
pnpm-lock.yaml generated
View File

@@ -12,17 +12,20 @@ importers:
specifier: ^1.2.0
version: 1.2.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@kevisual/api':
specifier: ^0.0.59
version: 0.0.59(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))
specifier: ^0.0.60
version: 0.0.60(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))
'@kevisual/context':
specifier: ^0.0.8
version: 0.0.8
'@kevisual/kv-login':
specifier: ^0.1.15
version: 0.1.15
'@kevisual/router':
specifier: 0.0.84
version: 0.0.84
'@tanstack/react-router':
specifier: ^1.162.6
version: 1.162.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
specifier: ^1.162.8
version: 1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -79,11 +82,11 @@ importers:
specifier: ^4.2.1
version: 4.2.1(vite@8.0.0-beta.14(@types/node@25.3.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))
'@tanstack/react-router-devtools':
specifier: ^1.162.6
version: 1.162.6(@tanstack/react-router@1.162.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.162.6)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
specifier: ^1.162.8
version: 1.162.8(@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.162.6)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/router-plugin':
specifier: ^1.162.6
version: 1.162.6(@tanstack/react-router@1.162.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0-beta.14(@types/node@25.3.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))
specifier: ^1.162.8
version: 1.162.8(@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0-beta.14(@types/node@25.3.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))
'@types/node':
specifier: ^25.3.0
version: 25.3.0
@@ -433,8 +436,8 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@kevisual/api@0.0.59':
resolution: {integrity: sha512-2w6GBG2mS92dz8afB0hLfjTw8lBGD5oXU5bw/QglFugHHp24fISZkEW1Hc+jP/jOiYlMzphws2/31DIXdDoGkg==}
'@kevisual/api@0.0.60':
resolution: {integrity: sha512-NTFDx1ns/iGli2fUJLJZRWu8nf5VkXV+sOQUqGGAJvrvGATvXSuITu6mD4P/aDQakx4hzQUPr9wDTZoNk7+RqQ==}
'@kevisual/cache@0.0.5':
resolution: {integrity: sha512-fgtUYGUUq/DY0KFV4CkWszNqvQUaA8XvMTUjoR9ZXRpau5IIDolD/Wen2TFsZ7G3Rfy+lef5dnaiZVDkZwdVKg==}
@@ -445,6 +448,9 @@ packages:
'@kevisual/js-filter@0.0.5':
resolution: {integrity: sha512-+S+Sf3K/aP6XtZI2s7TgKOr35UuvUvtpJ9YDW30a+mY0/N8gRuzyKhieBzQN7Ykayzz70uoMavBXut2rUlLgzw==}
'@kevisual/kv-login@0.1.15':
resolution: {integrity: sha512-PqFvhi11/zAIqdEePsINzhIX9QYf25nmvsIoT5kD8NB5663ZDsSPdgBiT/Jn3U+jx50a43ndNFwOTNuZkdp0Iw==}
'@kevisual/load@0.0.6':
resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==}
@@ -853,11 +859,11 @@ packages:
resolution: {integrity: sha512-Kp/WSt411ZWYvgXy6uiv5RmhHrz9cAml05AQPrtdAp7eUqvIDbMGPnML25OKbzR3RJ1q4wgENxDTvlGPa9+Mww==}
engines: {node: '>=20.19'}
'@tanstack/react-router-devtools@1.162.6':
resolution: {integrity: sha512-oSUmF5IrBUc67apoQlJ1lvIRD0EalXuAmfY9GIzW0x10BrdV/ecgCudT4Mo0U/mdXQuF4BHg4Et6MMIvuvdtaA==}
'@tanstack/react-router-devtools@1.162.8':
resolution: {integrity: sha512-dDohOU8eNbCukLQNcuocCTnvwSu8Z1XwbKvPc4U7KDYoUTUlJls48fXl5y/ENThK/nZEsA7i3oCy1BcX42OOlw==}
engines: {node: '>=20.19'}
peerDependencies:
'@tanstack/react-router': ^1.162.6
'@tanstack/react-router': ^1.162.8
'@tanstack/router-core': ^1.162.6
react: '>=18.0.0 || >=19.0.0'
react-dom: '>=18.0.0 || >=19.0.0'
@@ -865,8 +871,8 @@ packages:
'@tanstack/router-core':
optional: true
'@tanstack/react-router@1.162.6':
resolution: {integrity: sha512-aj/gQ+TrODVjQYG5spXAlJTd4ZGaqUuRG/CJaQn8mMdc7h7NrATCnxDOugz99WPOl0bzMYQum7cTEhjCe2zOgA==}
'@tanstack/react-router@1.162.8':
resolution: {integrity: sha512-WunoknGI5ielJ833yl/F7Vq4nv/OWzrJVBsMgyxX16Db1DwVvX/B5zTg8EMjdZUOJ7ONpvur3t4aq7KQiYRagQ==}
engines: {node: '>=20.19'}
peerDependencies:
react: '>=18.0.0 || >=19.0.0'
@@ -896,12 +902,12 @@ packages:
resolution: {integrity: sha512-mzkD3kfPW50xgX1hI8YrQx76+hshsUmpI9fVvS741L0cRQKH7bCIYTvcNHkz3sftZwmjt/lh+k7arV1AMLaWhA==}
engines: {node: '>=20.19'}
'@tanstack/router-plugin@1.162.6':
resolution: {integrity: sha512-4Q+MtwHqqCawazM6I3NG6wVFDJdBfJ4uJYggUzGY0ir2bgbOULvvAlDD2tBHEOIoNhQwLcnr2AQ0JQJSWl8iZA==}
'@tanstack/router-plugin@1.162.8':
resolution: {integrity: sha512-u6ZqYEjIA8jXge6JSl5UFFYPzVRciee0vwDwtkIF1Sb+G4cDdDaEjYQ4aN1/va8D7n3LptYvSMU8SeGkX+9slA==}
engines: {node: '>=20.19'}
peerDependencies:
'@rsbuild/core': '>=1.0.2'
'@tanstack/react-router': ^1.162.6
'@tanstack/react-router': ^1.162.8
vite: '>=5.0.0 || >=6.0.0 || >=7.0.0'
vite-plugin-solid: ^2.11.10
webpack: '>=5.92.0'
@@ -1854,7 +1860,7 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@kevisual/api@0.0.59(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))':
'@kevisual/api@0.0.60(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))':
dependencies:
'@kevisual/context': 0.0.8
'@kevisual/js-filter': 0.0.5
@@ -1885,6 +1891,8 @@ snapshots:
'@kevisual/js-filter@0.0.5': {}
'@kevisual/kv-login@0.1.15': {}
'@kevisual/load@0.0.6':
dependencies:
eventemitter3: 5.0.4
@@ -2190,9 +2198,9 @@ snapshots:
'@tanstack/history@1.161.4': {}
'@tanstack/react-router-devtools@1.162.6(@tanstack/react-router@1.162.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.162.6)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@tanstack/react-router-devtools@1.162.8(@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.162.6)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@tanstack/react-router': 1.162.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/react-router': 1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/router-devtools-core': 1.162.6(@tanstack/router-core@1.162.6)(csstype@3.2.3)
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
@@ -2201,7 +2209,7 @@ snapshots:
transitivePeerDependencies:
- csstype
'@tanstack/react-router@1.162.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@tanstack/history': 1.161.4
'@tanstack/react-store': 0.9.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -2251,7 +2259,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@tanstack/router-plugin@1.162.6(@tanstack/react-router@1.162.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0-beta.14(@types/node@25.3.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))':
'@tanstack/router-plugin@1.162.8(@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0-beta.14(@types/node@25.3.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0))':
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
@@ -2267,7 +2275,7 @@ snapshots:
unplugin: 2.3.11
zod: 3.25.76
optionalDependencies:
'@tanstack/react-router': 1.162.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/react-router': 1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
vite: 8.0.0-beta.14(@types/node@25.3.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)
transitivePeerDependencies:
- supports-color

View File

@@ -1,5 +1,5 @@
import { Query, DataOpts } from '@kevisual/query';
import { QueryLoginBrowser } from '@kevisual/api/login'
import { QueryLoginBrowser } from '@kevisual/api/query-login'
import { useContextKey } from '@kevisual/context';
export const query = useContextKey('query', new Query({
url: '/api/router',

View File

@@ -2,6 +2,10 @@ import { useEffect } from "react"
import { useLayoutStore } from "./store"
import { useShallow } from "zustand/shallow"
import { LogIn, LockKeyhole } from "lucide-react"
export { BaseHeader } from './modules/BaseHeader'
import { useMemo } from 'react';
import { useLocation, useNavigate } from '@tanstack/react-router';
type Props = {
children?: React.ReactNode,
@@ -11,12 +15,18 @@ export const AuthProvider = ({ children, mustLogin }: Props) => {
const store = useLayoutStore(useShallow(state => ({
init: state.init,
me: state.me,
openLinkList: state.openLinkList,
})));
useEffect(() => {
store.init()
}, [])
const location = useLocation()
const navigate = useNavigate();
const isOpen = useMemo(() => {
return store.openLinkList.some(item => location.pathname.startsWith(item))
}, [location.pathname])
const loginUrl = '/root/login/?redirect=' + encodeURIComponent(window.location.href);
if (mustLogin && !store.me) {
if (mustLogin && !store.me && !isOpen) {
return (
<div className="w-full h-full min-h-screen flex items-center justify-center bg-background">
<div className="flex flex-col items-center gap-6 p-10 rounded-2xl border border-border bg-card shadow-lg max-w-sm w-full mx-4">
@@ -30,7 +40,8 @@ export const AuthProvider = ({ children, mustLogin }: Props) => {
<div
className="inline-flex items-center justify-center gap-2 w-full px-6 py-2.5 rounded-lg bg-foreground text-background text-sm font-medium transition-opacity hover:opacity-80 active:opacity-70"
onClick={() => {
window.open(loginUrl, '_self')
// window.open(loginUrl, '_blank');
navigate({ to: '/login' });
}}
>
<LogIn className="w-4 h-4" />

View File

@@ -0,0 +1,75 @@
import { Home, User, LogIn, LogOut } from 'lucide-react';
import { Link } from '@tanstack/react-router'
import { useLayoutStore } from '../store';
import { useShallow } from 'zustand/shallow';
import { useMemo } from 'react';
export const BaseHeader = (props: { main?: React.ComponentType | null }) => {
const store = useLayoutStore(useShallow(state => ({
me: state.me,
clearMe: state.clearMe,
})));
const meInfo = useMemo(() => {
if (!store.me) {
return (
<button
onClick={() => window.open('/root/login/?redirect=' + encodeURIComponent(window.location.href), '_self')}
className="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors cursor-pointer"
>
<LogIn className="w-4 h-4" />
<span></span>
</button>
)
}
return (
<div className="flex items-center gap-3">
{store.me.avatar && (
<img
src={store.me.avatar}
alt="Avatar"
className="w-8 h-8 rounded-full object-cover"
/>
)}
{!store.me.avatar && (
<div className="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<User className="w-4 h-4 text-gray-500" />
</div>
)}
<span className="font-medium text-gray-700">{store.me?.username}</span>
<button
onClick={() => store.clearMe?.()}
className="flex items-center gap-1 px-2 py-1 text-sm text-gray-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors cursor-pointer"
title="退出登录"
>
<LogOut className="w-4 h-4" />
</button>
</div>
)
}, [store.me, store.clearMe])
return (
<>
<div className="flex gap-2 text-lg w-full h-12 items-center justify-between">
<div className='px-2'>
<Link
to="/"
activeProps={{
className: 'font-bold',
}}
activeOptions={{ exact: true }}
>
<Home className='w-5 h-5' />
</Link>
</div>
<div className='mr-4'>
{meInfo}
</div>
</div>
<hr />
</>
)
}
export const LayoutMain = () => {
return <BaseHeader />
}

49
src/pages/auth/page.tsx Normal file
View File

@@ -0,0 +1,49 @@
import { useContextKey } from '@kevisual/context';
import '@kevisual/kv-login';
import { checkPluginLogin } from '@kevisual/kv-login'
import { useEffect } from 'react';
import { useLayoutStore } from './store';
import { useShallow } from 'zustand/shallow';
import { useNavigate } from '@tanstack/react-router';
export const LoginComponent = ({ onLoginSuccess }: { onLoginSuccess: () => void }) => {
useEffect(() => {
// 监听登录成功事件
const handleLoginSuccess = () => {
console.log('监听到登录成功事件,关闭弹窗');
onLoginSuccess();
};
const loginEmitter = useContextKey('login-emitter')
console.log('KvLogin Types:', loginEmitter);
loginEmitter.on('login-success', handleLoginSuccess);
// 清理监听器
return () => {
loginEmitter.off('login-success', handleLoginSuccess);
};
}, [onLoginSuccess]);
// @ts-ignore
return (<kv-login></kv-login>)
}
export const App = () => {
const store = useLayoutStore(useShallow((state) => ({
init: state.init
})));
useEffect(() => {
checkPluginLogin();
}, []);
const navigate = useNavigate();
const handleLoginSuccess = async () => {
await store.init()
navigate({ to: '/' })
};
return <div className='w-full h-full'>
<div className='w-md mx-auto flex mt-20'>
<LoginComponent onLoginSuccess={handleLoginSuccess} />
</div>
</div>
}
export default App;

View File

@@ -19,11 +19,14 @@ export type LayoutStore = {
setOpenUser: (openUser: boolean) => void;
me?: UserInfo;
setMe: (me: UserInfo) => void;
clearMe: () => void;
getMe: () => Promise<void>;
switchOrg: (username?: string) => Promise<void>;
isAdmin: boolean;
setIsAdmin: (isAdmin: boolean) => void
init: () => Promise<void>;
openLinkList: string[];
setOpenLinkList: (openLinkList: string[]) => void;
};
export const useLayoutStore = create<LayoutStore>((set, get) => ({
open: false,
@@ -32,6 +35,10 @@ export const useLayoutStore = create<LayoutStore>((set, get) => ({
setOpenUser: (openUser) => set({ openUser }),
me: undefined,
setMe: (me) => set({ me }),
clearMe: () => {
set({ me: undefined, isAdmin: false });
window.location.href = '/root/login/?redirect=' + encodeURIComponent(window.location.href);
},
getMe: async () => {
const res = await queryLogin.getMe();
if (res.code === 200) {
@@ -53,13 +60,20 @@ export const useLayoutStore = create<LayoutStore>((set, get) => ({
isAdmin: false,
setIsAdmin: (isAdmin) => set({ isAdmin }),
init: async () => {
const token = await queryLogin.checkTokenValid()
const token = await queryLogin.getToken();
if (token) {
const user = await queryLogin.checkLocalUser() as UserInfo;
set({ me: {} })
const me = await queryLogin.getMe();
// const user = await queryLogin.checkLocalUser() as UserInfo;
const user = me.code === 200 ? me.data : undefined;
if (user) {
set({ me: user });
set({ isAdmin: user.orgs?.includes?.('admin') || false });
} else {
set({ me: undefined, isAdmin: false });
}
}
}
},
openLinkList: ['/login'],
setOpenLinkList: (openLinkList) => set({ openLinkList }),
}));

View File

@@ -9,10 +9,16 @@
// 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 LoginRouteImport } from './routes/login'
import { Route as DemoRouteImport } from './routes/demo'
import { Route as CnbBoardRouteImport } from './routes/cnb-board'
import { Route as IndexRouteImport } from './routes/index'
const LoginRoute = LoginRouteImport.update({
id: '/login',
path: '/login',
getParentRoute: () => rootRouteImport,
} as any)
const DemoRoute = DemoRouteImport.update({
id: '/demo',
path: '/demo',
@@ -33,34 +39,45 @@ export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/cnb-board': typeof CnbBoardRoute
'/demo': typeof DemoRoute
'/login': typeof LoginRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/cnb-board': typeof CnbBoardRoute
'/demo': typeof DemoRoute
'/login': typeof LoginRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/cnb-board': typeof CnbBoardRoute
'/demo': typeof DemoRoute
'/login': typeof LoginRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/cnb-board' | '/demo'
fullPaths: '/' | '/cnb-board' | '/demo' | '/login'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/cnb-board' | '/demo'
id: '__root__' | '/' | '/cnb-board' | '/demo'
to: '/' | '/cnb-board' | '/demo' | '/login'
id: '__root__' | '/' | '/cnb-board' | '/demo' | '/login'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
CnbBoardRoute: typeof CnbBoardRoute
DemoRoute: typeof DemoRoute
LoginRoute: typeof LoginRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/login': {
id: '/login'
path: '/login'
fullPath: '/login'
preLoaderRoute: typeof LoginRouteImport
parentRoute: typeof rootRouteImport
}
'/demo': {
id: '/demo'
path: '/demo'
@@ -89,6 +106,7 @@ const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
CnbBoardRoute: CnbBoardRoute,
DemoRoute: DemoRoute,
LoginRoute: LoginRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)

View File

@@ -1,43 +1,18 @@
// import { LayoutMain } from '@/modules/layout'
const LayoutMain = null;
import { Link, Outlet, createRootRoute } from '@tanstack/react-router'
import { LayoutMain } from '@/pages/auth/modules/BaseHeader';
import { Outlet, createRootRoute } from '@tanstack/react-router'
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 { Home } from 'lucide-react';
export const Route = createRootRoute({
component: RootComponent,
})
const BaseHeader = (props: { main?: React.ComponentType | null }) => {
if (props.main) {
const MainComponent = props.main
return <MainComponent />
}
return (
<>
<div className="flex gap-2 text-lg w-full h-12 items-center">
<div className='px-2'>
<Link
to="/"
activeProps={{
className: 'font-bold',
}}
activeOptions={{ exact: true }}
>
<Home className='w-5 h-5' />
</Link>
</div>
</div>
<hr />
</>
)
}
function RootComponent() {
return (
<div className='h-full overflow-hidden'>
<BaseHeader main={LayoutMain} />
<LayoutMain />
<AuthProvider mustLogin={true}>
<TooltipProvider>
<main className='h-[calc(100%-3rem)] overflow-auto scrollbar'>

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

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