shop-web/src/pages/product/components/checkout.vue

268 lines
6.6 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.

<script setup lang="ts">
import { useCartStore } from "@/pinia/stores/cart"
import { useWxStore } from "@/pinia/stores/wx"
import { storeToRefs } from "pinia"
import { showConfirmDialog } from "vant"
import { submitOrderApi } from "@/common/apis/shop"
import type { SubmitOrderRequestData, WxJsApiPreCreateResponse } from "@/common/apis/shop/type"
import { useRouter } from 'vue-router'
const router = useRouter()
const cartStore = useCartStore()
const { cartItems, totalPrice } = storeToRefs(cartStore)
const wxStore = useWxStore()
const { openid, balance } = storeToRefs(wxStore)
const selectedPayment = ref<'wechat' | 'balance'>('wechat')
const contact = ref("")
const remark = ref("")
const submitting = ref(false)
function callWxJsApi(paymentInfo: WxJsApiPreCreateResponse) {
return new Promise((resolve, reject) => {
function onBridgeReady() {
(window as any).WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
appId: paymentInfo.appId,
timeStamp: paymentInfo.timeStamp,
nonceStr: paymentInfo.nonceStr,
package: paymentInfo.package.startsWith('prepay_id=')
? paymentInfo.package
: `prepay_id=${paymentInfo.package}`,
signType: paymentInfo.signType,
paySign: paymentInfo.paySign
},
(res: { err_msg: string }) => {
if (res.err_msg === "get_brand_wcpay_request:ok") {
resolve(true);
} else {
reject(new Error('支付未完成'));
}
}
);
}
if (typeof (window as any).WeixinJSBridge === 'undefined') {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else {
onBridgeReady();
}
});
}
async function handleSubmit() {
if (!cartItems.value.length) {
return showConfirmDialog({
title: "提示",
message: "请先选择商品后再结算"
})
}
if (!openid.value) {
return showConfirmDialog({
title: "登录提示",
message: "请从微信中打开"
})
}
// 移除原有的支付方式选择弹窗代码
submitting.value = true
try {
const requestData: SubmitOrderRequestData = {
openid: openid.value,
userid: wxStore.userid,
corpid: wxStore.corpid,
goodsList: cartItems.value.map(item => ({
goodsId: item.product.id,
quantity: item.quantity
})),
paymentType: selectedPayment.value
}
const { code, data } = await submitOrderApi(requestData)
if (code !== 0) {
throw new Error("订单提交失败")
}
if (selectedPayment.value === 'wechat') {
if (data.paymentInfo) {
await callWxJsApi(data.paymentInfo);
}
} else {
// 余额支付成功处理
wxStore.setBalance(data.newBalance || 0);
try {
await showConfirmDialog({
title: "支付成功",
message: `余额支付成功,剩余余额:¥${data.newBalance?.toFixed(2)}`
})
} catch (error) {
}
}
router.push({
path: '/order-success',
query: { orderId: data.orderId }
});
cartStore.clearCart()
} catch (error) {
if (error !== 'user_cancel') {
showConfirmDialog({
title: "支付失败",
message: error instanceof Error ? error.message : "支付流程中断"
});
}
} finally {
submitting.value = false
}
}
</script>
<template>
<div class="checkout-container">
<van-nav-bar title="结算页面" left-text="返回" left-arrow fixed @click-left="() => $router.go(-1)" />
<div class="content-wrapper">
<!-- 原有商品列表等代码保持不动 -->
<van-cell-group class="product-list">
<van-cell v-for="item in cartItems" :key="item.product.id" class="product-item">
<template #icon>
<van-image :src="item.product.image" width="60" height="60" class="product-image" />
</template>
<div class="product-info">
<div class="product-name van-ellipsis">
{{ item.product.name }}
</div>
<div class="price-row">
<span class="product-price">
¥{{ item.product.price.toFixed(2) }}
</span>
<span class="quantity">
×{{ item.quantity }}
</span>
</div>
</div>
</van-cell>
</van-cell-group>
<van-cell-group class="contact-form">
<van-field label="支付方式" :model-value="selectedPayment" readonly>
<template #input>
<van-radio-group v-model="selectedPayment" direction="horizontal">
<van-radio name="wechat" v-if="!wxStore.corpid">
<van-icon name="wechat" class="method-icon" />
微信支付
</van-radio>
<van-radio name="balance" :disabled="balance < totalPrice">
<van-icon name="balance-o" class="method-icon" />
余额支付(当前:¥{{ balance.toFixed(2) }}
</van-radio>
</van-radio-group>
</template>
</van-field>
</van-cell-group>
<!-- 提交订单栏 -->
<div class="submit-bar">
<div class="total-price">
合计:¥{{ totalPrice.toFixed(2) }}
</div>
<van-button type="primary" size="large" :loading="submitting" loading-text="提交中..." @click="handleSubmit">
提交订单
</van-button>
</div>
</div>
</div>
</template>
<style scoped>
.van-nav-bar {
position: fixed;
top: 0;
width: 100%;
z-index: 999;
background: #fff;
border-bottom: 1px solid #ebedf0;
}
.content-wrapper {
padding-top: 46px;
/* 导航栏高度 */
}
.checkout-container {
padding: 12px 16px 80px;
}
.product-list {
margin-bottom: 20px;
}
.product-item {
align-items: flex-start;
}
.product-image {
margin-right: 12px;
border-radius: 4px;
}
.product-info {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 60px;
}
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-price {
color: #e95d5d;
font-weight: bold;
font-size: 14px;
}
.quantity {
color: #666;
font-size: 13px;
}
.method-icon {
font-size: 24px;
margin-right: 10px;
}
.method-label {
font-size: 15px;
}
.submit-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 10px 16px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
}
.total-price {
font-size: 16px;
color: #e95d5d;
font-weight: bold;
}
</style>