|
@@ -0,0 +1,379 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <view class="level-container">
|
|
|
|
|
+ <!-- 顶部会员信息区域 -->
|
|
|
|
|
+ <view class="vip-info">
|
|
|
|
|
+ <view class="flex-between vip-box">
|
|
|
|
|
+ <view class="flex_2">
|
|
|
|
|
+ <view class="flex-between">
|
|
|
|
|
+ <view class="vip-text">
|
|
|
|
|
+ <view class="vip-title">SVIP会员</view>
|
|
|
|
|
+ <view class="vip-desc"
|
|
|
|
|
+ >会员等级 {{ appStore?.$userInfo?.level || 0 }}</view
|
|
|
|
|
+ >
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="point-info">
|
|
|
|
|
+ <text class="point-text">{{
|
|
|
|
|
+ appStore?.$userInfo?.integral || 0
|
|
|
|
|
+ }}</text>
|
|
|
|
|
+ <text class="point-desc">会员贝币</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view>
|
|
|
|
|
+ <view class="growth-info">
|
|
|
|
|
+ <text>成长值 {{ appStore?.$userInfo?.experience || 0 }}</text>
|
|
|
|
|
+ <text class="growth-progress">
|
|
|
|
|
+ {{ appStore?.$userInfo?.experience || 0 }}/{{
|
|
|
|
|
+ appStore?.$userInfo?.experienceCount || 0
|
|
|
|
|
+ }}
|
|
|
|
|
+ </text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <up-line-progress
|
|
|
|
|
+ :percentage="percentExperience"
|
|
|
|
|
+ activeColor="#d6c3a3"
|
|
|
|
|
+ inactiveColor="#808080"
|
|
|
|
|
+ height="6"
|
|
|
|
|
+ :showText="false"
|
|
|
|
|
+ ></up-line-progress>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- SVIP尊享权益 -->
|
|
|
|
|
+ <view class="vip-benefit">
|
|
|
|
|
+ <view class="task-title">SVIP尊享权益</view>
|
|
|
|
|
+ <view class="task-item" v-for="(task, index) in taskList" :key="index">
|
|
|
|
|
+ <image class="task-icon" :src="task.icon"></image>
|
|
|
|
|
+ <view class="task-desc flex-center-between flex_1">
|
|
|
|
|
+ <text class="task-name">{{ task.name }}</text>
|
|
|
|
|
+ <text class="task-reward flex_1">{{ task.reward }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- SVIP购买按钮(仅非SVIP用户显示) -->
|
|
|
|
|
+ <view class="svip-price" v-if="!isSvip">
|
|
|
|
|
+ <view class="flex-center" @click="handlePayOpen">
|
|
|
|
|
+ <view class="svip-price-btn">
|
|
|
|
|
+ <text class="svip-price-name">年费会员¥</text>
|
|
|
|
|
+ <text class="svip-price-num">{{ totalPrice }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <WechatPayment ref="wechatPaymentRef" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup>
|
|
|
|
|
+import { ref, computed } from "vue";
|
|
|
|
|
+import { useAppStore } from "@/stores/app";
|
|
|
|
|
+import { svipPrice, svipsaveAPI } from "@/api/svip";
|
|
|
|
|
+import { getUserInfo } from "@/api/user";
|
|
|
|
|
+// 引入封装的微信支付组件(替换原paymentCommon)
|
|
|
|
|
+import WechatPayment from "@/components/payment/WechatPayment.vue";
|
|
|
|
|
+// 引入后端接口(按需补充:如“创建会员订单”“标记SVIP身份”的接口)
|
|
|
|
|
+// import { createVipOrder, markUserSvip } from "@/api/vip";
|
|
|
|
|
+import { onLoad } from "@dcloudio/uni-app";
|
|
|
|
|
+
|
|
|
|
|
+// 状态管理
|
|
|
|
|
+const appStore = useAppStore();
|
|
|
|
|
+const wechatPaymentRef = ref(null); // 微信支付组件引用
|
|
|
|
|
+const totalPrice = ref(0); // 会员价格(单位:元,从接口获取)
|
|
|
|
|
+const orderId = ref(""); // 后端生成的会员订单ID(用于后续标记SVIP)
|
|
|
|
|
+
|
|
|
|
|
+// 计算是否为SVIP用户
|
|
|
|
|
+const isSvip = computed(() => {
|
|
|
|
|
+ return appStore.isLogin && !!appStore?.$userInfo?.svip;
|
|
|
|
|
+});
|
|
|
|
|
+// 获取用户信息
|
|
|
|
|
+async function fetchUserInfo() {
|
|
|
|
|
+ const { data } = await getUserInfo();
|
|
|
|
|
+ appStore.UPDATE_USERINFO(data);
|
|
|
|
|
+}
|
|
|
|
|
+// 会员权益列表
|
|
|
|
|
+const taskList = ref([
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "每日福利",
|
|
|
|
|
+ reward: "200成长值",
|
|
|
|
|
+ icon: "https://sb-admin.oss-cn-shenzhen.aliyuncs.com/shuibei-mini/svip/%E8%B7%AF%E5%BE%84733%403x.png",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "回收特权",
|
|
|
|
|
+ reward: "回收价+0.3/克",
|
|
|
|
|
+ icon: "https://sb-admin.oss-cn-shenzhen.aliyuncs.com/shuibei-mini/svip/huishoutequan.png",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "整点秒杀",
|
|
|
|
|
+ reward: "抄底补贴,限时抢购",
|
|
|
|
|
+ icon: "https://sb-admin.oss-cn-shenzhen.aliyuncs.com/shuibei-mini/svip/zhengdianmiaosha.png",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "超级工具",
|
|
|
|
|
+ reward: "自动回收,金价预警使用权限",
|
|
|
|
|
+ icon: "https://sb-admin.oss-cn-shenzhen.aliyuncs.com/shuibei-mini/svip/chaojigongju.png",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "邮费减半",
|
|
|
|
|
+ reward: "邮费全部五折",
|
|
|
|
|
+ icon: "https://sb-admin.oss-cn-shenzhen.aliyuncs.com/shuibei-mini/svip/youfeijianban.png",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "专属服务群",
|
|
|
|
|
+ reward: "专属福利,优先传达",
|
|
|
|
|
+ icon: "https://sb-admin.oss-cn-shenzhen.aliyuncs.com/shuibei-mini/svip/zhuanshufuli.png",
|
|
|
|
|
+ },
|
|
|
|
|
+]);
|
|
|
|
|
+
|
|
|
|
|
+// 成长值进度百分比
|
|
|
|
|
+const percentExperience = computed(() => {
|
|
|
|
|
+ if (!appStore.isLogin) return 0;
|
|
|
|
|
+ const current = appStore.$userInfo.experience || 0;
|
|
|
|
|
+ const target = appStore.$userInfo.experienceCount || 1; // 避免除以0
|
|
|
|
|
+ const percent = Math.floor((current / target) * 100);
|
|
|
|
|
+ return percent > 100 ? 100 : percent;
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 页面加载时获取会员价格
|
|
|
|
|
+onLoad(() => {
|
|
|
|
|
+ getSvipPrice();
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取SVIP会员价格(单位:元)
|
|
|
|
|
+ */
|
|
|
|
|
+const getSvipPrice = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await svipPrice();
|
|
|
|
|
+ totalPrice.value = Number(res.data) || 99; // 默认99元,防止接口返回异常
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ totalPrice.value = 99; // 兜底价格
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 点击“开通会员”触发:发起微信支付
|
|
|
|
|
+ */
|
|
|
|
|
+const handlePayOpen = async () => {
|
|
|
|
|
+ // 1. 前置校验:是否登录、是否已为SVIP
|
|
|
|
|
+ if (!appStore.isLogin) {
|
|
|
|
|
+ uni.showToast({ title: "未登录", icon: "none" });
|
|
|
|
|
+ console.log("未登录");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isSvip.value) {
|
|
|
|
|
+ uni.showToast({ title: "已经是svip会员", icon: "none" });
|
|
|
|
|
+ console.log("已经是svip会员");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log("没有openId:", appStore?.$userInfo);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 校验用户openId(微信支付必需)
|
|
|
|
|
+ const userOpenId = appStore?.$userInfo?.openId; // 假设用户信息中存储了openId
|
|
|
|
|
+ if (!userOpenId) {
|
|
|
|
|
+ uni.showToast({ title: "用户openId获取失败", icon: "none" });
|
|
|
|
|
+ console.log("没有openId:");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 调用微信支付组件的支付方法
|
|
|
|
|
+ wechatPaymentRef.value.createUniPay({
|
|
|
|
|
+ // 支付金额:元转分(微信支付要求单位为“分”)
|
|
|
|
|
+ amount: Math.round(totalPrice.value * 100),
|
|
|
|
|
+ description: `SVIP年费会员(¥${totalPrice.value})`, // 商品描述(显示在微信支付账单中)
|
|
|
|
|
+ openId: userOpenId, // 微信支付必需:用户的微信openId
|
|
|
|
|
+ orderPrefix: "SVIP", // 订单号前缀(与公共组件generateCustomId匹配)
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 回调1:支付订单创建成功(用户尚未支付)
|
|
|
|
|
+ * 作用:记录后端生成的会员订单ID,用于后续支付成功后标记SVIP
|
|
|
|
|
+ */
|
|
|
|
|
+ onUniPayCreate: async (payRes) => {
|
|
|
|
|
+ console.log("会员支付订单创建成功:", payRes);
|
|
|
|
|
+ try {
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error("创建会员订单异常:", err);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 回调2:支付成功(核心逻辑)
|
|
|
|
|
+ * 作用:通知后端标记用户为SVIP,刷新用户状态
|
|
|
|
|
+ */
|
|
|
|
|
+ onUniPaySuccess: async (payStatusRes) => {
|
|
|
|
|
+ console.log("SVIP会员支付成功:", payStatusRes);
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await svipsaveAPI({
|
|
|
|
|
+ orderId: payStatusRes.out_trade_no,
|
|
|
|
|
+ price: totalPrice.value,
|
|
|
|
|
+ payType: "weixin",
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ appStore.USERINFO();
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error("标记SVIP身份异常:", err);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 回调3:用户取消支付
|
|
|
|
|
+ */
|
|
|
|
|
+ onUniPayCancel: () => {
|
|
|
|
|
+ console.log("用户取消SVIP支付");
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 回调4:支付失败(如网络异常、余额不足等)
|
|
|
|
|
+ */
|
|
|
|
|
+ onUniPayFail: (err) => {
|
|
|
|
|
+ const errMsg = err.message || "支付失败,请重新尝试";
|
|
|
|
|
+
|
|
|
|
|
+ console.error("SVIP会员支付失败:", err);
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+.level-container {
|
|
|
|
|
+ color: black;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ top: -130rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 顶部会员信息 */
|
|
|
|
|
+.vip-info {
|
|
|
|
|
+ color: #dfd7bc;
|
|
|
|
|
+ margin-bottom: -25rpx;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background-image: url("https://sb-admin.oss-cn-shenzhen.aliyuncs.com/shuibei-mini/svip/svip-bg.jpg");
|
|
|
|
|
+ background-size: 100%;
|
|
|
|
|
+ background-repeat: no-repeat;
|
|
|
|
|
+ height: 450rpx;
|
|
|
|
|
+
|
|
|
|
|
+ .vip-box {
|
|
|
|
|
+ margin: 200rpx 90rpx 100rpx 265rpx;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+.vip-avatar {
|
|
|
|
|
+ width: 151rpx;
|
|
|
|
|
+ height: 161rpx;
|
|
|
|
|
+ margin-right: 70rpx;
|
|
|
|
|
+ margin-top: 50rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.vip-title {
|
|
|
|
|
+ font-size: 36rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ margin-bottom: 8rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.vip-desc {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ margin-bottom: 12rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.growth-info {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-top: 20rpx;
|
|
|
|
|
+ margin-bottom: 5rpx;
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.point-info {
|
|
|
|
|
+ text-align: right;
|
|
|
|
|
+}
|
|
|
|
|
+.point-text {
|
|
|
|
|
+ font-size: 40rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+}
|
|
|
|
|
+.point-desc {
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+ opacity: 0.8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* VIP尊享权益 */
|
|
|
|
|
+.vip-benefit {
|
|
|
|
|
+ padding: 30rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.task-title {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ margin: 32rpx 0 16rpx 0;
|
|
|
|
|
+}
|
|
|
|
|
+.task-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 20rpx 30rpx;
|
|
|
|
|
+ border: 1px solid #f2f2f2;
|
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
|
+ margin-bottom: 16rpx;
|
|
|
|
|
+ background-color: #f4f3f1;
|
|
|
|
|
+ .task-icon {
|
|
|
|
|
+ width: 70rpx;
|
|
|
|
|
+ height: 70rpx;
|
|
|
|
|
+ margin-right: 30rpx;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+.task-desc {
|
|
|
|
|
+ .task-name {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ margin-right: 30rpx;
|
|
|
|
|
+ width: 160rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ }
|
|
|
|
|
+ .task-reward {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ color: #5d5c5a;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* SVIP购买按钮 */
|
|
|
|
|
+.svip-price {
|
|
|
|
|
+ .svip-price-title {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ }
|
|
|
|
|
+ .flex-center {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ }
|
|
|
|
|
+ .svip-price-btn {
|
|
|
|
|
+ margin-top: 20rpx;
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ border: 1px solid #fdfdf9;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 40rpx 100rpx;
|
|
|
|
|
+ background-color: #fee1a9;
|
|
|
|
|
+ color: #080604;
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ .svip-price-num {
|
|
|
|
|
+ font-size: 40rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ margin-left: 10rpx;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 工具类(如果项目中没有全局flex工具类,需保留) */
|
|
|
|
|
+.flex-between {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+.flex-center-between {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+.flex_1 {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.flex_2 {
|
|
|
|
|
+ flex: 2;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|