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

255 lines
6.2 KiB
Vue
Raw Normal View History

2025-03-08 08:09:31 +08:00
<script setup lang="ts">
import { useCartStore } from "@/pinia/stores/cart"
2025-03-17 08:30:57 +08:00
import { useWxStore } from "@/pinia/stores/wx"
2025-03-08 08:09:31 +08:00
import { storeToRefs } from "pinia"
import { showConfirmDialog } from "vant"
2025-03-17 08:30:57 +08:00
import { submitOrderApi } from "@/common/apis/shop"
import type { SubmitOrderRequestData, WxJsApiPreCreateResponse } from "@/common/apis/shop/type"
import { useRouter } from 'vue-router'
const router = useRouter()
2025-03-08 08:09:31 +08:00
const cartStore = useCartStore()
const { cartItems, totalPrice } = storeToRefs(cartStore)
2025-03-17 08:30:57 +08:00
const wxStore = useWxStore()
const { openid } = storeToRefs(wxStore)
2025-03-08 08:09:31 +08:00
const contact = ref("")
const remark = ref("")
const submitting = ref(false)
2025-03-17 08:30:57 +08:00
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,
2025-03-21 17:00:06 +08:00
package: paymentInfo.package.startsWith('prepay_id=')
? paymentInfo.package
: `prepay_id=${paymentInfo.package}`,
2025-03-17 08:30:57 +08:00
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();
}
});
}
2025-03-08 08:09:31 +08:00
async function handleSubmit() {
if (!cartItems.value.length) {
return showConfirmDialog({
title: "提示",
message: "请先选择商品后再结算"
})
}
2025-03-17 08:30:57 +08:00
if (!openid.value) {
2025-03-08 08:09:31 +08:00
return showConfirmDialog({
2025-03-17 08:30:57 +08:00
title: "登录提示",
message: "请从微信中打开"
2025-03-08 08:09:31 +08:00
})
}
submitting.value = true
try {
2025-03-17 08:30:57 +08:00
// 构造请求参数
const requestData: SubmitOrderRequestData = {
openid: openid.value, // 假设联系方式即为openid根据实际情况调整
goodsList: cartItems.value.map(item => ({
goodsId: item.product.id,
quantity: item.quantity
}))
}
// 调用提交订单接口
const { data } = await submitOrderApi(requestData)
2025-03-08 08:09:31 +08:00
2025-03-21 17:00:06 +08:00
// await showConfirmDialog({
// title: "提交成功",
// message: `订单号:${data.orderId},正在跳转支付...`
// })
2025-03-08 08:09:31 +08:00
2025-03-17 08:30:57 +08:00
// 调用微信支付
if (data.paymentInfo) {
await callWxJsApi(data.paymentInfo);
// 支付成功后跳转
2025-03-21 17:00:06 +08:00
router.push({
path: '/order-success',
query: {
orderId: data.orderId
}
});
2025-03-17 08:30:57 +08:00
} else {
throw new Error('无法获取支付信息');
}
2025-03-08 08:09:31 +08:00
// 清空购物车
cartStore.clearCart()
2025-03-17 08:30:57 +08:00
// 这里添加支付跳转逻辑根据paymentInfo处理
// 例如调用微信JSAPI支付等
} catch (error) {
if(error !== 'user_cancel') {
showConfirmDialog({
title: "支付失败",
message: error instanceof Error ? error.message : "支付流程中断"
});
}
2025-03-08 08:09:31 +08:00
} finally {
submitting.value = false
}
}
</script>
<template>
<div class="checkout-container">
2025-03-17 08:30:57 +08:00
<van-nav-bar title="结算页面" left-text="返回" left-arrow fixed @click-left="() => $router.go(-1)" />
2025-03-08 08:09:31 +08:00
<div class="content-wrapper">
<!-- 原有商品列表等代码保持不动 -->
<van-cell-group class="product-list">
2025-03-17 08:30:57 +08:00
<van-cell v-for="item in cartItems" :key="item.product.id" class="product-item">
2025-03-08 08:09:31 +08:00
<template #icon>
2025-03-17 08:30:57 +08:00
<van-image :src="item.product.image" width="60" height="60" class="product-image" />
2025-03-08 08:09:31 +08:00
</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">
2025-03-24 10:26:34 +08:00
<!-- <van-field v-model="contact" label="联系方式" placeholder="请输入手机号" :rules="[
2025-03-17 08:30:57 +08:00
{ required: true, message: '请填写联系方式' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' },
]" />
2025-03-24 10:26:34 +08:00
<van-field v-model="remark" label="备注" type="textarea" placeholder="选填,可备注特殊需求" rows="2" autosize /> -->
2025-03-08 08:09:31 +08:00
</van-cell-group>
<!-- 提交订单栏 -->
<div class="submit-bar">
<div class="total-price">
合计¥{{ totalPrice.toFixed(2) }}
</div>
2025-03-17 08:30:57 +08:00
<van-button type="primary" size="large" :loading="submitting" loading-text="提交中..." @click="handleSubmit">
2025-03-08 08:09:31 +08:00
提交订单
</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 {
2025-03-17 08:30:57 +08:00
padding-top: 46px;
/* 导航栏高度 */
2025-03-08 08:09:31 +08:00
}
.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>