453 lines
9.5 KiB
Vue
453 lines
9.5 KiB
Vue
<template>
|
||
<div>
|
||
<joyful-selection-checkout-skeleton v-if="loading" />
|
||
<main v-else class="checkout-page">
|
||
<van-nav-bar title="确认订单" left-arrow @click-left="$router.back()" />
|
||
|
||
<section class="address-card" @click="$router.push({ path: '/joyful-selection/address', query: { from: 'checkout' } })">
|
||
<van-icon class="address-icon" name="location" />
|
||
<div class="address-info">
|
||
<h3>收货人:{{ chosenAddress.name }} <span>{{ maskTel(chosenAddress.tel) }}</span></h3>
|
||
<p>{{ chosenAddress.province }}{{ chosenAddress.city }}{{ chosenAddress.county }}</p>
|
||
<p>{{ chosenAddress.addressDetail }}</p>
|
||
</div>
|
||
<van-icon class="arrow-icon" name="arrow" />
|
||
</section>
|
||
|
||
<section class="delivery-card">
|
||
<div class="delivery-row">
|
||
<span>配送方式</span>
|
||
<b>快速配送 免邮</b>
|
||
</div>
|
||
<div class="delivery-row delivery-date">
|
||
<span>预计送达:</span>
|
||
<em>5月20日(周二)</em>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="goods-card">
|
||
<article v-for="item in visibleGoods" :key="item.cartId || item.id" class="order-item">
|
||
<van-image class="order-thumb" :src="item.image" fit="cover" />
|
||
<div class="order-info">
|
||
<h3>{{ item.name }}</h3>
|
||
<p>¥{{ item.price }}</p>
|
||
</div>
|
||
<span class="order-count">x{{ item.count || 1 }}</span>
|
||
</article>
|
||
<button v-if="showLoadMore" type="button" class="load-more" @click="showAllGoods = true">
|
||
加载更多
|
||
<van-icon name="arrow-down" />
|
||
</button>
|
||
</section>
|
||
|
||
<section class="remark-card">
|
||
<span>订单备注</span>
|
||
<input v-model="remark" type="text" placeholder="选填,给商家留言" />
|
||
</section>
|
||
|
||
<section class="amount-card">
|
||
<p><span>商品金额</span><b>¥{{ formatYuan(goodsAmount) }}</b></p>
|
||
<p><span>运费</span><b>¥0.00</b></p>
|
||
<p><span>优惠券</span><b class="muted">选择优惠券</b></p>
|
||
</section>
|
||
|
||
<van-submit-bar :price="goodsAmount" button-text="提交订单" @submit="onSubmit" />
|
||
</main>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { mapActions, mapState } from 'pinia'
|
||
import { postFunction } from '@/api/supplied-service'
|
||
import { useCartStore } from '@/store/cart'
|
||
import { useUserStore } from '@/store/user'
|
||
import { readAddresses, readChosenAddressId } from '../../address/address-data'
|
||
import { createOrderNo, savePaymentInfo } from '../payment/payment-data'
|
||
import JoyfulSelectionCheckoutSkeleton from '@/skeleton-view/joyful-selection/order/checkout/index.vue'
|
||
|
||
const CHECKOUT_ITEMS_KEY = 'joyful-selection-checkout-items'
|
||
|
||
export default {
|
||
name: 'JoyfulSelectionCheckout',
|
||
components: {
|
||
JoyfulSelectionCheckoutSkeleton
|
||
},
|
||
data: () => ({
|
||
loading: true,
|
||
remark: '',
|
||
goods: [],
|
||
showAllGoods: false,
|
||
chosenAddress: {},
|
||
submitting: false
|
||
}),
|
||
computed: {
|
||
...mapState(useCartStore, ['items']),
|
||
...mapState(useUserStore, ['supplierId', 'userId']),
|
||
visibleGoods() {
|
||
return this.showAllGoods ? this.goods : this.goods.slice(0, 3)
|
||
},
|
||
showLoadMore() {
|
||
return !this.showAllGoods && this.goods.length > 3
|
||
},
|
||
goodsAmount() {
|
||
return this.goods.reduce((total, item) => total + Math.round(Number(item.price) * 100) * Number(item.count || 1), 0)
|
||
}
|
||
},
|
||
created() {
|
||
this.loadCart()
|
||
this.goods = this.getCheckoutGoods()
|
||
this.loadAddress()
|
||
this.$nextTick(() => {
|
||
this.loading = false
|
||
})
|
||
},
|
||
activated() {
|
||
this.loadAddress()
|
||
},
|
||
methods: {
|
||
...mapActions(useCartStore, ['loadCart', 'removeItems']),
|
||
...mapActions(useUserStore, ['fetchBridgeToken']),
|
||
getCheckoutGoods() {
|
||
const storedItems = this.readCheckoutItems()
|
||
|
||
if (storedItems.length) {
|
||
return storedItems
|
||
}
|
||
|
||
if (this.$route.query.source === 'cart') {
|
||
return this.items
|
||
}
|
||
|
||
return []
|
||
},
|
||
readCheckoutItems() {
|
||
try {
|
||
return JSON.parse(sessionStorage.getItem(CHECKOUT_ITEMS_KEY) || '[]')
|
||
} catch (error) {
|
||
return []
|
||
}
|
||
},
|
||
formatYuan(price) {
|
||
return (Number(price || 0) / 100).toFixed(2)
|
||
},
|
||
loadAddress() {
|
||
const addresses = readAddresses()
|
||
const chosenId = readChosenAddressId()
|
||
this.chosenAddress = addresses.find(item => item.id === chosenId) || addresses[0] || {}
|
||
},
|
||
maskTel(tel = '') {
|
||
return tel.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
|
||
},
|
||
async onSubmit() {
|
||
if (this.submitting) {
|
||
return
|
||
}
|
||
|
||
this.submitting = true
|
||
try {
|
||
await this.fetchBridgeToken()
|
||
|
||
if (!this.supplierId || !this.userId) {
|
||
throw new Error('供应商授权信息不完整,请重新进入商城')
|
||
}
|
||
|
||
const orderNo = createOrderNo()
|
||
const data = await postFunction('order-create', {
|
||
supplierId: this.supplierId,
|
||
userId: this.userId,
|
||
orderNo,
|
||
amount: this.goodsAmount,
|
||
goods: this.goods
|
||
})
|
||
|
||
savePaymentInfo({
|
||
orderNo: data?.orderId || orderNo,
|
||
orderNumberId: data?.orderNumberId || '',
|
||
amount: this.goodsAmount,
|
||
receiverName: this.chosenAddress.name || '',
|
||
receiverTel: this.maskTel(this.chosenAddress.tel || ''),
|
||
goods: this.goods
|
||
})
|
||
|
||
this.clearSubmittedCartItems()
|
||
this.$router.push('/joyful-selection/payment')
|
||
} catch (error) {
|
||
console.warn('提交待支付订单失败', error)
|
||
this.$toast(error.message || '提交待支付订单失败')
|
||
} finally {
|
||
this.submitting = false
|
||
}
|
||
},
|
||
clearSubmittedCartItems() {
|
||
if (this.$route.query.source !== 'cart') {
|
||
return
|
||
}
|
||
|
||
const cartIds = this.goods.map(item => item.cartId).filter(Boolean)
|
||
|
||
if (cartIds.length) {
|
||
this.removeItems(cartIds)
|
||
}
|
||
|
||
sessionStorage.removeItem(CHECKOUT_ITEMS_KEY)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
.checkout-page {
|
||
min-height: 100vh;
|
||
padding: 46px 0 62px;
|
||
background: #f7f7f7;
|
||
}
|
||
|
||
.checkout-page ::v-deep .van-nav-bar {
|
||
position: fixed;
|
||
z-index: 20;
|
||
top: 0;
|
||
right: 0;
|
||
left: 0;
|
||
background: #fff;
|
||
box-shadow: 0 1px 0 rgba(0, 0, 0, .04);
|
||
}
|
||
|
||
.address-card,
|
||
.delivery-card,
|
||
.goods-card,
|
||
.remark-card,
|
||
.amount-card {
|
||
margin: 10px 12px 0;
|
||
border-radius: 8px;
|
||
background: #fff;
|
||
}
|
||
|
||
.address-card {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 14px 12px;
|
||
}
|
||
|
||
.address-icon {
|
||
flex: none;
|
||
margin-right: 10px;
|
||
color: #ff5b20;
|
||
font-size: 20px;
|
||
}
|
||
|
||
.address-info {
|
||
min-width: 0;
|
||
flex: 1;
|
||
}
|
||
|
||
.address-info h3 {
|
||
margin: 0 0 5px;
|
||
color: #222;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.address-info h3 span {
|
||
margin-left: 12px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.address-info p {
|
||
overflow: hidden;
|
||
margin: 2px 0 0;
|
||
color: #555;
|
||
font-size: 12px;
|
||
line-height: 17px;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.arrow-icon {
|
||
flex: none;
|
||
color: #999;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.delivery-card {
|
||
padding: 13px 14px;
|
||
}
|
||
|
||
.delivery-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
color: #333;
|
||
font-size: 13px;
|
||
line-height: 22px;
|
||
}
|
||
|
||
.delivery-row b {
|
||
color: #333;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.delivery-date {
|
||
justify-content: flex-start;
|
||
color: #999;
|
||
}
|
||
|
||
.delivery-date em {
|
||
margin-left: 8px;
|
||
color: #999;
|
||
font-style: normal;
|
||
}
|
||
|
||
.goods-card {
|
||
padding: 2px 14px;
|
||
}
|
||
|
||
.order-item {
|
||
display: flex;
|
||
align-items: center;
|
||
min-height: 86px;
|
||
padding: 12px 0;
|
||
}
|
||
|
||
.order-item + .order-item {
|
||
border-top: 1px solid #f4f4f4;
|
||
}
|
||
|
||
.load-more {
|
||
display: flex;
|
||
width: 100%;
|
||
height: 44px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 4px;
|
||
border: 0;
|
||
border-top: 1px solid #f4f4f4;
|
||
color: #ff5b20;
|
||
font-size: 13px;
|
||
background: #fff;
|
||
}
|
||
|
||
.load-more .van-icon {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.order-thumb {
|
||
width: 64px;
|
||
height: 64px;
|
||
flex: none;
|
||
overflow: hidden;
|
||
border-radius: 4px;
|
||
background: #f7f7f7;
|
||
}
|
||
|
||
.order-info {
|
||
min-width: 0;
|
||
flex: 1;
|
||
padding: 0 10px;
|
||
}
|
||
|
||
.order-info h3 {
|
||
display: -webkit-box;
|
||
overflow: hidden;
|
||
margin: 0 0 8px;
|
||
color: #222;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
line-height: 18px;
|
||
text-overflow: ellipsis;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
}
|
||
|
||
.order-info p {
|
||
margin: 0;
|
||
color: #333;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.order-count {
|
||
flex: none;
|
||
color: #222;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.remark-card {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 46px;
|
||
padding: 0 14px;
|
||
}
|
||
|
||
.remark-card span {
|
||
flex: none;
|
||
color: #333;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.remark-card input {
|
||
min-width: 0;
|
||
flex: 1;
|
||
margin-left: 16px;
|
||
border: 0;
|
||
outline: none;
|
||
color: #333;
|
||
font-size: 13px;
|
||
text-align: right;
|
||
background: transparent;
|
||
}
|
||
|
||
.remark-card input::placeholder {
|
||
color: #999;
|
||
}
|
||
|
||
.amount-card {
|
||
padding: 12px 14px;
|
||
}
|
||
|
||
.amount-card p {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin: 0;
|
||
color: #777;
|
||
font-size: 13px;
|
||
line-height: 28px;
|
||
}
|
||
|
||
.amount-card b {
|
||
color: #333;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.amount-card .muted {
|
||
color: #777;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.checkout-page ::v-deep .van-submit-bar {
|
||
bottom: 0;
|
||
height: 58px;
|
||
border-top: 1px solid #eee;
|
||
}
|
||
|
||
.checkout-page ::v-deep .van-submit-bar__bar {
|
||
height: 58px;
|
||
padding: 0 16px;
|
||
}
|
||
|
||
.checkout-page ::v-deep .van-submit-bar__price {
|
||
color: #ff5b20;
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.checkout-page ::v-deep .van-submit-bar__button {
|
||
width: 112px;
|
||
height: 40px;
|
||
border-radius: 22px;
|
||
background: #ff5b20;
|
||
border-color: #ff5b20;
|
||
font-weight: 700;
|
||
}
|
||
</style>
|