feat: 简化为'每天5次免费→充值星尘'模型——问答免费优先星尘扣减(10/次)+充值引导弹窗+我的页星尘余额卡(去掉免费/Pro分层)
This commit is contained in:
@@ -160,6 +160,21 @@ async function sendMessage() {
|
||||
const msg = e.message || 'AI 服务暂时不可用,请稍后再试'
|
||||
if (msg.includes('游客体验已用完')) {
|
||||
guestExhausted.value = true
|
||||
} else if (msg.includes('免费次数已用完') || msg.includes('星尘余额不足') || msg.includes('充值星尘')) {
|
||||
// 引导充值
|
||||
uni.showModal({
|
||||
title: '免费次数用完了',
|
||||
content: '充值星尘可继续提问(10星尘/次),还可以享受更多功能',
|
||||
confirmText: '去充值',
|
||||
cancelText: '分享得次数',
|
||||
success: (m) => {
|
||||
if (m.confirm) {
|
||||
uni.navigateTo({ url: '/pages/vpay/recharge' })
|
||||
} else {
|
||||
onQuotaTap()
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
messages.value.push({ role: 'assistant', content: escapeHtml(`抱歉,${msg}`) })
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user