Files
shoot-miniprograms/src/pages/be-vip.vue

259 lines
6.3 KiB
Vue
Raw Permalink Normal View History

2025-05-28 16:03:08 +08:00
<script setup>
2025-08-25 13:47:32 +08:00
import { ref, onMounted, onBeforeUnmount } from "vue";
2025-05-28 16:03:08 +08:00
import Container from "@/components/Container.vue";
import Avatar from "@/components/Avatar.vue";
import SButton from "@/components/SButton.vue";
2025-06-24 13:18:03 +08:00
import Signin from "@/components/Signin.vue";
2025-07-15 14:02:09 +08:00
import UserHeader from "@/components/UserHeader.vue";
2025-08-29 10:20:37 +08:00
import { createOrderAPI, getHomeData, getVIPDescAPI } from "@/apis";
2025-07-01 10:57:49 +08:00
import { formatTimestamp } from "@/util";
2025-05-28 16:03:08 +08:00
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
2025-06-22 15:04:10 +08:00
const { user, config } = storeToRefs(store);
2025-07-12 17:12:24 +08:00
const { updateUser } = store;
2025-05-28 16:03:08 +08:00
2025-06-22 15:04:10 +08:00
const selectedVIP = ref(0);
2025-06-24 13:18:03 +08:00
const showModal = ref(false);
2025-07-25 10:00:18 +08:00
const lastDate = ref(user.value.expiredAt);
const refreshing = ref(false);
const timer = ref(null);
2025-08-29 10:20:37 +08:00
const richContent = ref("");
2025-05-28 16:03:08 +08:00
2025-07-01 00:25:17 +08:00
const onPay = async () => {
2025-06-19 01:55:40 +08:00
if (!user.value.id) {
2025-06-24 13:18:03 +08:00
showModal.value = true;
2025-07-01 00:25:17 +08:00
} else if (config.value.vipMenus[selectedVIP.value]) {
if (config.value.vipMenus[selectedVIP.value].id) {
const result = await createOrderAPI(
config.value.vipMenus[selectedVIP.value].id
);
2025-07-12 17:12:24 +08:00
if (!result.pay) return;
const params = result.pay.order.jsApi.params;
if (params) {
wx.requestPayment({
timeStamp: params.timeStamp, // 时间戳
nonceStr: params.nonceStr, // 随机字符串
package: params.package, // 统一下单接口返回的 prepay_id 参数值格式prepay_id=***
paySign: params.paySign, // 签名
signType: "RSA", // 签名类型默认为RSA
async success(res) {
uni.showToast({
title: "支付成功",
icon: "none",
});
2025-07-25 10:00:18 +08:00
timer.value = setInterval(async () => {
refreshing.value = true;
const result = await getHomeData();
if (result.user.expiredAt > lastDate.value) {
refreshing.value = false;
if (result.user) updateUser(result.user);
clearInterval(timer.value);
}
}, 1000);
2025-07-12 17:12:24 +08:00
},
fail(res) {
console.log("pay error", res);
},
});
}
2025-07-01 00:25:17 +08:00
}
2025-06-19 01:55:40 +08:00
}
};
2025-07-15 14:02:09 +08:00
2025-08-29 10:20:37 +08:00
onMounted(async () => {
const result = await getVIPDescAPI();
richContent.value = result.describe;
});
2025-07-15 14:02:09 +08:00
const toOrderPage = () => {
uni.navigateTo({
url: "/pages/orders",
});
};
2025-07-25 10:00:18 +08:00
2025-08-25 13:47:32 +08:00
onBeforeUnmount(() => {
2025-07-25 10:00:18 +08:00
if (timer.value) clearInterval(timer.value);
});
2025-05-28 16:03:08 +08:00
</script>
<template>
<Container title="会员说明">
2026-01-09 18:12:27 +08:00
<view v-if="user.id" class="header">
<view>
<Avatar :src="user.avatar" :size="35" />
<text class="truncate">{{ user.nickName }}</text>
<image
class="user-name-image"
src="../static/vip1.png"
mode="widthFix"
/>
</view>
<block v-if="refreshing">
<image
src="../static/btn-loading.png"
mode="widthFix"
class="loading"
/>
</block>
<block v-else>
<text v-if="user.expiredAt">
{{ formatTimestamp(user.expiredAt) }} 到期
</text>
</block>
</view>
<view
class="container"
:style="{ height: !user.id ? 'calc(100% - 10px)' : 'calc(100% - 62px)' }"
>
<view class="content vip-content">
<view class="title-bar">
<view />
<text>VIP 介绍</text>
</view>
<view :style="{ marginTop: '10rpx' }">
<rich-text :nodes="richContent" />
2025-05-28 16:03:08 +08:00
</view>
</view>
2026-01-09 18:12:27 +08:00
<view class="content">
<view class="title-bar">
<view />
<text>会员续费</text>
2025-05-28 16:03:08 +08:00
</view>
2026-01-09 18:12:27 +08:00
<view class="vip-items">
<view
v-for="(item, index) in config.vipMenus || []"
:key="index"
:style="{
color: selectedVIP === index ? '#fff' : '#333333',
borderColor: selectedVIP === index ? '#FF7D57' : '#eee',
background:
selectedVIP === index
? '#FF7D57'
: 'linear-gradient(180deg, #fbfbfb 0%, #f5f5f5 100%)',
}"
@click="() => (selectedVIP = index)"
>
{{ item.name }}
2025-05-28 16:03:08 +08:00
</view>
</view>
2026-01-09 18:12:27 +08:00
</view>
<SButton :onClick="onPay">支付</SButton>
<view class="my-orders" v-if="user.id">
<view @click="toOrderPage">
<text>我的订单</text>
<image src="../static/enter-arrow-blue.png" mode="widthFix" />
2025-07-15 14:02:09 +08:00
</view>
2025-05-28 16:03:08 +08:00
</view>
2026-01-09 18:12:27 +08:00
<Signin :show="showModal" :onClose="() => (showModal = false)" />
2025-05-28 16:03:08 +08:00
</view>
</Container>
</template>
<style scoped>
.header {
2025-06-08 12:52:49 +08:00
width: calc(100% - 30px);
2025-05-28 16:03:08 +08:00
display: flex;
align-items: center;
justify-content: space-between;
color: #fff;
padding: 15px;
padding-top: 0;
font-size: 14px;
}
.header > view {
display: flex;
align-items: center;
}
2025-07-28 15:05:19 +08:00
.header > view > text {
2025-05-28 16:03:08 +08:00
margin-left: 10px;
2025-07-28 15:05:19 +08:00
max-width: 120px;
2025-06-24 13:18:03 +08:00
text-align: left;
2025-05-28 16:03:08 +08:00
}
2025-07-28 15:05:19 +08:00
.header > view > image {
margin-left: 5px;
width: 20px;
}
2025-05-28 16:03:08 +08:00
.header > text:nth-child(2) {
color: #fed847;
}
.container {
width: 100%;
2025-07-14 14:25:37 +08:00
background-color: #f5f5f5;
2025-05-28 16:03:08 +08:00
padding-top: 10px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
background-color: #fff;
padding: 15px;
margin-bottom: 10px;
}
.title-bar {
width: 100%;
display: flex;
align-items: center;
2025-10-21 15:13:22 +08:00
color: #000;
2025-05-28 16:03:08 +08:00
}
.title-bar > view:first-child {
width: 5px;
height: 15px;
border-radius: 10px;
background-color: #fed847;
margin-right: 10px;
}
.content > view:nth-child(2) {
font-size: 14px;
color: #333;
}
.vip-items {
width: 100%;
2025-06-22 15:04:10 +08:00
display: grid;
grid-template-columns: repeat(4, 23.5%);
2025-05-28 16:03:08 +08:00
padding: 10px;
2025-06-22 15:04:10 +08:00
row-gap: 5%;
column-gap: 2%;
2025-05-28 16:03:08 +08:00
}
.vip-items > view {
border: 1px solid #eee;
padding: 12px 0;
border-radius: 10px;
text-align: center;
2025-09-04 13:54:39 +08:00
font-size: 27rpx;
2025-05-28 16:03:08 +08:00
}
2025-07-15 14:02:09 +08:00
.vip-content {
2025-07-28 15:07:51 +08:00
max-height: 62%;
2025-07-15 14:02:09 +08:00
}
.vip-content > view:nth-child(2) {
overflow: auto;
}
2025-07-24 11:54:38 +08:00
.vip-content > view:nth-child(2)::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
2025-07-15 14:02:09 +08:00
.my-orders {
display: flex;
justify-content: center;
color: #39a8ff;
margin-top: 10px;
font-size: 13px;
}
.my-orders > view {
display: flex;
align-items: center;
}
.my-orders > view > image {
width: 15px;
}
2025-07-25 10:00:18 +08:00
.loading {
width: 20px;
height: 20px;
margin-left: 10px;
transition: all 0.3s ease;
background-blend-mode: darken;
animation: rotate 2s linear infinite;
}
2025-05-28 16:03:08 +08:00
</style>