likes
comments
collection
share

「11」next-shopping:个人中心页

作者站长头像
站长
· 阅读数 4

首先我们来添加几个svg静态资源,可以直接从当前地址下载:github.com/liyunfu1998…

组件

通用组件

我们先来Icons中引入更多的icon,修改components/share/Icons.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用作返回的组件

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用于订单页

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 用于个人中心页

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中将她们都导出

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

'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

@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-badgesection-divide-ysection-divide-x

页面

新建app/profile/layout.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

'use client'

import { Orders } from '@/components'
import { useSelector } from 'react-redux'

export default function Page() {
  const { user } = useSelector(state => state.auth)

  return (
    <>
      <Orders />
    </>
  )
}

现在效果如下:

「11」next-shopping:个人中心页

顺便把缺失的profile下面的页面补充完整吧,类似于下面所示,随意填写页面内容即可:

「11」next-shopping:个人中心页

「11」next-shopping:个人中心页

代码地址: github.com/liyunfu1998…