首先我们来添加几个svg静态资源,可以直接从当前地址下载:github.com/liyunfu1998...
组件
通用组件
我们先来Icons
中引入更多的icon,修改components/share/Icons.js
js
import {
BsEyeSlash,
BsEye,
BsCart2,
BsShieldExclamation,
BsQuestionSquare,
BsClockHistory,
} from 'react-icons/bs'
import { FiLogIn, FiLogOut, FiEdit } from 'react-icons/fi'
import { VscThreeBars } from 'react-icons/vsc'
import { HiOutlineUser } from 'react-icons/hi'
import { IoSearch } from 'react-icons/io5'
import {
RiArrowDownSFill,
RiArrowDropLeftLine,
RiArrowRightLine,
RiHandbagLine,
RiHeartLine,
RiChat1Line,
RiUserLocationLine,
RiHome6Line,
} from 'react-icons/ri'
const Icons = {
Eye: BsEye,
EyeSlash: BsEyeSlash,
Cart: BsCart2,
Login: FiLogIn,
Logout: FiLogOut,
Exclamation: BsShieldExclamation,
Bars: VscThreeBars,
User: HiOutlineUser,
Question: BsQuestionSquare,
Search: IoSearch,
ArrowDown: RiArrowDownSFill,
ArrowLeft: RiArrowDropLeftLine,
ArrowRight: RiArrowRightLine,
Edit: FiEdit,
Clock: BsClockHistory,
Heart: RiHeartLine,
Bag: RiHandbagLine,
Comment: RiChat1Line,
Location: RiUserLocationLine,
Home: RiHome6Line,
}
export default Icons
然后新建components/share/BackButton.js
用作返回的组件
js
import { useRouter } from 'next/navigation'
import { Icons } from '@/components'
export default function BackButton({ children }) {
const router = useRouter()
return (
<div className="flex items-center gap-x-1 pb-4 px-5">
<button className="lg:hidden" type="button" onClick={() => router.back()}>
<Icons.ArrowRight className="icon" />
</button>
<span>{children}</span>
</div>
)
}
普通组件
新建components/Order.jsx
用于订单页
jsx
import Image from 'next/image'
import { ArrowLink } from '@/components'
export default function Orders() {
return (
<>
<div className=" py-6 lg:py-0">
<div className="flex justify-between mb-7 px-5">
<h2 className="inline-block py-1 border-b-2 border-red-500">我的订单</h2>
<ArrowLink path="profile/orders">所有订单</ArrowLink>
</div>
<div className="flex justify-evenly lg:py-20">
<div className="flex flex-col items-center lg:flex-row lg:gap-x-2">
<div className="relative w-14 h-14 ">
<Image src="/images/status-processing.svg" layout="fill" alt="processing" />
<span className="absolute order-badge">0</span>
</div>
<div className="text-gray-700">
<span className="hidden lg:block"> 0 顺序</span>
<span>现在的</span>
</div>
</div>
<div className="section-divide-x" />
<div className="flex flex-col items-center lg:flex-row lg:gap-x-2">
<div className="relative w-14 h-14 ">
<Image src="/images/status-delivered.svg" layout="fill" alt="delivered" />
<span className="absolute order-badge">0</span>
</div>
<div className="text-gray-700">
<span className="hidden lg:block"> 0 顺序</span>
<span>已传输</span>
</div>
</div>
<div className="section-divide-x" />
<div className="flex flex-col items-center lg:flex-row lg:gap-x-2">
<div className="relative w-14 h-14 ">
<Image src="/images/status-returned.svg" layout="fill" alt="returned" />
<span className="absolute order-badge">0</span>
</div>
<div className="text-gray-700">
<span className="hidden lg:block"> 0 顺序</span>
<span>提示</span>
</div>
</div>
</div>
</div>
<div className="section-divide-y" />
</>
)
}
新建components/ProfileAside.jsx
用于个人中心页
jsx
import Image from 'next/image'
import Link from 'next/link'
import { Icons, BoxLink, Logout, Orders } from '@/components'
export default function ProfileAside({ user }) {
const profilePaths = [
{
name: '订单',
icon: <Icons.Bag className="icon text-black" />,
path: '/profile/orders',
},
{
name: '我的列表',
icon: <Icons.Heart className="icon text-black" />,
path: '/profile/lists',
},
{
name: '视图',
icon: <Icons.Comment className="icon text-black" />,
path: '/profile/comments',
},
{
name: '地址',
icon: <Icons.Location className="icon text-black" />,
path: '/profile/addresses',
},
{
name: '最近访问',
icon: <Icons.Clock className="icon text-black" />,
path: '/profile/user-history',
},
{
name: '用户帐户信息',
icon: <Icons.User className="icon text-black" />,
path: '/profile/personal-info',
},
]
return (
<aside className="mt-6 lg:border lg:border-gray-200 lg:rounded-md lg:py-4">
<div className="px-5 py-2 flex justify-between items-center ">
<div className="relative w-12 h-12">
<Image src={user?.avatar} layout="fill" alt="avatar" />
</div>
<span className="ml-auto mr-2 font-bold ">{user?.name}</span>
<Link href="profile/personal-info">
<span>
<Icons.Edit className="text-blue-400 w-6 h-6" />
</span>
</Link>
</div>
<div className="lg:hidden">
<Orders />
</div>
<div className="mt-7">
<div className="hidden lg:block">
<BoxLink name="活动步骤" path="/profile">
<Icons.Home className="icon text-black" />
</BoxLink>
</div>
{profilePaths.map((item, index) => (
<BoxLink key={index} path={item.path} name={item.name}>
{item.icon}
</BoxLink>
))}
<Logout />
</div>
</aside>
)
}
在components/index.js
中将她们都导出
js
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 ProfileLayout } from './Layouts/ProfileLayout'
export { default as DashboardLayout } from './Layouts/DashboardLayout'
export { default as Orders } from './Order'
export { default as Logout } from './Logout'
export { default as ArrowLink } from './share/ArrowLink'
export { default as BoxLink } from './share/BoxLink'
export { default as BackButton } from './share/BackButton'
export { default as ProfileAside } from './ProfileAside'
到现在我们可以改变ProfileLayout
了,改变components/Layouts/ProfileLayout.jsx
jsx
'use client'
import { ClientLayout, ProfileAside } from '@/components'
import { useSelector } from 'react-redux'
export default function Layout({ children }) {
const { user } = useSelector(state => state.auth)
if (!user) return null
return (
<>
<ClientLayout />
<div className="lg:flex lg:gap-x-4 lg:px-3 lg:container lg:max-w-7xl">
<div className="hidden lg:block">
<ProfileAside user={user} />
</div>
<div className="py-4 lg:py-8 lg:border lg:border-gray-300 flex-1 lg:rounded-md lg:mt-6 h-fit">
{children}
</div>
</div>
</>
)
}
再加几个基于class的全局样式,修改global.css
css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
@font-face {
font-family: "Vazir";
font-style: normal;
font-weight: 400;
src: url("/fonts/vazir/Vazirmatn-Regular.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: "Vazir";
font-style: normal;
font-weight: 500;
src: url("/fonts/vazir/Vazirmatn-Medium.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: "Vazir";
font-style: normal;
font-weight: 700;
src: url("/fonts/vazir/Vazirmatn-Bold.woff2") format("woff2");
font-display: swap;
}
}
@layer base {
body {
font-family: "Vazir";
}
input {
box-shadow: 0 0 0px 1000px #fafafa inset;
-webkit-box-shadow: 0 0 0px 1000px #fafafa inset;
}
h2 {
@apply text-xl;
}
p {
@apply text-base;
}
button {
@apply cursor-pointer;
}
a {
@apply cursor-pointer;
}
}
@layer components {
.input {
@apply bg-zinc-50 w-full block
outline-none py-2 px-3 rounded-md
text-base lg:text-lg text-center
border border-gray-200
focus:border-blue-600 transition-colors;
}
.error-msg {
@apply mt-1.5 inline-flex gap-x-1 justify-center text-sm text-red-600;
}
.btn {
@apply text-white bg-red-500 py-3 px-4 rounded-3xl block outline-none;
}
.icon {
@apply w-6 h-6 text-gray-700;
}
.order-badge {
@apply bottom-0 left-1 bg-slate-200 p-1 rounded-md w-6 h-6 text-center text-sm text-black/80 lg:hidden;
}
.section-divide-y {
@apply h-3 bg-gray-200 lg:hidden;
}
.section-divide-x {
@apply w-0.5 bg-gray-200;
}
.b {
@apply border border-red-600;
}
}
加了order-badge
、section-divide-y
、section-divide-x
页面
新建app/profile/layout.js
js
'use client'
import { ProfileLayout } from '@/components'
import { useRefreshToken } from '@/hooks'
export default function Layout({ children }) {
useRefreshToken()
return (
<>
<ProfileLayout>{children}</ProfileLayout>
</>
)
}
新建app/profile/page.js
js
'use client'
import { Orders } from '@/components'
import { useSelector } from 'react-redux'
export default function Page() {
const { user } = useSelector(state => state.auth)
return (
<>
<Orders />
</>
)
}
现在效果如下:
顺便把缺失的profile
下面的页面补充完整吧,类似于下面所示,随意填写页面内容即可: