Skip to content

Commit ec7504e

Browse files
committed
fix: 布局、组件调整及优化
1 parent 1aad7c6 commit ec7504e

24 files changed

+494
-194
lines changed

app/(admin)/admin/page.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Admin() {
2+
return (
3+
<>
4+
控制台
5+
</>
6+
)
7+
}

app/(admin)/layout.tsx

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { Metadata } from 'next'
2+
import '~/style/globals.css'
3+
import { Providers } from '~/app/providers/providers'
4+
import Header from '~/components/Header'
5+
import { UserStoreProvider } from '~/app/providers/userStoreProvider'
6+
import { Toaster } from 'sonner'
7+
8+
export const metadata: Metadata = {
9+
title: "PicImpact",
10+
description: "开发中...",
11+
icons: {
12+
icon: "/favicon.ico",
13+
},
14+
};
15+
16+
export default function RootLayout({
17+
children,
18+
}: Readonly<{
19+
children: React.ReactNode;
20+
}>) {
21+
return (
22+
<html lang="en">
23+
<body>
24+
<UserStoreProvider>
25+
<Providers>
26+
<Toaster position="top-right" />
27+
<Header />
28+
{children}
29+
</Providers>
30+
</UserStoreProvider>
31+
</body>
32+
</html>
33+
);
34+
}

app/layout.tsx renamed to app/(default)/layout.tsx

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import type { Metadata } from 'next';
2-
import '~/style/globals.css';
3-
import { Providers } from '~/app/providers/providers';
4-
import Header from '~/components/Header';
5-
import { UserStoreProvider } from '~/app/providers/userStoreProvider';
1+
import type { Metadata } from 'next'
2+
import '~/style/globals.css'
3+
import { Providers } from '~/app/providers/providers'
4+
import Header from '~/components/Header'
5+
import { UserStoreProvider } from '~/app/providers/userStoreProvider'
6+
import { Toaster } from 'sonner'
67

78
export const metadata: Metadata = {
89
title: "PicImpact",
@@ -22,6 +23,7 @@ export default function RootLayout({
2223
<body>
2324
<UserStoreProvider>
2425
<Providers>
26+
<Toaster position="top-right" />
2527
<Header />
2628
{children}
2729
</Providers>

app/page.tsx renamed to app/(default)/page.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
export default function Home() {
42
return (
53
<main className="flex flex-col items-center justify-between">

app/(none)/layout.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import '~/style/globals.css'
2+
import { UserStoreProvider } from '~/app/providers/userStoreProvider'
3+
import { Providers } from '~/app/providers/providers'
4+
import { Toaster } from 'sonner'
5+
6+
export default function RootLayout({
7+
children,
8+
}: Readonly<{
9+
children: React.ReactNode;
10+
}>) {
11+
return (
12+
<html lang="en">
13+
<body>
14+
<UserStoreProvider>
15+
<Providers>
16+
<Toaster position="top-right" />
17+
{children}
18+
</Providers>
19+
</UserStoreProvider>
20+
</body>
21+
</html>
22+
);
23+
}

app/(none)/login/page.tsx

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use client'
2+
3+
import Image from "next/image"
4+
import Link from "next/link"
5+
import { Input } from '@nextui-org/input'
6+
import { Button } from '@nextui-org/react'
7+
import fufu from '~/public/112962239_p0.jpg'
8+
import { useRouter } from 'next/navigation'
9+
import { toast } from 'sonner'
10+
11+
export default function Login() {
12+
const router = useRouter()
13+
14+
return (
15+
<div className="w-full h-screen lg:grid lg:grid-cols-2">
16+
<div className="hidden bg-muted lg:block">
17+
<Image
18+
src={fufu}
19+
alt="Image"
20+
className="h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
21+
style={{
22+
width: 'auto',
23+
height: '100%',
24+
}}
25+
/>
26+
</div>
27+
<div className="flex items-center justify-center py-12">
28+
<div className="mx-auto grid w-[350px] gap-6">
29+
<div className="grid gap-2 text-center">
30+
<h1 className="text-3xl font-bold">登录</h1>
31+
<p className="text-balance text-muted-foreground">
32+
输入您的用户名和密码
33+
</p>
34+
</div>
35+
<div className="grid gap-4">
36+
<div className="grid gap-2">
37+
<div>用户名</div>
38+
<Input
39+
id="username"
40+
type="username"
41+
placeholder="admin"
42+
required
43+
/>
44+
</div>
45+
<div className="grid gap-2">
46+
<div className="flex items-center">
47+
<div>密码</div>
48+
<Link
49+
href={"/forgot-password"}
50+
className="ml-auto inline-block text-sm underline"
51+
>
52+
忘记密码?
53+
</Link>
54+
</div>
55+
<Input id="password" type="password" required/>
56+
</div>
57+
<Button type="submit" className="w-full" onClick={() => toast('还没写!')}>
58+
登录
59+
</Button>
60+
<Button type="submit" className="w-full" onClick={() => router.push('/')}>
61+
返回首页
62+
</Button>
63+
</div>
64+
</div>
65+
</div>
66+
</div>
67+
)
68+
}

app/api/login/route.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'server-only'
22
import { NextRequest } from 'next/server'
33

44
export async function POST(request: NextRequest) {
5-
const res = await request.json()
6-
console.log(res)
7-
return Response.json(res)
5+
const res = await request.json()
6+
console.log(res)
7+
return Response.json(res)
88
}

app/api/route.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'server-only'
22

33
export async function GET() {
4-
5-
return Response.json('hello')
4+
return Response.json('hello')
65
}

app/providers/providers.tsx

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
'use client'
22

3-
import { NextUIProvider } from '@nextui-org/react';
4-
import { ThemeProvider as NextThemesProvider } from 'next-themes';
3+
import { NextUIProvider } from '@nextui-org/react'
4+
import { ThemeProvider as NextThemesProvider } from 'next-themes'
55

66
export function Providers({children}: { children: React.ReactNode }) {
7-
return (
8-
<NextUIProvider>
9-
<NextThemesProvider attribute="class" defaultTheme="dark">
10-
{children}
11-
</NextThemesProvider>
12-
</NextUIProvider>
13-
)
7+
return (
8+
<NextUIProvider>
9+
<NextThemesProvider attribute="class" defaultTheme="dark">
10+
{children}
11+
</NextThemesProvider>
12+
</NextUIProvider>
13+
)
1414
}

app/providers/userStoreProvider.tsx

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use client'
2+
3+
import { type ReactNode, createContext, useRef, useContext } from 'react'
4+
import { type StoreApi, useStore } from 'zustand'
5+
6+
import { type UserStore, createUserStore, initUserStore } from '~/stores/userStore'
7+
8+
export const UserStoreContext = createContext<StoreApi<UserStore> | null>(
9+
null,
10+
)
11+
12+
export interface UserStoreProviderProps {
13+
children: ReactNode
14+
}
15+
16+
export const UserStoreProvider = ({
17+
children,
18+
}: UserStoreProviderProps) => {
19+
const storeRef = useRef<StoreApi<UserStore>>()
20+
if (!storeRef.current) {
21+
storeRef.current = createUserStore(initUserStore())
22+
}
23+
24+
return (
25+
<UserStoreContext.Provider value={storeRef.current}>
26+
{children}
27+
</UserStoreContext.Provider>
28+
)
29+
}
30+
31+
export const useUserStore = <T,>(
32+
selector: (store: UserStore) => T,
33+
): T => {
34+
const userStoreContext = useContext(UserStoreContext)
35+
36+
if (!userStoreContext) {
37+
throw new Error(`useUserStore must be use within UserStoreProvider`)
38+
}
39+
40+
return useStore(userStoreContext, selector)
41+
}

components/DarkToggle.tsx

+15-50
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,22 @@
1-
'use client';
1+
'use client'
22

3-
import { useEffect, useState } from 'react';
4-
import { useTheme } from 'next-themes';
5-
6-
import { IconSvgProps } from '~/types';
3+
import { useEffect, useState } from 'react'
4+
import { useTheme } from 'next-themes'
5+
import { MoonFilledIcon, SunFilledIcon } from '@nextui-org/shared-icons'
76

87
export const ThemeSwitch= () => {
8+
const [mounted, setMounted] = useState(false)
9+
const { theme, setTheme } = useTheme()
910

10-
const [mounted, setMounted] = useState(false)
11-
const { theme, setTheme } = useTheme()
12-
13-
useEffect(() => {
14-
setMounted(true)
15-
}, [])
16-
17-
if(!mounted) return null
11+
useEffect(() => {
12+
setMounted(true)
13+
}, [])
1814

19-
const SunFilledIcon = ({size = 24, width, height, ...props}: IconSvgProps) => (
20-
<svg
21-
aria-hidden="true"
22-
focusable="false"
23-
height={size || height}
24-
role="presentation"
25-
viewBox="0 0 24 24"
26-
width={size || width}
27-
{...props}
28-
>
29-
<g fill="currentColor">
30-
<path d="M19 12a7 7 0 11-7-7 7 7 0 017 7z" />
31-
<path d="M12 22.96a.969.969 0 01-1-.96v-.08a1 1 0 012 0 1.038 1.038 0 01-1 1.04zm7.14-2.82a1.024 1.024 0 01-.71-.29l-.13-.13a1 1 0 011.41-1.41l.13.13a1 1 0 010 1.41.984.984 0 01-.7.29zm-14.28 0a1.024 1.024 0 01-.71-.29 1 1 0 010-1.41l.13-.13a1 1 0 011.41 1.41l-.13.13a1 1 0 01-.7.29zM22 13h-.08a1 1 0 010-2 1.038 1.038 0 011.04 1 .969.969 0 01-.96 1zM2.08 13H2a1 1 0 010-2 1.038 1.038 0 011.04 1 .969.969 0 01-.96 1zm16.93-7.01a1.024 1.024 0 01-.71-.29 1 1 0 010-1.41l.13-.13a1 1 0 011.41 1.41l-.13.13a.984.984 0 01-.7.29zm-14.02 0a1.024 1.024 0 01-.71-.29l-.13-.14a1 1 0 011.41-1.41l.13.13a1 1 0 010 1.41.97.97 0 01-.7.3zM12 3.04a.969.969 0 01-1-.96V2a1 1 0 012 0 1.038 1.038 0 01-1 1.04z" />
32-
</g>
33-
</svg>
34-
);
35-
const MoonFilledIcon = ({size = 24, width, height, ...props}: IconSvgProps) => (
36-
<svg
37-
aria-hidden="true"
38-
focusable="false"
39-
height={size || height}
40-
role="presentation"
41-
viewBox="0 0 24 24"
42-
width={size || width}
43-
{...props}
44-
>
45-
<path
46-
d="M21.53 15.93c-.16-.27-.61-.69-1.73-.49a8.46 8.46 0 01-1.88.13 8.409 8.409 0 01-5.91-2.82 8.068 8.068 0 01-1.44-8.66c.44-1.01.13-1.54-.09-1.76s-.77-.55-1.83-.11a10.318 10.318 0 00-6.32 10.21 10.475 10.475 0 007.04 8.99 10 10 0 002.89.55c.16.01.32.02.48.02a10.5 10.5 0 008.47-4.27c.67-.93.49-1.519.32-1.79z"
47-
fill="currentColor"
48-
/>
49-
</svg>
50-
);
15+
if(!mounted) return null
5116

52-
return (
53-
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
54-
{theme === 'light' ? <SunFilledIcon size={22} /> : <MoonFilledIcon className="text-white" size={22} />}
55-
</button>
56-
);
17+
return (
18+
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
19+
{theme === 'light' ? <SunFilledIcon height={24} width={24} /> : <MoonFilledIcon height={24} width={24} />}
20+
</button>
21+
);
5722
};

components/DropMenu.tsx

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use client'
2+
3+
import React, { useEffect } from 'react'
4+
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from '@nextui-org/react'
5+
import { useUserStore } from '~/app/providers/userStoreProvider'
6+
import type { UserStore } from '~/stores/userStore'
7+
import { useRouter } from 'next/navigation'
8+
9+
export const DropMenu = () => {
10+
const { token, setToken, removeToken } = useUserStore((state: UserStore) => state)
11+
const router = useRouter()
12+
13+
useEffect(() => {
14+
setToken('666666')
15+
}, [])
16+
17+
return (
18+
<>
19+
{
20+
token ?
21+
<Dropdown>
22+
<DropdownTrigger>
23+
<Button variant="light">
24+
菜单
25+
</Button>
26+
</DropdownTrigger>
27+
<DropdownMenu aria-label="Static Actions">
28+
<DropdownItem key="new" onClick={() => router.push('/admin')}>后台</DropdownItem>
29+
<DropdownItem key="copy" onClick={() => { removeToken(); router.push('/') }}>退出登录</DropdownItem>
30+
</DropdownMenu>
31+
</Dropdown>
32+
:
33+
<Button variant="light" onClick={() => router.push('/login')}>
34+
登录
35+
</Button>
36+
}
37+
</>
38+
)
39+
}

components/DynamicNavbar.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use client'
2+
3+
import { useBreakpoints } from '~/utils/useBreakpoints'
4+
import { ThemeSwitch } from '~/components/DarkToggle'
5+
import VaulDrawer from '~/components/VaulDrawer'
6+
import { useHydrated } from '~/composables/react'
7+
import { DropMenu } from '~/components/DropMenu'
8+
9+
export default function DynamicNavbar() {
10+
const hydrated = useHydrated()
11+
const { smAndLarger } = useBreakpoints()
12+
13+
return (
14+
<>
15+
{ hydrated ? smAndLarger ? <ThemeSwitch/> : <VaulDrawer/> : null }
16+
<DropMenu/>
17+
</>
18+
)
19+
}

0 commit comments

Comments
 (0)