Files
points-mall/supplied-h5/src/view/joyful-selection/order/checkout/index.vue
T

453 lines
9.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>快速配送&nbsp;&nbsp;免邮</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>