feat: 简化为'每天5次免费→充值星尘'模型——问答免费优先星尘扣减(10/次)+充值引导弹窗+我的页星尘余额卡(去掉免费/Pro分层)

This commit is contained in:
2026-09-14 11:07:19 +08:00
parent 3777f67af6
commit d44893d78d
3 changed files with 87 additions and 35 deletions
+50 -30
View File
@@ -55,39 +55,21 @@
</view>
</view>
<!-- 订阅卡片 -->
<view class="plan-card" @click="goPayment">
<view class="plan-info">
<text class="plan-title">{{ planTitle }}</text>
<text class="plan-desc">{{ planDesc }}</text>
<view class="plan-progress" v-if="userStore.subInfo">
<view class="progress-track">
<view class="progress-fill" :style="{ width: planPercent + '%' }"></view>
</view>
<text class="progress-text">{{ planExpiryText }}</text>
<!-- 星尘余额 + 今日免费(简化模型:每天5次免费,用完充值星尘) -->
<view class="balance-card">
<view class="balance-left">
<view class="coin-row">
<text class="coin-icon">✨</text>
<text class="coin-num">{{ vpayBalance }}</text>
<text class="coin-name">星尘</text>
</view>
<text class="free-left">今日免费问答剩余 {{ freeRemaining }} 次</text>
</view>
<view class="plan-btn" :class="{ disabled: userStore.isSubscribed }">
<text>{{ planBtnText }}</text>
<view class="recharge-go" @click="goRecharge">
<text>充值</text>
</view>
</view>
<!-- AI 问答配额 -->
<view class="quota-card" @click="goChat" v-if="userStore.isLoggedIn && quotaInfo">
<view class="quota-head">
<AppIcon name="chat" :size="15" />
<text class="quota-card-title">AI 问答 · 今日</text>
</view>
<view class="quota-bar">
<view class="quota-fill" :style="{ width: quotaPercent + '%' }"></view>
</view>
<view class="quota-foot">
<text class="quota-text">剩余 {{ quotaInfo.remainingQuota }} 次</text>
<text class="quota-share" @click.stop="goShare">分享 +5 次</text>
</view>
</view>
<!-- 星尘充值入口 -->
<!-- 星尘充值入口 -->
<view class="vpay-entry" @click="goRecharge">
<view class="ve-icon">✨</view>
<view class="ve-info">
@@ -155,7 +137,7 @@
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
import { ref, computed, onMounted } from 'vue'
import { useUserStore } from '@/stores/user'
import { knowledgeApi, aiChatApi } from '@/utils/api'
import { knowledgeApi, aiChatApi, request } from '@/utils/api'
import { DIMENSIONS } from '@/utils/dimensions'
import AppIcon from '@/components/AppIcon.vue'
import { APP_VERSION } from '@/utils/version'
@@ -169,6 +151,8 @@ const userStore = useUserStore()
const dimensions = DIMENSIONS
const collectedCount = ref(0)
const vpayBalance = ref(0)
const freeRemaining = ref(5)
const stats = ref(null)
const byDim = ref({})
const quotaInfo = ref(null)
@@ -218,12 +202,23 @@ const quotaPercent = computed(() => {
onMounted(() => {
loadStats()
if (userStore.isLoggedIn) {
loadVpayInfo()
userStore.fetchMe()
loadCollected()
loadQuota()
}
})
async function loadVpayInfo() {
try {
const data = await request('GET', '/api/vpay/info', null, userStore.token)
vpayBalance.value = data.balance || 0
if (userStore.quota) freeRemaining.value = userStore.quota.remainingQuota ?? 5
} catch (e) {
// 未开通或未登录
}
}
async function loadStats() {
try {
const data = await knowledgeApi.stats(userStore.isLoggedIn ? userStore.token : '')
@@ -649,3 +644,28 @@ onShareTimeline(() => ({
.ve-desc { font-size: 11px; color: var(--text-tertiary); }
.ve-arrow { font-size: 14px; color: var(--text-muted); }
</style>
<style scoped>
.balance-card {
display: flex; align-items: center; gap: 14px;
background: linear-gradient(135deg, #5b5ff8, #8b5cf6);
border-radius: var(--radius-lg);
padding: 18px 20px;
margin-bottom: 12px;
}
.balance-left { flex: 1; }
.coin-row { display: flex; align-items: baseline; gap: 6px; }
.coin-icon { font-size: 20px; }
.coin-num { font-size: 32px; font-weight: 800; color: #fff; }
.coin-name { font-size: 13px; color: rgba(255,255,255,.7); }
.free-left { display: block; font-size: 11px; color: rgba(255,255,255,.55); margin-top: 4px; }
.recharge-go {
padding: 10px 22px;
background: #fff;
border-radius: var(--radius-full);
color: #5b5ff8;
font-size: 14px;
font-weight: 700;
flex-shrink: 0;
}
</style>