You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

748 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="container">
<!-- 顶部标题 -->
<view class="header">
<view class="header-title">管理您的账户信息和服务</view>
</view>
<!-- 用户信息卡片 -->
<view class="user-card">
<view class="user-info">
<div class="avatar"></div>
<!-- <image
class="avatar"
src="/static/imgs/avatar.png"
mode="aspectFill"
></image> -->
<view class="user-details">
<view class="username">
张三
<u-icon
name="checkmark-circle-fill"
color="#ffa500"
size="16"
></u-icon>
</view>
<view class="user-id">用户ID: U12345678</view>
<view class="membership">
会员等级: 年会员 (有效期至: 2026-05-15)
</view>
</view>
</view>
<view class="edit-btn" @click="editProfile">
<text>点击编辑资料</text>
</view>
</view>
<!-- 账户余额 -->
<view class="balance-card">
<div>
<view class="balance-header">
<view class="balance-label">账户余额</view>
</view>
<view class="balance-amount">¥50.00</view>
</div>
<uv-button
type="primary"
color="#fff"
customStyle="color:#000;width:90px"
shape="circle"
@click="recharge"
>
充值
</uv-button>
</view>
<!-- 会员套餐 -->
<view class="membership-section">
<view class="membership-grid">
<view
class="membership-item"
:class="{ active: selectedPlan === 'monthly' }"
@click="selectPlan('monthly')"
>
<view class="tag">新人特惠</view>
<view class="plan-name">包月会员</view>
<view class="plan-price">¥120</view>
</view>
<view
class="membership-item"
:class="{ active: selectedPlan === 'season' }"
@click="selectPlan('season')"
>
<view class="tag">最受欢迎</view>
<view class="plan-name">包季会员</view>
<view class="plan-price">¥300</view>
<view class="plan-discount">立减¥60</view>
</view>
<view
class="membership-item"
:class="{ active: selectedPlan === 'yearly' }"
@click="selectPlan('yearly')"
>
<view class="tag">年度首享</view>
<view class="plan-name">包年会员</view>
<view class="plan-price">¥1000</view>
<view class="plan-discount">立减¥440</view>
</view>
</view>
</view>
<!-- 支付方式 -->
<view class="payment-section">
<!-- 微信支付 -->
<view
class="payment-item"
:class="{ active: selectedPayment === 'wechat' }"
@click="selectPayment('wechat')"
>
<view class="payment-info">
<uv-icon name="weixin-fill" color="#07c160" size="24"></uv-icon>
<text class="payment-name">微信支付</text>
</view>
<uv-icon
name="checkmark-circle-fill"
:color="selectedPayment === 'wechat' ? '#ff3333' : '#ccc'"
size="20"
></uv-icon>
</view>
<!-- 支付宝支付 - 仅在H5环境显示 -->
<view
v-if="showAlipay"
class="payment-item"
:class="{ active: selectedPayment === 'alipay' }"
@click="selectPayment('alipay')"
>
<view class="payment-info">
<uv-icon
name="alipay-circle-fill"
color="#1677ff"
size="24"
></uv-icon>
<text class="payment-name">支付宝</text>
</view>
<uv-icon
name="checkmark-circle-fill"
:color="selectedPayment === 'alipay' ? '#ff3333' : '#ccc'"
size="20"
></uv-icon>
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-section">
<uv-button
type="primary"
size="large"
@click="confirmPayment"
:loading="paying"
>
确认支付
</uv-button>
<view class="agreement">
购买前请仔细阅读
<text class="link" @click="showAgreement"></text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
import HeaderCustom from "/components/HeaderCustom/HeaderCustom.vue";
import { useAuthStore, useUserStore } from "@/store";
import RPC from "@/utils/request";
const modal = ref(null);
const authStore = useAuthStore();
const userStore = useUserStore();
// 响应式数据
const selectedPlan = ref("season"); // 默认选中包季会员
const selectedPayment = ref("wechat"); // 默认选中微信支付
const paying = ref(false);
const showAlipay = ref(true); // 控制是否显示支付宝选项
// 选择会员套餐
const selectPlan = (plan) => {
selectedPlan.value = plan;
};
// 选择支付方式
const selectPayment = (payment) => {
selectedPayment.value = payment;
};
// 编辑资料
const editProfile = () => {
uni.navigateTo({
url: "/pagesA/edit-profile/edit-profile",
});
};
// 充值
const recharge = () => {
uni.navigateTo({
url: "/pagesA/recharge/recharge",
});
};
// 获取套餐价格
const getPlanPrice = (plan) => {
const prices = {
monthly: 120,
season: 300,
yearly: 1000,
};
return prices[plan] || 0;
};
// 获取套餐名称
const getPlanName = (plan) => {
const names = {
monthly: "包月会员",
season: "包季会员",
yearly: "包年会员",
};
return names[plan] || "";
};
// 创建订单
const createOrder = async (params) => {
try {
// 调用后端创建订单API
const result = await RPC.post("/api/order/create", params);
return result.data;
} catch (error) {
console.error("创建订单失败:", error);
throw error;
}
};
// 微信支付
const wechatPay = async (orderInfo) => {
// #ifdef MP-WEIXIN
// 微信小程序支付
try {
const paymentResult = await uni.requestPayment({
provider: "wxpay",
timeStamp: orderInfo.timeStamp,
nonceStr: orderInfo.nonceStr,
package: orderInfo.package,
signType: orderInfo.signType,
paySign: orderInfo.paySign,
});
console.log("微信支付成功:", paymentResult);
return { success: true, data: paymentResult };
} catch (error) {
console.error("微信支付失败:", error);
if (error.errMsg === "requestPayment:fail cancel") {
throw new Error("用户取消支付");
}
throw new Error("微信支付失败");
}
// #endif
// #ifdef H5
// H5微信支付
if (typeof WeixinJSBridge !== "undefined") {
return new Promise((resolve, reject) => {
WeixinJSBridge.invoke(
"getBrandWCPayRequest",
{
appId: orderInfo.appId,
timeStamp: orderInfo.timeStamp,
nonceStr: orderInfo.nonceStr,
package: orderInfo.package,
signType: orderInfo.signType,
paySign: orderInfo.paySign,
},
(res) => {
if (res.err_msg === "get_brand_wcpay_request:ok") {
resolve({ success: true, data: res });
} else if (res.err_msg === "get_brand_wcpay_request:cancel") {
reject(new Error("用户取消支付"));
} else {
reject(new Error("微信支付失败"));
}
}
);
});
} else {
// 不在微信环境,跳转到支付页面
const payUrl = orderInfo.mwebUrl; // 后端返回的H5支付链接
window.location.href = payUrl;
return { success: true, redirect: true };
}
// #endif
};
// 支付宝支付仅H5环境
const alipayPay = async (orderInfo) => {
// #ifdef H5
// H5支付宝支付
const payUrl = orderInfo.payUrl; // 后端返回的支付宝H5支付链接
window.location.href = payUrl;
return { success: true, redirect: true };
// #endif
};
// 确认支付
const confirmPayment = async () => {
if (!selectedPlan.value) {
uni.showToast({
title: "请选择会员套餐",
icon: "none",
});
return;
}
if (!selectedPayment.value) {
uni.showToast({
title: "请选择支付方式",
icon: "none",
});
return;
}
paying.value = true;
try {
// 1. 创建订单参数
const orderParams = {
plan: selectedPlan.value,
planName: getPlanName(selectedPlan.value),
amount: getPlanPrice(selectedPlan.value),
paymentMethod: selectedPayment.value,
userId: userStore.userInfo?.id || "mock_user_id",
// #ifdef MP-WEIXIN
platform: "mp-weixin",
// #endif
// #ifdef H5
platform: "h5",
// #endif
};
uni.showLoading({
title: "正在创建订单...",
});
// 2. 创建订单
// const orderResult = await createOrder(orderParams);
// 模拟创建订单返回的数据
const orderResult = {
orderId: "ORDER_" + Date.now(),
amount: orderParams.amount,
// 微信支付参数(实际应该由后端返回)
wechatPayInfo: {
appId: "wx1234567890",
timeStamp: String(Date.now()),
nonceStr: "random_string_" + Date.now(),
package: "prepay_id=wx_prepay_id_123456",
signType: "RSA",
paySign: "mock_pay_sign_123456",
// H5支付链接
mwebUrl:
"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx_prepay_id_123456",
},
// 支付宝支付参数(实际应该由后端返回)
alipayInfo: {
orderString: "mock_alipay_order_string_123456",
payUrl: "https://openapi.alipay.com/gateway.do?mock_pay_url",
},
};
uni.hideLoading();
// 3. 根据支付方式调用对应的支付方法
let paymentResult;
if (selectedPayment.value === "wechat") {
uni.showLoading({
title: "正在跳转微信支付...",
});
paymentResult = await wechatPay(orderResult.wechatPayInfo);
} else if (selectedPayment.value === "alipay") {
uni.showLoading({
title: "正在跳转支付宝...",
});
paymentResult = await alipayPay(orderResult.alipayInfo);
}
uni.hideLoading();
// 4. 处理支付结果
if (paymentResult.success) {
if (paymentResult.redirect) {
// H5跳转支付不需要额外处理
uni.showToast({
title: "正在跳转支付页面...",
icon: "loading",
duration: 2000,
});
} else {
// 支付成功
uni.showToast({
title: "支付成功",
icon: "success",
});
// 延迟跳转到订单页面或刷新用户信息
setTimeout(() => {
// 可以跳转到订单详情页面
uni.navigateTo({
url: `/pages/order-detail/order-detail?orderId=${orderResult.orderId}`,
});
// 或者刷新当前页面的用户信息
// loadUserInfo();
}, 1500);
}
}
} catch (error) {
uni.hideLoading();
console.error("支付失败:", error);
let errorMessage = "支付失败,请重试";
if (error.message === "用户取消支付") {
errorMessage = "支付已取消";
} else if (error.message) {
errorMessage = error.message;
}
uni.showToast({
title: errorMessage,
icon: "none",
});
} finally {
paying.value = false;
}
};
// 显示协议
const showAgreement = () => {
uni.navigateTo({
url: "/pagesA/user-agreement/user-agreement",
});
};
// 检查支付结果用于H5支付回调
const checkPaymentResult = async (orderId) => {
try {
// 调用后端接口检查支付结果
// const result = await RPC.get(`/api/order/check-payment/${orderId}`);
// 模拟检查结果
const result = {
success: true,
status: "paid", // paid, pending, failed
};
if (result.status === "paid") {
uni.showToast({
title: "支付成功",
icon: "success",
});
// 更新用户信息或跳转到成功页面
setTimeout(() => {
uni.navigateTo({
url: `/pages/order-detail/order-detail?orderId=${orderId}`,
});
}, 1500);
} else if (result.status === "failed") {
uni.showToast({
title: "支付失败",
icon: "none",
});
} else {
uni.showToast({
title: "支付处理中,请稍后查看订单状态",
icon: "none",
});
}
} catch (error) {
console.error("检查支付结果失败:", error);
}
};
// 页面显示时检查是否有支付回调参数
const handlePaymentCallback = () => {
// #ifdef H5
const urlParams = new URLSearchParams(window.location.search);
const orderId = urlParams.get("orderId");
const payResult = urlParams.get("payResult");
if (orderId && payResult) {
// 清理URL参数
const url = new URL(window.location);
url.searchParams.delete("orderId");
url.searchParams.delete("payResult");
window.history.replaceState({}, document.title, url.pathname + url.search);
// 检查支付结果
if (payResult === "success") {
checkPaymentResult(orderId);
} else {
uni.showToast({
title: "支付失败或取消",
icon: "none",
});
}
}
// #endif
};
// 检查当前环境并设置支付选项
const checkEnvironment = () => {
// #ifdef MP-WEIXIN
// 微信小程序环境,隐藏支付宝选项,默认选择微信支付
showAlipay.value = false;
selectedPayment.value = "wechat";
// #endif
// #ifdef H5
// H5环境显示所有支付选项
showAlipay.value = true;
// 如果在微信浏览器内,默认选择微信支付
const ua = navigator.userAgent.toLowerCase();
const isWechat = ua.indexOf("micromessenger") !== -1;
selectedPayment.value = isWechat ? "wechat" : "alipay";
// #endif
};
// 页面挂载时处理支付回调和环境检查
onMounted(() => {
checkEnvironment();
handlePaymentCallback();
});
// 页面显示时也检查支付回调(从其他页面返回时)
onShow(() => {
handlePaymentCallback();
});
</script>
<style lang="scss" scoped>
.container {
font-size: 14px;
background: linear-gradient(180deg, #7b9cdb 0%, #f8f8f8 40%);
padding-bottom: 120px;
}
.header {
padding: 20px 16px 16px;
.header-title {
color: white;
font-size: 18px;
font-weight: 500;
}
}
.user-card {
margin: 0 16px 16px;
background: white;
border-radius: 12px;
padding: 16px;
position: relative;
.user-info {
display: flex;
align-items: center;
// margin-bottom: 12px;
.avatar {
width: 60px;
height: 60px;
border-radius: 50%;
margin-right: 12px;
background-color: skyblue;
overflow: hidden;
.img {
width: 100%;
height: 100%;
}
}
.user-details {
flex: 1;
.username {
display: flex;
align-items: center;
gap: 4px;
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 4px;
}
.user-id {
font-size: 12px;
color: #999;
margin-bottom: 2px;
}
.membership {
font-size: 12px;
color: #999;
}
}
}
.edit-btn {
text-align: right;
color: #4285f4;
font-size: 14px;
position: absolute;
top: 10px;
right: 20px;
}
}
.balance-card {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 16px 16px;
background: linear-gradient(135deg, #4285f4 0%, #6fa8f5 100%);
border-radius: 12px;
padding: 16px;
color: white;
.balance-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
.balance-label {
font-size: 14px;
opacity: 0.9;
}
}
.balance-amount {
font-size: 28px;
font-weight: bold;
}
}
.membership-section {
margin: 0 16px 16px;
.membership-grid {
display: flex;
gap: 8px;
.membership-item {
flex: 1;
background: white;
border-radius: 12px;
padding: 16px 12px;
text-align: center;
position: relative;
border: 2px solid transparent;
transition: all 0.3s;
overflow: hidden;
&.active {
border-color: #4285f4;
background-color: #f0f7ff;
}
.tag {
position: absolute;
top: 0px;
left: 0;
padding: 2px 8px;
border-radius: 0 0 10px 0;
font-size: 10px;
color: white;
background: #ff3333;
}
.plan-name {
font-size: 14px;
color: #333;
margin: 12px 0 8px;
}
.plan-price {
font-size: 20px;
font-weight: bold;
color: #ff3333;
margin-bottom: 4px;
}
.plan-discount {
font-size: 12px;
color: #999;
}
}
}
}
.payment-section {
margin: 0 16px 24px;
.payment-item {
display: flex;
align-items: center;
justify-content: space-between;
background: white;
border-radius: 12px;
padding: 16px;
margin-bottom: 8px;
border: 1px solid #e5e5e5;
transition: all 0.3s;
&.active {
border-color: #4285f4;
background-color: #f0f7ff;
}
&:last-child {
margin-bottom: 0;
}
.payment-info {
display: flex;
align-items: center;
gap: 12px;
.payment-name {
font-size: 14px;
color: #333;
}
}
}
}
.bottom-section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
padding: 16px;
border-top: 1px solid #e5e5e5;
.agreement {
text-align: center;
font-size: 12px;
color: #999;
margin-top: 12px;
.link {
color: #4285f4;
}
}
}
</style>