我们先来调整一下C端页面,把app/(normal-layout)
改名叫app/(client-layout)
,虽然对实际没有影响,但是对开发者更好识别
我们现在可以把layout
抽成通用的:
新建components/Layouts/ClientLayout.jsx
jsx
import { Navbar } from '@/components'
export default function Layout({ children }) {
return (
<>
<Navbar />
{children}
</>
)
}
这是客户端的页面通用布局,header部分需要nav
新建components/Layouts/DashboardLayout.jsx
jsx
export default function Layout({ children }) {
return <div>{children} </div>
}
目前仅最简单的包一层,后面会添加Nav
和Footer
的
新建components/Layouts/ProfileLayout.jsx
jsx
export default function Layout({ children }) {
return <div>{children}</div>
}
这个个人中心也是组简单的布局
在components/index.js
中导出
jsx
export { default as Icons } from './share/Icons'
export { default as Loading } from './share/Loading'
export { default as DisplayError } from './share/DisplayError'
export { default as Navbar } from './Navbar'
export { default as User } from './User'
export { default as Cart } from './Cart'
export { default as Search } from './Search'
export { default as ClientLayout } from './Layouts/ClientLayout'
export { default as DashboardLayout } from './Layouts/DashboardLayout'
export { default as ProfileLayout } from './Layouts/ProfileLayout'
然后我们用ClientLayout
来代替app/(main)/(client-layout)/layout.js
的布局
jsx
'use client'
import { ClientLayout } from '@/components'
import { useRefreshToken } from '@/hooks'
export default function Layout({ children }) {
useRefreshToken()
return (
<>
<ClientLayout>{children}</ClientLayout>
</>
)
}
效果如下:
然后我们新建app/(dashboard-layout)/layout.js
jsx
'use client'
import { DashboardLayout } from '@/components'
export default function Layout({ children }) {
return (
<>
<DashboardLayout>{children}</DashboardLayout>
</>
)
}
新建app/(dashboard-layout)/dashboard/page.jsx
jsx
export default function Page() {
return <div>这是管理页面</div>
}
效果如下:
注销页面
我们先来实现两个通用组件BoxLink
和ArrowLink
新建components/share/BoxLink.jsx
jsx
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { Icons } from '@/components'
export default function BoxLink({ children, path, name }) {
const router = useRouter()
return (
<div
className={`transition-colors hover:bg-gray-200 px-3 ${
router.asPath === path ? 'border-r-4 border-red-600' : 'border-r-4 border-white'
}`}
>
<Link href={path}>
<span className="flex justify-between mx-4 py-4 gap-x-2 border-t border-gray-300">
{children}
<span className="ml-auto mr-3">{name}</span>
<Icons.ArrowLeft className="icon" />
</span>
</Link>
</div>
)
}
新建components/share/ArrowLink.jsx
jsx
import Link from 'next/link'
import { Icons } from '@/components'
export default function ArrowLink({ children, path }) {
return (
<Link href={path}>
<span className="inline-flex items-center text-blue-400 text-sm max-w-max">
<span className="uppercase">{children}</span>
<Icons.ArrowLeft className="icon text-blue-400" />
</span>
</Link>
)
}
然后我们来写退出登录组件:
新建components/Logout.jsx
jsx
import { useRouter } from 'next/navigation'
import { useDispatch } from 'react-redux'
import { userLogout } from '@/store/slices/authSlice'
import { Icons } from '@/components'
import alert from '@/utils/alert'
export default function Logout() {
const dispatch = useDispatch()
const router = useRouter()
const handleLogout = () => {
router.push('/')
dispatch(userLogout())
alert('success', '退出成功')
}
return (
<div className="transition-colors hover:bg-gray-200 px-3">
<div
role="button"
className="flex justify-between cursor-pointer py-4 gap-x-2 mx-4 border-t border-gray-300"
onClick={() => handleLogout()}
>
<Icons.Logout className="icon text-black" />
<span className="ml-auto mr-3">其他组织者</span>
</div>
</div>
)
}
先在components/index.js
中导出组件:
jsx
export { default as Icons } from './share/Icons'
export { default as Loading } from './share/Loading'
export { default as DisplayError } from './share/DisplayError'
export { default as Navbar } from './Navbar'
export { default as User } from './User'
export { default as Cart } from './Cart'
export { default as Search } from './Search'
export { default as ClientLayout } from './Layouts/ClientLayout'
export { default as DashboardLayout } from './Layouts/DashboardLayout'
export { default as ProfileLayout } from './Layouts/ProfileLayout'
export { default as ArrowLink } from './share/ArrowLink'
export { default as BoxLink } from './share/BoxLink'
export { default as Logout } from './Lagout'
现在来修改User
组件,我们先看看当前的效果:
修改components/User.jsx
jsx
import Image from 'next/image'
import Link from 'next/link'
import { useState } from 'react'
import { useSelector } from 'react-redux'
import { BoxLink, Icons, Logout } from './index'
export default function User() {
const { user } = useSelector(state => state.auth)
const [isOpen, setIsOpen] = useState(false)
if (!user) {
return (
<div className="flex items-center gap-x-2 lg:border lg:border-gray-300 lg:rounded-md lg:py-2 lg:px-3 text-sm">
<Link href="/login">
<span className="flex items-center gap-x-1">
<Icons.Login className="icon" />
登录
</span>
</Link>
<span className="hidden lg:block lg:border lg:border-gray-300 lg:h-6"></span>
<Link href="/register">
<span className="hidden lg:block px-2">注册</span>
</Link>
</div>
)
}
return (
<>
<div className="lg:hidden">
<Link href="/profile">
<span>
<Icons.User className="icon" />
</span>
</Link>
</div>
<div
className={`hidden lg:cursor-pointer lg:relative lg:flex lg:rounded lg:p-1.5 lg:transition ${isOpen && 'bg-red-100'}`}
onClick={() => setIsOpen(!isOpen)}
>
<Icons.User className="icon" />
<Icons.ArrowDown className="icon" />
<div
className={` bg-white shadow-md rounded overflow-hidden absolute top-full left-0 w-60
border border-gray-100 ${isOpen ? 'block' : 'hidden'}`}
>
<BoxLink path="/profile" name={user.name} className="border-t-0">
<div className="realative w-6 h-6">
<Image src={'/avatar.png'} alt="user" width={200} height={200} />
</div>
</BoxLink>
<Logout />
</div>
</div>
</>
)
}
主要是用BoxLink
套了一层,省略很多代码,然后把退出登录的逻辑单独抽出来的Logout.jsx