系列文章
【B站 heima 】小兔鲜Vue3 项目学习笔记 Day01
【B站 heima 】小兔鲜Vue3 项目学习笔记 Day02
【B站 heima】小兔鲜Vue3 项目学习笔记 Day03
【B站 heima】小兔鲜Vue3 项目学习笔记 Day04
【B站 heima】小兔鲜Vue3 项目学习笔记 Day05
【B站 heima】小兔鲜Vue3 项目学习笔记 Day06
Day 07
支付中心
1. 整体功能梳理和路由配置
- 准备路由
views/Member/index.vue
<!-- 会员中心 -->
<script setup></script>
<template>
<div class="container">
<div class="xtx-member-aside">
<div class="user-manage">
<h4>我的账户</h4>
<div class="links">
<RouterLink to="/member/user">个人中心</RouterLink>
</div>
<h4>交易管理</h4>
<div class="links">
<RouterLink to="/member/order">我的订单</RouterLink>
</div>
</div>
</div>
<div class="article">
<!-- 三级路由的挂载点 -->
<!-- <RouterView /> -->
</div>
</div>
</template>
<style scoped lang="scss">
.container {
display: flex;
padding-top: 20px;
.xtx-member-aside {
width: 220px;
margin-right: 20px;
border-radius: 2px;
background-color: #fff;
.user-manage {
background-color: #fff;
h4 {
font-size: 18px;
font-weight: 400;
padding: 20px 52px 5px;
border-top: 1px solid #f6f6f6;
}
.links {
padding: 0 52px 10px;
}
a {
display: block;
line-height: 1;
padding: 15px 0;
font-size: 14px;
color: #666;
position: relative;
&:hover {
color: $xtxColor;
}
&.active,
&.router-link-exact-active {
color: $xtxColor;
&:before {
display: block;
}
}
&:before {
content: '';
display: none;
width: 6px;
height: 6px;
border-radius: 50%;
position: absolute;
top: 19px;
left: -16px;
background-color: $xtxColor;
}
}
}
}
.article {
width: 1000px;
background-color: #fff;
}
}
</style>
- 配置路由,引入,二级路由
{
path: 'member',
component: Member
}
views/Member/components/UserInfo.vue
(个人信息组件)
<!-- 个人信息组件 -->
<script setup>
const userStore = {}
</script>
<template>
<div class="home-overview">
<!-- 用户信息 -->
<div class="user-meta">
<div class="avatar">
<img :src="userStore.userInfo?.avatar" />
</div>
<h4>{{ userStore.userInfo?.account }}</h4>
</div>
<div class="item">
<a href="javascript:;">
<span class="iconfont icon-hy"></span>
<p>会员中心</p>
</a>
<a href="javascript:;">
<span class="iconfont icon-aq"></span>
<p>安全设置</p>
</a>
<a href="javascript:;">
<span class="iconfont icon-dw"></span>
<p>地址管理</p>
</a>
</div>
</div>
<div class="like-container">
<div class="home-panel">
<div class="header">
<h4 data-v-bcb266e0="">猜你喜欢</h4>
</div>
<div class="goods-list">
<!-- <GoodsItem v-for="good in likeList" :key="good.id" :good="good" /> -->
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.home-overview {
height: 132px;
background: url(@/assets/images/center-bg.png) no-repeat center / cover;
display: flex;
.user-meta {
flex: 1;
display: flex;
align-items: center;
.avatar {
width: 85px;
height: 85px;
border-radius: 50%;
overflow: hidden;
margin-left: 60px;
img {
width: 100%;
height: 100%;
}
}
h4 {
padding-left: 26px;
font-size: 18px;
font-weight: normal;
color: white;
}
}
.item {
flex: 1;
display: flex;
align-items: center;
justify-content: space-around;
&:first-child {
border-right: 1px solid #f4f4f4;
}
a {
color: white;
font-size: 16px;
text-align: center;
.iconfont {
font-size: 32px;
}
p {
line-height: 32px;
}
}
}
}
.like-container {
margin-top: 20px;
border-radius: 4px;
background-color: #fff;
}
.home-panel {
background-color: #fff;
padding: 0 20px;
margin-top: 20px;
height: 400px;
.header {
height: 66px;
border-bottom: 1px solid #f5f5f5;
padding: 18px 0;
display: flex;
justify-content: space-between;
align-items: baseline;
h4 {
font-size: 22px;
font-weight: 400;
}
}
.goods-list {
display: flex;
justify-content: space-around;
}
}
</style>
views/Member/components/UserOrder.vue
(我的订单组件)
<script setup>
// tab列表
const tabTypes = [
{ name: "all", label: "全部订单" },
{ name: "unpay", label: "待付款" },
{ name: "deliver", label: "待发货" },
{ name: "receive", label: "待收货" },
{ name: "comment", label: "待评价" },
{ name: "complete", label: "已完成" },
{ name: "cancel", label: "已取消" }
]
// 订单列表
const orderList = []
</script>
<template>
<div class="order-container">
<el-tabs>
<!-- tab切换 -->
<el-tab-pane v-for="item in tabTypes" :key="item.name" :label="item.label" />
<div class="main-container">
<div class="holder-container" v-if="orderList.length === 0">
<el-empty description="暂无订单数据" />
</div>
<div v-else>
<!-- 订单列表 -->
<div class="order-item" v-for="order in orderList" :key="order.id">
<div class="head">
<span>下单时间:{{ order.createTime }}</span>
<span>订单编号:{{ order.id }}</span>
<!-- 未付款,倒计时时间还有 -->
<span class="down-time" v-if="order.orderState === 1">
<i class="iconfont icon-down-time"></i>
<b>付款截止: {{order.countdown}}</b>
</span>
</div>
<div class="body">
<div class="column goods">
<ul>
<li v-for="item in order.skus" :key="item.id">
<a class="image" href="javascript:;">
<img :src="item.image" alt="" />
</a>
<div class="info">
<p class="name ellipsis-2">
{{ item.name }}
</p>
<p class="attr ellipsis">
<span>{{ item.attrsText }}</span>
</p>
</div>
<div class="price">¥{{ item.realPay?.toFixed(2) }}</div>
<div class="count">x{{ item.quantity }}</div>
</li>
</ul>
</div>
<div class="column state">
<p>{{ order.orderState }}</p>
<p v-if="order.orderState === 3">
<a href="javascript:;" class="green">查看物流</a>
</p>
<p v-if="order.orderState === 4">
<a href="javascript:;" class="green">评价商品</a>
</p>
<p v-if="order.orderState === 5">
<a href="javascript:;" class="green">查看评价</a>
</p>
</div>
<div class="column amount">
<p class="red">¥{{ order.payMoney?.toFixed(2) }}</p>
<p>(含运费:¥{{ order.postFee?.toFixed(2) }})</p>
<p>在线支付</p>
</div>
<div class="column action">
<el-button v-if="order.orderState === 1" type="primary"
size="small">
立即付款
</el-button>
<el-button v-if="order.orderState === 3" type="primary" size="small">
确认收货
</el-button>
<p><a href="javascript:;">查看详情</a></p>
<p v-if="[2, 3, 4, 5].includes(order.orderState)">
<a href="javascript:;">再次购买</a>
</p>
<p v-if="[4, 5].includes(order.orderState)">
<a href="javascript:;">申请售后</a>
</p>
<p v-if="order.orderState === 1"><a href="javascript:;">取消订单</a></p>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination background layout="prev, pager, next" />
</div>
</div>
</div>
</el-tabs>
</div>
</template>
<style scoped lang="scss">
.order-container {
padding: 10px 20px;
.pagination-container {
display: flex;
justify-content: center;
}
.main-container {
min-height: 500px;
.holder-container {
min-height: 500px;
display: flex;
justify-content: center;
align-items: center;
}
}
}
.order-item {
margin-bottom: 20px;
border: 1px solid #f5f5f5;
.head {
height: 50px;
line-height: 50px;
background: #f5f5f5;
padding: 0 20px;
overflow: hidden;
span {
margin-right: 20px;
&.down-time {
margin-right: 0;
float: right;
i {
vertical-align: middle;
margin-right: 3px;
}
b {
vertical-align: middle;
font-weight: normal;
}
}
}
.del {
margin-right: 0;
float: right;
color: #999;
}
}
.body {
display: flex;
align-items: stretch;
.column {
border-left: 1px solid #f5f5f5;
text-align: center;
padding: 20px;
>p {
padding-top: 10px;
}
&:first-child {
border-left: none;
}
&.goods {
flex: 1;
padding: 0;
align-self: center;
ul {
li {
border-bottom: 1px solid #f5f5f5;
padding: 10px;
display: flex;
&:last-child {
border-bottom: none;
}
.image {
width: 70px;
height: 70px;
border: 1px solid #f5f5f5;
}
.info {
width: 220px;
text-align: left;
padding: 0 10px;
p {
margin-bottom: 5px;
&.name {
height: 38px;
}
&.attr {
color: #999;
font-size: 12px;
span {
margin-right: 5px;
}
}
}
}
.price {
width: 100px;
}
.count {
width: 80px;
}
}
}
}
&.state {
width: 120px;
.green {
color: $xtxColor;
}
}
&.amount {
width: 200px;
.red {
color: $priceColor;
}
}
&.action {
width: 140px;
a {
display: block;
&:hover {
color: $xtxColor;
}
}
}
}
}
}
</style>
- 配置三级路由
import MemberInfo from '@/views/Member/components/UserInfo.vue'
import MemberOrder from '@/views/Member/components/UserOrder.vue'
{
path: '/member',
component: Member,
children: [
{
path: 'user',
component: MemberInfo
},
{
path: 'order',
component: MemberOrder
}
]
}
记得把这里放开:
效果:
2. 个人中心信息渲染
- 用户头像和名字使用的时pinia里面的数据进行渲染
<script setup>
// 导入userStore
import { useUserStore } from '@/stores/userStore'
const userStore = useUserStore()
</script>
<div class="user-meta">
<div class="avatar">
<img :src="userStore.userInfo?.avatar" />
</div>
<h4>{{ userStore.userInfo?.account }}</h4>
</div>
- 封装并调用 猜你喜欢 接口
//apis/user.js
//猜你喜欢 接口
export const getLikeListAPI = ({ limit = 4 }) => {
return httpInstance({
url: '/goods/relevant',
params: {
limit
}
})
}
<!-- 个人信息组件 -->
// 导入userStore
import { useUserStore } from '@/stores/user'
import { getLikeListAPI } from '@/apis/user.js'
import { onMounted, ref } from 'vue'
import GoodItem from '@/views/Home/components/GoodItem.vue'
const userStore = useUserStore()
//猜你喜欢 信息
const likeList = ref([])
const getLikeList = async () => {
const res = await getLikeListAPI({ limit: 4 })
likeList.value = res.result
}
onMounted(() => getLikeList())
渲染:
<GoodItem v-for="goods in likeList" :key="goods.id" :goods="goods" />
效果:
3. 我的订单-基础订单列表实现(主区域)
- 封装获取订单接口
apis/order.js
import httpInstance from '@/utils/http'
export const getUserOrder = (params) => {
return httpInstance({
url: '/member/order',
method: 'GET',
params
})
}
// UserOrder.vue
import { getUserOrder } from '@/apis/order'
import { onMounted, ref } from 'vue'
// tab列表
const tabTypes = [
{ name: "all", label: "全部订单" },
{ name: "unpay", label: "待付款" },
{ name: "deliver", label: "待发货" },
{ name: "receive", label: "待收货" },
{ name: "comment", label: "待评价" },
{ name: "complete", label: "已完成" },
{ name: "cancel", label: "已取消" }
]
// 获取订单列表
const orderList = ref([])
const params = ref({
orderState: 0,
page: 1,
pageSize: 2
})
const getOrderList = async () => {
const res = await getUserOrder(params.value)
orderList.value = res.result.items //订单列表
total.value = res.result.counts //订单总数
}
onMounted(() => getOrderList())
报错的话修改一下
http.js
中的超时时间,让其长一些试试
4. 我的订单-tab切换
点击哪一项显示当前项的数据
思路:切换时修改orderState
参数,再次发起请求获取数据。
- 绑定事件
<el-tabs @tab-change="tabChange">...
- 逻辑
//切换tab type是这个组件自带的,用来记录当前点击的是哪一个
const tabChange = (type) => {
params.value.orderState = type
getOrderList() //重新获取数据
}
5. 分页逻辑实现
无论是C端或者B端都会有应用
使用total
绑定总条数,使用page-size
属性绑定每页条数。
const total = ref(0)
...
total.value = res.result.counts //订单总数
...
<!-- 分页 绑定total-->
<div class="pagination-container">
<el-pagination background layout="prev, pager, next" :total="total"
page-size="params.pageSize" />
</div>
实现切换分页:
绑定current-change
事件,拿到当前页数,使用最新页数发送请求获取数据
//切换分页
const pageChange = (page) => {
params.value.page = page
getOrderList() //获取最新的数据
}
<!-- 分页 绑定total-->
<div class="pagination-container">
<el-pagination background layout="prev, pager, next" :total="total"
page-size="params.pageSize" @current-change="pageChange"/>
</div>
6. 细节优化
- 当我们跳转到
member
这个路由时,希望能显示个人中心的数据而不是这样⬇空白
路由路径置空,修改路径即可
{
path: 'member',
component: Member,
children: [
{
path: '',
component: MemberInfo
},
{
path: 'order',
component: MemberOrder
}
]
}
<!--Member/index.vue-->
<div class="links">
<RouterLink to="/member">个人中心</RouterLink>
</div>
//OrderInfo.vue
// 创建格式化函数
const fomartPayState = (payState) => {
const stateMap = {
1: '待付款',
2: '待发货',
3: '待收货',
4: '待评价',
5: '已完成',
6: '已取消'
}
return stateMap[payState]
}
<div class="column state">
<p>{{ fomartPayState(order.orderState) }}</p>
<p v-if="order.orderState === 3">
<a href="javascript:;" class="green">查看物流</a>
</p>
<p v-if="order.orderState === 4">
<a href="javascript:;" class="green">评价商品</a>
</p>
<p v-if="order.orderState === 5">
<a href="javascript:;" class="green">查看评价</a>
</p>
</div>
小结
完结撒花❀❀❀
做完项目及时复盘喔,找出难点重点,理清思路
常看常新哦