接下来是其余部分的实现。
接口
支付相关
在apis文件夹下新建pay.js
1 2 3 4 5
| export const getOrderAPI = (id) => { return request({ url: `/member/order/${id}` }) }
|
会员中心
在apis文件夹下新建checkout.js
1 2 3 4 5 6 7 8 9 10 11 12 13
| export const getCheckInfoAPI = () => { return request({ url: 'member/order/pre' }) }
export const createOrderAPI = (data) => { return request({ url: 'member/order', method: 'POST', data }) }
|
新建order.js
1 2 3 4 5 6 7
| export const getUserOrderAPI = (params) => { return request({ url: 'member/order', method: 'GET', params }) }
|
在user.js
下新增一个接口
1 2 3 4 5 6 7 8
| export const getLikeListAPI = ({ limit = 4 }) => { return request({ url: '/goods/relevant', params: { limit } }) }
|
确认订单网页
在views/checkout文件夹下新建index.vue
,源码如下

其中的这部分
可以使用<i></i>
来实现有效的字段偏移,实现等宽显示。
独立出来的<el-dialog />
部分用于显示切换地址部分。

其中需要一个接口获取相关信息
1 2 3 4 5 6 7 8 9
| const getCheckInfo = async () => { const res = await getCheckInfoAPI() checkInfo.value = res.result
const item = checkInfo.value.userAddresses.find(item => item.isDefault === 0) curAddress.value = item }
onMounted(() => getCheckInfo())
|
该方法通过相关接口获取订单信息。至于从购物车到订单的转换,是在后端实现的。同时,将显示地址切换为默认地址(找到默认地址。
若需要切换地址,也需要相应的方法。但需要注意,点击地址不等于立即切换,需要先在一个临时变量中存储切换到的值,点击确认后再将临时变量赋值给显示地址。而临时变量本身也承担着控制高亮组件的功能。
1 2 3 4 5 6 7 8
| const switchAddress = (item) => { activeAddAddress.value = item } const confirm = () => { curAddress.value = activeAddAddress.value showDialog.value = false activeAddAddress.value = {} }
|
创造订单功能,注入相关数据,并跳转到订单页,开始等待支付
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const createOrder = async () => { const res = await createOrderAPI({ deliveryTimeType: 1, payType: 1, pauChannel: 1, buyMessage: '', goods: checkInfo.value.goods.map(item => { return { skuId: item.skuId, count: item.count } }), addressId: curAddress.value.id, }) const orderId = res.result.id router.push({ path: '/pay', query: { id: orderId } }) cartStore.updateNewList() }
|
等待支付网页
在views/pay文件夹下新建index.vue
,源码如下

js部分除了常规的调用接口,还有调用计数器部分,该部分需要在src/composable下新建文件useCountDown.vue
文件,源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { computed, onUnmounted, ref } from "vue" import dayjs from "dayjs"
export const useCountDown = () => { let timer = null const time = ref(0) const formatTime = computed(() => dayjs.unix(time.value).format('mm分ss秒')) const start = (currentTime) => { formatTime.value = currentTime setInterval(() => { formatTime.value-- }, 1000) }
onUnmounted(() => { timer && clearInterval(timer) })
return { formatTime, start } }
|
dayjs
库用于格式化日期输出,此处在计算方法中表现。start部分设立了一个每隔1000毫秒减1的计数器,开始时间由参数决定。
接下来配置沙箱网页和返回网页
1 2 3 4
| const baseURL = 'http://pcapi-xiaotuxian-front-devtest.itheima.net/' const backURL = 'http://127.0.0.1:5173/paycallback' const redirectUrl = encodeURIComponent(backURL) const payUrl = `${baseURL}pay/aliPay?orderId=${route.query.id}&redirect=${redirectUrl}`
|
返回页
从沙箱中跳转后,需要一个页面显示支付情况。在pay文件夹下新建payBack.vue
文件,源码如下

会员中心页面
其实更贴切的说法是用户中心,但原视频如此,这里不好改变。
这里主要涉及三级路由的实现。在Member文件夹下新建components文件夹和index.vue
文件,源码如下
用户信息
在components文件夹下新建UserInfo.vue
,代码如下

订单信息
在components文件夹下新建UserOrder.vue
,代码如下

源码
确认订单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
| <script setup> import { onMounted, ref } from 'vue' import { useRouter } from 'vue-router' import { getCheckInfoAPI, createOrderAPI } from '../../apis/checkout' import { useCartStore } from '../../stores/cartStore'
const checkInfo = ref({}) const curAddress = ref({}) const cartStore = useCartStore() const router = useRouter()
const getCheckInfo = async () => { const res = await getCheckInfoAPI() checkInfo.value = res.result
const item = checkInfo.value.userAddresses.find(item => item.isDefault === 0) curAddress.value = item }
onMounted(() => getCheckInfo())
const showDialog = ref(false)
const activeAddAddress = ref({}) const switchAddress = (item) => { activeAddAddress.value = item }
const confirm = () => { curAddress.value = activeAddAddress.value showDialog.value = false activeAddAddress.value = {} }
const createOrder = async () => { const res = await createOrderAPI({ deliveryTimeType: 1, payType: 1, pauChannel: 1, buyMessage: '', goods: checkInfo.value.goods.map(item => { return { skuId: item.skuId, count: item.count } }), addressId: curAddress.value.id, }) const orderId = res.result.id router.push({ path: '/pay', query: { id: orderId } }) cartStore.updateNewList() } </script>
<template> <div class="xtx-pay-checkout-page"> <div class="container"> <div class="wrapper"> <h3 class="box-title">收货地址</h3> <div class="box-body"> <div class="address"> <div class="text"> <div class="none" v-if="!curAddress">您需要先添加收货地址才可提交订单</div> <ul v-else> <li><span>收<i />货<i />人: </span>{{ curAddress.receiver }}</li> <li><span>联系方式: </span>{{ curAddress.contact }}</li> <li><span>收货地址</span>{{ curAddress.fullLocation }} {{ curAddress.address }}</li> </ul> </div> <dic class="action"> <el-button size="large" @click="showDialog = true">切换地址</el-button> <el-button size="large">添加地址</el-button> </dic> </div> </div>
<h3 class="box-title">商品信息</h3> <div class="box-body"> <table class="goods"> <thead> <tr> <th width="520">商品信息</th> <th width="170">单价</th> <th width="170">数量</th> <th width="170">小计</th> <th width="170">实付</th> </tr> </thead> <tbody> <tr v-for="i in checkInfo.goods" :key="i.id"> <td> <a href="javascript:;" class="info"> <img :src="i.picture" alt=""> <div class="right"> <p>{{ i.name }}</p> <p>{{ i.attrsText }}</p> </div> </a> </td> <td>¥{{ i.price }}</td> <td>{{ i.price }}</td> <td>¥{{ i.totalPrice }}</td> <td>¥{{ i.totalPayPrice }}</td> </tr> </tbody> </table>
<h3 class="box-title">配送时间</h3> <div class="box-body"> <a class="my-btn active" href="javascript:;">不限送货时间:周一至周日</a> <a class="my-btn" href="javascript:;">工作日送货:周一至周五</a> <a class="my-btn" href="javascript:;">双休日、假日送货:周六至周日</a> </div>
<h3 class="box-title">支付方式</h3> <div class="box-body"> <a class="my-btn active" href="javascript:;">在线支付</a> <a class="my-btn" href="javascript:;">货到付款</a> <span style="color:#999">货到付款需付5元手续费</span> </div>
<h3 class="box-title">金额明细</h3> <div class="box-body"> <div class="total"> <dl> <dt>商品件数:</dt> <dd>{{ checkInfo.summary?.goodsCount }}件</dd> </dl> <dl> <dt>商品总价:</dt> <dd>¥{{ checkInfo.summary?.totalPrice.toFixed(2) }}</dd> </dl> <dl> <dt>运<i></i>费:</dt> <dd>¥{{ checkInfo.summary?.postFee.toFixed(2) }}</dd> </dl> <dl> <dt>应付总额:</dt> <dd class="price">{{ checkInfo.summary?.totalPayPrice.toFixed(2) }}</dd> </dl> </div> </div>
<div class="submit"> <el-button @click="createOrder" type="primary" size="large">提交订单</el-button> </div> </div> </div>
</div> </div> <el-dialog v-model="showDialog" title="切换收货地址" width="30%" center> <div class="addressWrapper"> <div class="text item" :class="{ active: activeAddAddress.id === item.id }" @click="switchAddress(item)" v-for="item in checkInfo.userAddresses" :key="item.id"> <ul> <li><span>收<i />货<i />人:</span>{{ item.receiver }} </li> <li><span>联系方式:</span>{{ item.contact }}</li> <li><span>收货地址:</span>{{ item.fullLocation + item.address }}</li> </ul> </div> </div> <template #footer> <span class="dialog-footer"> <el-button>取消</el-button> <el-button type="primary" @click="confirm">确定</el-button> </span> </template> </el-dialog> </template>
<style scoped lang="scss"> .xtx-pay-checkout-page { margin-top: 20px;
.wrapper { background: #fff; padding: 0 20px;
.box-title { font-size: 16px; font-weight: normal; padding-left: 10px; line-height: 70px; border-bottom: 1px solid #f5f5f5; }
.box-body { padding: 20px 0; } } }
.address { border: 1px solid #f5f5f5; display: flex; align-items: center;
.text { flex: 1; min-height: 90px; display: flex; align-items: center;
.none { line-height: 90px; color: #999; text-align: center; width: 100%; }
>ul { flex: 1; padding: 20px;
li { line-height: 30px;
span { color: #999; margin-right: 5px;
>i { width: 0.5em; display: inline-block; } } } }
>a { color: $xtxColor; width: 160px; text-align: center; height: 90px; line-height: 90px; border-right: 1px solid #f5f5f5; } }
.action { width: 420px; text-align: center;
.btn { width: 140px; height: 46px; line-height: 44px; font-size: 14px;
&:first-child { margin-right: 10px; } } } }
.goods { width: 100%; border-collapse: collapse; border-spacing: 0;
.info { display: flex; text-align: left;
img { width: 70px; height: 70px; margin-right: 20px; }
.right { line-height: 24px;
p { &:last-child { color: #999; } } } }
tr { th { background: #f5f5f5; font-weight: normal; }
td, th { text-align: center; padding: 20px; border-bottom: 1px solid #f5f5f5;
&:first-child { border-left: 1px solid #f5f5f5; }
&:last-child { border-right: 1px solid #f5f5f5; } } } }
.my-btn { width: 228px; height: 50px; border: 1px solid #e4e4e4; text-align: center; line-height: 48px; margin-right: 25px; color: #666666; display: inline-block;
&.active, &:hover { border-color: $xtxColor; } }
.total { dl { display: flex; justify-content: flex-end; line-height: 50px;
dt { i { display: inline-block; width: 2em; } }
dd { width: 240px; text-align: right; padding-right: 70px;
&.price { font-size: 20px; color: $priceColor; } } } }
.submit { text-align: right; padding: 60px; border-top: 1px solid #f5f5f5; }
.addressWrapper { max-height: 500px; overflow-y: auto; }
.text { flex: 1; min-height: 90px; display: flex; align-items: center;
&.item { border: 1px solid #f5f5f5; margin-bottom: 10px; cursor: pointer;
&.active, &:hover { border-color: $xtxColor; background: $xtxColor; }
>ul { padding: 10px; font-size: 14px; line-height: 30px; } } } </style>
|
等待支付
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
| <script setup> import { onMounted, ref } from 'vue' import { useRoute } from 'vue-router' import { getOrderAPI } from '@/apis/pay' import { useCountDown } from '../../composables/useCountDown'
const { formatTime, start } = useCountDown() const orderInfo = ref({}) const route = useRoute() const getOrderInfo = async () => { const res = await getOrderAPI(route.query.id) orderInfo.value = res.result start(res.result.countdown) }
onMounted(() => getOrderInfo())
const baseURL = 'http://pcapi-xiaotuxian-front-devtest.itheima.net/' const backURL = 'http://127.0.0.1:5173/paycallback' const redirectUrl = encodeURIComponent(backURL) const payUrl = `${baseURL}pay/aliPay?orderId=${route.query.id}&redirect=${redirectUrl}`
</script>
<template> <div class="xtx-pay-page"> <div class="container"> <div class="pay-info"> <span class="iconfont icon-queren"></span> <div class="tip"> <p>订单提交成功,请尽快完成支付</p> <p>支付还剩<span>{{ formatTime }}</span>,超时后将取消订单</p> </div> <div class="amount"> <span>应付总额:</span> <span>¥ {{ orderInfo.payMoney?.toFixed(2) }}</span> </div> </div> </div>
<div class="pay-type"> <p class="head">请选择以下支付方式付款</p> <div class="item"> <p>支付平台</p> <a class="btn wx" href="javascript:;"></a> <a class="btn alipay" :href="payUrl"></a> </div> <div class="item"> <a class="btn" href="javascript:;">招商银行</a> <a class="btn" href="javascript:;">工商银行</a> <a class="btn" href="javascript:;">建设银行</a> <a class="btn" href="javascript:;">农业银行</a> <a class="btn" href="javascript:;">交通银行</a> </div> </div> </div> </template>
<style scoped lang="scss"> .xtx-pay-page { margin-top: 20px; }
.pay-info {
background: #fff; display: flex; align-items: center; height: 240px; padding: 0 80px;
.icon { font-size: 80px; color: #1dc779; }
.tip { padding-left: 10px; flex: 1;
p { &:first-child { font-size: 20px; margin-bottom: 5px; }
&:last-child { color: #999; font-size: 16px; } } }
.amount { span { &:first-child { font-size: 16px; color: #999; }
&:last-child { color: $priceColor; font-size: 20px; } } } }
.pay-type { margin-top: 20px; background-color: #fff; padding-bottom: 70px;
p { line-height: 70px; height: 70px; padding-left: 30px; font-size: 16px;
&.head { border-bottom: 1px solid #f5f5f5; } }
.btn { width: 150px; height: 50px; border: 1px solid #e4e4e4; text-align: center; line-height: 48px; margin-left: 30px; color: #666666; display: inline-block;
&.active, &:hover { border-color: $xtxColor; }
&.alipay { background: url(https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/7b6b02396368c9314528c0bbd85a2e06.png) no-repeat center / contain; }
&.wx { background: url(https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/c66f98cff8649bd5ba722c2e8067c6ca.jpg) no-repeat center / contain; } } } </style>
|
返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| <script setup> import { getOrderAPI } from '@/apis/pay' import { onMounted, ref } from 'vue'; import { useRoute } from 'vue-router';
const orderInfo = ref({}) const route = useRoute()
const getOrderInfo = async () => { const res = await getOrderAPI(route.query.orderId) orderInfo.value = res.result }
onMounted(() => getOrderInfo()) </script>
<template> <div class="xtx-pay-page"> <div class="container"> <div class="pay-result"> <span class="iconfont icon-queren green" v-if="$route.query.payResult === 'true'"></span> <span class="iconfont icon-shanchu red" v-else></span> <p class="tit">支付{{ $route.query.payResult === 'true' ? '成功' : '失败' }}</p> <p class="tip">我们将尽快为您发货,收货期间请保持手机畅通</p> <p>支付方式:<span>支付宝</span></p> <p>支付金额:<span>¥{{ orderInfo.payMoney?.toFixed(2) }}</span></p> <div class="btn"> <el-button type="primary" style="margin-right: 20px;">查看订单</el-button> <el-button>进入首页</el-button> </div> <p class="alert"> <span class="iconfont icon-tip"></span> 温馨提示:小兔鲜儿不会以订单异常、系统升级为由要求您点击任何网址链接进行退款操作,谨防诈骗! </p> </div> </div> </div> </template>
<style scoped lang="scss"> .pay-result { padding: 100px 0; background: #fff; text-align: center; margin-top: 20px;
>.iconfont { font-size: 100px; }
.green { color: #1dc779; }
.red { color: $priceColor; }
.tit { font-size: 24px; }
.tip { color: #999; }
p { line-height: 40px; font-size: 16px; }
.btn { margin-top: 50px; }
.alert { font-size: 12px; color: #999; margin-top: 50px; } } </style>
|
会员开始页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| <script setup>
</script>
<template> <div class="container"> <div class="xtx-member-aside"> <div class="user-manage"> <h4>我的账户</h4> <div class="links"> <router-link to="/member">个人中心</router-link> </div> <h4>交易管理</h4> <div class="links"> <router-link to="/member/order">我的订单</router-link> </div> </div> </div> <div class="article"> <router-view /> </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>
|
UserInfo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
| <script setup> import { useUserStore } from '../../../stores/user' import { getLikeListAPI } from '../../../apis/user' import { onMounted, ref } from 'vue' import GoodsItem from '../../Home/components/GoodsItem.vue'
const userStore = useUserStore()
const likeList = ref([]) const getLikeList = async () => { const res = await getLikeListAPI({ limit: 4 }) likeList.value = res.result }
onMounted(() => getLikeList()) </script>
<template> <div class="home-overview"> <div class="user-meta"> <div class="avatar"><img :src="userStore.userInfo?.avatar" alt="" /></div> <h4>{{ userStore.userInfo?.account }}</h4> </div> <div class="item"> <a href="javascript:;"> <span class="iconfont icon-huiyuan"></span> <p>会员中心</p> </a> <a href="javascript:;"> <span class="iconfont icon-anquan"></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" :goods="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>
|
UserOrder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
| <script setup> import { onMounted, ref } from 'vue'; import { getUserOrderAPI } from '@/apis/order'
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 total = ref(0) const params = ref({ orderState: 0, page: 1, pageSize: 2 })
const getOrderList = async () => { const res = await getUserOrderAPI(params.value) orderList.value = res.result.items total.value = res.result.counts }
onMounted(() => getOrderList())
const tabChange = (type) => { params.value.orderState = type getOrderList() }
const pageChange = (page) => { console.log(page); params.value.page = page getOrderList() }
const formatPayState = (payState) => { const stateMap = { 1: '待付款', 2: '待发货', 3: '待收货', 4: '待评价', 5: '已完成', 6: '已取消' } return stateMap[payState] } </script>
<template> <div class="order-container"> <el-tabs @tab-change="tabChange"> <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">×{{ item.quantity }}</div> </li> </ul> </div> <div class="column state"> <p>{{ formatPayState(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.postFree?.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 :total="total" @current-change="pageChange" :page-size="params.pageSize" 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>
|