| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957 |
- <template>
- <view class="page-profile">
- <!-- ========== Region 1: User Card ========== -->
- <view class="user-card">
- <!-- Login prompt for unauthenticated users -->
- <view v-if="!userStore.isLoggedIn" class="user-card__login" @click="goLogin">
- <view class="avatar avatar--empty">
- <text class="avatar__icon">+</text>
- </view>
- <text class="user-card__nickname">未登录</text>
- <text class="user-card__login-hint">点击登录 / 注册</text>
- </view>
- <!-- Authenticated user card — tap to edit -->
- <view v-else class="user-card__logged" @click="goEditProfile">
- <view class="user-card__inner">
- <view class="avatar">
- <image :src="userStore.avatarUrl || '/static/default-avatar.png'" mode="aspectFill" />
- </view>
- <view class="user-card__info">
- <text class="user-card__nickname">{{ userStore.nickname }}</text>
- <!-- Role tag -->
- <view
- class="user-card__tag"
- :class="{
- 'user-card__tag--practitioner': userStore.vipType === 'practitioner',
- 'user-card__tag--annual': userStore.vipType === 'annual',
- 'user-card__tag--normal': !userStore.vipType
- }"
- >
- <text v-if="!userStore.vipType">普通用户</text>
- <text v-else-if="userStore.vipType === 'annual'">C端会员</text>
- <text v-else>能量师 🌟</text>
- </view>
- <!-- Social summary (optional) -->
- <text v-if="userStore.bio || userStore.city" class="user-card__summary">
- {{ userStore.city || '' }}{{ userStore.city && userStore.bio ? ' · ' : '' }}{{ userStore.bio || '' }}
- </text>
- </view>
- <text class="user-card__arrow">›</text>
- </view>
- </view>
- </view>
- <!-- ========== Region 2: Membership Status Cards ========== -->
- <!-- US-5.1 §13: Profile incomplete banner -->
- <view v-if="userStore.isLoggedIn && userStore.profileIncomplete" class="banner" @click="goEditProfile">
- <text class="banner__text">📝 请完善个人资料,开启能量匹配</text>
- <text class="banner__arrow">›</text>
- </view>
- <!-- US-4.3: Renewal reminder (7 days before expiry) -->
- <view v-if="isExpiringSoon" class="banner banner--warning" @click="goRenew">
- <text class="banner__text">⏰ 您的会员即将到期,请及时续费</text>
- <text class="banner__arrow">›</text>
- </view>
- <!-- Seed-price badge -->
- <view v-if="userStore.isSeedPriceUser" class="seed-badge">
- <text class="seed-badge__text">🌱 种子价专属会员</text>
- </view>
- <!-- VIP status card — role-based -->
- <!-- Case 1: Energy master (practitioner, in valid period) -->
- <view
- v-if="userStore.vipType === 'practitioner' && userStore.isVip && !isExpired"
- class="vip-card vip-card--practitioner"
- >
- <view class="vip-card__body">
- <text class="vip-card__title">能量师会员 🌟</text>
- <text class="vip-card__desc">
- 有效期至 {{ formatDate(userStore.vipEndTime) }}
- </text>
- </view>
- <text class="vip-card__action" @click="goRenew">续费</text>
- </view>
- <!-- Case 2: C-end annual subscriber (in valid period) -->
- <view
- v-else-if="userStore.vipType === 'annual' && userStore.isVip && !isExpired"
- class="vip-card vip-card--annual"
- >
- <view class="vip-card__body">
- <text class="vip-card__title">C端会员</text>
- <text class="vip-card__desc">
- 有效期至 {{ formatDate(userStore.vipEndTime) }}
- </text>
- </view>
- <text class="vip-card__action" @click="goUpgradePractitioner">升级能量师</text>
- </view>
- <!-- Case 3: Expired member (vipEndTime < now) -->
- <view v-else-if="userStore.vipType && isExpired" class="vip-card vip-card--expired">
- <view class="vip-card__body">
- <text class="vip-card__title">会员已过期</text>
- <text class="vip-card__desc">
- 过期时间 {{ formatDate(userStore.vipEndTime) }}
- </text>
- </view>
- <text class="vip-card__action" @click="goPayment">重新开通</text>
- </view>
- <!-- Case 4: Free user — two payment options -->
- <view v-else class="vip-card vip-card--upsell">
- <view class="vip-card__body">
- <text class="vip-card__title">选择方案</text>
- <view class="vip-card__dual">
- <view class="vip-card__option" @click="goPayment">
- <text class="option-title">C端会员</text>
- <text class="option-price">¥131/年</text>
- <text class="option-desc">无限AI解读</text>
- <button class="option-btn light">开通</button>
- </view>
- <view class="vip-card__option vip-card__option--pro" @click="goUpgradePractitioner">
- <text class="option-title">能量师</text>
- <text class="option-price">¥1,314/年</text>
- <text class="option-desc">完整功能+分销</text>
- <button class="option-btn primary">申请</button>
- </view>
- </view>
- </view>
- </view>
- <!-- Daily quota (non-VIP only) -->
- <view v-if="!userStore.isVip" class="quota-bar">
- <view class="quota-bar__item">
- <text class="quota-bar__label">今日分析</text>
- <text class="quota-bar__value">{{ userStore.usedCharts }}/{{ userStore.maxCharts }}</text>
- </view>
- <view class="quota-bar__divider"></view>
- <view class="quota-bar__item">
- <text class="quota-bar__label">今日解读</text>
- <text class="quota-bar__value">{{ userStore.usedChats }}/{{ userStore.maxChats }}</text>
- </view>
- </view>
- <!-- ========== Region 3: Feature Menu ========== -->
- <!-- Group 1: My Services -->
- <view class="menu-section">
- <view class="menu-section__header">我的服务</view>
- <!-- Analysis history (all logged-in) -->
- <view v-if="userStore.isLoggedIn" class="menu-item" @click="goRecords">
- <text class="menu-item__label">📋 我的分析</text>
- <text class="menu-item__arrow">›</text>
- </view>
- <!-- Distribution panel (paid users only) -->
- <view v-if="userStore.isLoggedIn && userStore.isVip" class="menu-item" @click="goCommission">
- <text class="menu-item__label">💰 我的推广</text>
- <text class="menu-item__arrow">›</text>
- </view>
- <!-- Practitioner workbench (practitioners only) — EPIC 9 -->
- <view v-if="userStore.vipType === 'practitioner' && userStore.isVip" class="menu-item" @click="goWorkbench">
- <text class="menu-item__label">🔧 工作台</text>
- <view class="menu-item__badge menu-item__badge--new">NEW</view>
- <text class="menu-item__arrow">›</text>
- </view>
- </view>
- <!-- Group 2: Promotion Tools (paid users only) -->
- <view v-if="userStore.isLoggedIn && userStore.isVip && userStore.referralCode" class="menu-section">
- <view class="menu-section__header">推广工具</view>
- <view class="promo-card">
- <view class="promo-card__code-area">
- <text class="promo-card__prefix">推广码</text>
- <text class="promo-card__code">{{ userStore.referralCode }}</text>
- </view>
- <view class="promo-card__actions">
- <text class="promo-card__btn" @click="copyReferral">复制推广码</text>
- <text class="promo-card__divider">|</text>
- <text class="promo-card__btn" @click="generatePoster">生成推广海报</text>
- </view>
- </view>
- </view>
- <!-- Group 3: More -->
- <view class="menu-section">
- <view class="menu-section__header">更多</view>
- <!-- My tags (practitioner only, P2) -->
- <view v-if="userStore.vipType === 'practitioner'" class="menu-item" @click="goTags">
- <text class="menu-item__label">🏷 我的标签</text>
- <text class="menu-item__arrow">›</text>
- </view>
- <!-- About us (all) -->
- <view class="menu-item" @click="goAbout">
- <text class="menu-item__label">ℹ️ 关于我们</text>
- <text class="menu-item__arrow">›</text>
- </view>
- </view>
- <!-- ========== Region 4: Logout ========== -->
- <view v-if="userStore.isLoggedIn" class="menu-section">
- <view class="menu-item menu-item--danger" @click="handleLogout">
- <text class="menu-item__label menu-item__label--danger">退出登录</text>
- </view>
- </view>
- <!-- Hidden canvas for poster generation -->
- <canvas canvas-id="posterCanvas" id="posterCanvas" class="poster-canvas" />
- </view>
- </template>
- <script setup>
- import { computed, getCurrentInstance } from 'vue'
- import { onShow } from '@dcloudio/uni-app'
- import { useUserStore } from '@/stores/user'
- import { profileApi } from '@/utils/api'
- const userStore = useUserStore()
- // US-4.3: Renewal reminder if expiry within 7 days
- const isExpiringSoon = computed(() => {
- if (!userStore.vipEndTime) return false
- const expiry = new Date(userStore.vipEndTime)
- const now = new Date()
- const diffHours = (expiry - now) / (1000 * 60 * 60)
- return diffHours > 0 && diffHours <= 7 * 24
- })
- // US-4.3: Expired check
- const isExpired = computed(() => {
- if (!userStore.vipEndTime) return false
- return new Date(userStore.vipEndTime) < new Date()
- })
- function formatDate(dateStr) {
- if (!dateStr) return ''
- return dateStr.slice(0, 10)
- }
- onShow(() => {
- if (userStore.isLoggedIn) {
- userStore.fetchProfile()
- userStore.fetchQuota()
- userStore.fetchCommissionSummary()
- }
- })
- function goLogin() {
- uni.navigateTo({ url: '/pages/login/index' })
- }
- function goEditProfile() {
- if (!userStore.isLoggedIn) {
- uni.navigateTo({ url: '/pages/login/index' })
- return
- }
- uni.navigateTo({ url: '/pages/profile/edit' })
- }
- function goPayment() {
- uni.navigateTo({ url: '/pages/payment/index?product=annual' })
- }
- function goRenew() {
- const product = userStore.vipType === 'practitioner' ? 'practitioner' : 'annual'
- uni.navigateTo({ url: `/pages/payment/index?product=${product}` })
- }
- function goUpgradePractitioner() {
- uni.navigateTo({ url: '/pages/payment/index?product=practitioner' })
- }
- function goRecords() {
- uni.switchTab({ url: '/pages/records/index' })
- }
- function goCommission() {
- if (!userStore.isLoggedIn) {
- uni.navigateTo({ url: '/pages/login/index' })
- return
- }
- uni.navigateTo({ url: '/pages/commission/index' })
- }
- // EPIC 9: Navigate to practitioner workbench
- function goWorkbench() {
- uni.navigateTo({ url: '/pages/plan/workbench' })
- }
- function copyReferral() {
- if (userStore.referralCode) {
- uni.setClipboardData({
- data: userStore.referralCode,
- success: () => uni.showToast({ title: '已复制推广码', icon: 'success' })
- })
- }
- }
- function generatePoster() {
- uni.showLoading({ title: '生成海报…', mask: true })
- const baseUrl = 'http://127.0.0.1:8186'
- Promise.all([
- downloadImage(userStore.avatarUrl),
- profileApi.qrcode().then(res => {
- const url = res.qrCodeUrl.startsWith('http') ? res.qrCodeUrl : baseUrl + res.qrCodeUrl
- return downloadImage(url)
- })
- ]).then(([avatarPath, qrPath]) => {
- const ctx = uni.createCanvasContext('posterCanvas')
- const W = 375, H = 670
- // Background
- const grd = ctx.createLinearGradient(0, 0, 0, H)
- grd.addColorStop(0, '#0c0a1a')
- grd.addColorStop(1, '#1a1040')
- ctx.fillStyle = grd
- ctx.fillRect(0, 0, W, H)
- // Top glow
- const topGlow = ctx.createLinearGradient(0, 0, 0, 220)
- topGlow.addColorStop(0, 'rgba(90, 50, 180, 0.35)')
- topGlow.addColorStop(1, 'rgba(90, 50, 180, 0)')
- ctx.fillStyle = topGlow
- ctx.fillRect(0, 0, W, 220)
- // Gold accent line
- ctx.fillStyle = '#C9A84C'
- ctx.fillRect(W * 0.32, 28, W * 0.36, 1.5)
- // Brand title
- ctx.font = '13px sans-serif'
- ctx.fillStyle = 'rgba(201, 168, 76, 0.7)'
- ctx.textAlign = 'center'
- ctx.fillText('数字能量学', W / 2, 52)
- // Avatar (circular)
- const avatarSize = 76
- ctx.save()
- ctx.beginPath()
- ctx.arc(W / 2, 128, avatarSize / 2, 0, 2 * Math.PI)
- ctx.closePath()
- ctx.fillStyle = '#ffffff'
- ctx.fill()
- if (avatarPath) {
- ctx.clip()
- ctx.drawImage(avatarPath, (W - avatarSize) / 2, 128 - avatarSize / 2, avatarSize, avatarSize)
- }
- ctx.restore()
- // Nickname
- ctx.font = 'bold 18px sans-serif'
- ctx.fillStyle = '#FFFFFF'
- ctx.textAlign = 'center'
- ctx.fillText(userStore.nickname || '数字能量探索者', W / 2, 192)
- // Role badge
- const roleText = userStore.vipType === 'practitioner' ? '能量师' : userStore.vipType === 'annual' ? 'C端会员' : '探索者'
- ctx.font = '11px sans-serif'
- ctx.fillStyle = userStore.vipType === 'practitioner' ? '#C9A84C' : 'rgba(255,255,255,0.5)'
- ctx.fillText(roleText, W / 2, 214)
- // Divider
- ctx.fillStyle = 'rgba(255,255,255,0.07)'
- ctx.fillRect(30, 240, W - 60, 1)
- // Section title
- ctx.font = '12px sans-serif'
- ctx.fillStyle = 'rgba(255,255,255,0.38)'
- ctx.fillText('推荐体验', W / 2, 270)
- // QR code background
- const qrSize = 160
- const qrX = (W - qrSize) / 2
- const qrY = 288
- ctx.fillStyle = '#FFFFFF'
- ctx.fillRect(qrX, qrY, qrSize, qrSize)
- if (qrPath) {
- ctx.drawImage(qrPath, qrX + 8, qrY + 8, qrSize - 16, qrSize - 16)
- }
- // Hint
- ctx.font = '11px sans-serif'
- ctx.fillStyle = 'rgba(255,255,255,0.32)'
- ctx.fillText('扫码开启你的数字能量之旅', W / 2, qrY + qrSize + 24)
- // Bottom
- ctx.font = '10px sans-serif'
- ctx.fillStyle = 'rgba(255,255,255,0.18)'
- ctx.fillText('数字能量学 · 数字能量智能体', W / 2, H - 22)
- ctx.draw(false)
- setTimeout(() => {
- uni.canvasToTempFilePath({
- canvasId: 'posterCanvas',
- quality: 1,
- success: (res) => {
- uni.saveImageToPhotosAlbum({
- filePath: res.tempFilePath,
- success: () => {
- uni.hideLoading()
- uni.showToast({ title: '海报已保存到相册', icon: 'success' })
- },
- fail: (e) => {
- uni.hideLoading()
- if (e.errMsg && e.errMsg.includes('auth deny')) {
- uni.showModal({
- title: '保存海报',
- content: '需要相册权限才能保存海报到本地',
- confirmText: '去设置',
- success: (r) => { if (r.confirm) uni.openSetting() }
- })
- } else {
- uni.showToast({ title: '保存失败,请重试', icon: 'none' })
- }
- }
- })
- },
- fail: () => {
- uni.hideLoading()
- uni.showToast({ title: '生成失败,请重试', icon: 'none' })
- }
- }, getCurrentInstance())
- }, 150)
- }).catch(e => {
- console.error('generatePoster error:', e)
- uni.hideLoading()
- uni.showToast({ title: '生成失败,请重试', icon: 'none' })
- })
- }
- function downloadImage(url) {
- if (!url) return Promise.resolve('')
- return new Promise((resolve) => {
- uni.downloadFile({
- url,
- success: (res) => resolve(res.tempFilePath),
- fail: () => resolve('')
- })
- })
- }
- function goTags() {
- uni.navigateTo({ url: '/pages/tags/index' })
- }
- function goAbout() {
- uni.navigateTo({ url: '/pages/about/index' })
- }
- function handleLogout() {
- uni.showModal({
- title: '确认退出',
- content: '确定要退出登录吗?',
- success: (res) => {
- if (res.confirm) {
- userStore.logout()
- uni.showToast({ title: '已退出', icon: 'success' })
- }
- }
- })
- }
- </script>
- <style scoped lang="scss">
- // Brand colors (aligned with style-guide.md)
- $blue-dark: #1E3A5F;
- $gold: #C9A84C;
- $gold-light: #F8F0D8;
- $warm-white: #F8F6F1;
- $white: #FFFFFF;
- $text-primary: #1F2937;
- $text-secondary: #6B7280;
- $text-muted: #9CA3AF;
- $danger: #EF4444;
- .page-profile {
- padding: 16px;
- min-height: 100vh;
- background: $warm-white;
- }
- // ========== Region 1: User Card ==========
- .user-card {
- background: linear-gradient(135deg, $blue-dark, darken($blue-dark, 6%));
- border-radius: 16px;
- padding: 20px 18px;
- margin-bottom: 16px;
- &__login {
- cursor: pointer;
- text-align: center;
- padding: 8px 0;
- }
- &__logged {
- cursor: pointer;
- }
- &__inner {
- display: flex;
- align-items: center;
- gap: 14px;
- }
- &__info {
- flex: 1;
- min-width: 0;
- }
- &__nickname {
- font-size: 17px;
- font-weight: 600;
- color: $white;
- display: block;
- }
- &__login-hint {
- font-size: 12px;
- color: rgba(255, 255, 255, 0.7);
- display: block;
- margin-top: 4px;
- }
- &__tag {
- display: inline-flex;
- padding: 2px 12px;
- border-radius: 20px;
- font-size: 11px;
- margin-top: 6px;
- &--practitioner {
- background: $gold;
- color: $blue-dark;
- font-weight: 600;
- }
- &--annual {
- background: rgba($gold, 0.25);
- color: $gold;
- }
- &--normal {
- background: rgba(255, 255, 255, 0.15);
- color: rgba(255, 255, 255, 0.8);
- }
- }
- &__summary {
- font-size: 11px;
- color: rgba(255, 255, 255, 0.55);
- margin-top: 4px;
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- &__arrow {
- font-size: 22px;
- color: rgba(255, 255, 255, 0.3);
- flex-shrink: 0;
- padding-left: 4px;
- }
- }
- // Avatar
- .avatar {
- width: 56px;
- height: 56px;
- border-radius: 50%;
- overflow: hidden;
- flex-shrink: 0;
- image {
- width: 100%;
- height: 100%;
- border-radius: 50%;
- border: 2px solid rgba(255, 255, 255, 0.25);
- }
- &--empty {
- width: 56px;
- height: 56px;
- background: rgba(255, 255, 255, 0.12);
- display: flex;
- align-items: center;
- justify-content: center;
- border: 2px dashed rgba(255, 255, 255, 0.25);
- }
- &__icon {
- font-size: 24px;
- color: rgba(255, 255, 255, 0.5);
- }
- }
- // ========== Region 2: Membership Status ==========
- // Banners
- .banner {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 14px;
- border-radius: 10px;
- margin-bottom: 12px;
- background: #FEF3C7;
- border: 1px solid #FDE68A;
- &--warning {
- background: #FEF3C7;
- border-color: #FDE68A;
- }
- &__text {
- font-size: 13px;
- color: #92400E;
- flex: 1;
- }
- &__arrow {
- font-size: 18px;
- color: #92400E;
- margin-left: 8px;
- }
- }
- // Seed-price badge
- .seed-badge {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 6px 14px;
- border-radius: 8px;
- margin-bottom: 12px;
- background: $gold-light;
- border: 1px solid $gold;
- &__text {
- font-size: 12px;
- color: $blue-dark;
- font-weight: 500;
- }
- }
- // VIP status card
- .vip-card {
- border-radius: 12px;
- padding: 16px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16px;
- &__body {
- flex: 1;
- }
- &__title {
- font-weight: 600;
- font-size: 16px;
- display: block;
- }
- &__desc {
- font-size: 12px;
- opacity: 0.8;
- display: block;
- margin-top: 4px;
- }
- &__action {
- font-size: 14px;
- font-weight: 500;
- padding: 6px 14px;
- border-radius: 8px;
- white-space: nowrap;
- cursor: pointer;
- }
- // Practitioner
- &--practitioner {
- background: linear-gradient(135deg, #7c3aed, #6d28d9);
- .vip-card__title, .vip-card__desc { color: #fff; }
- .vip-card__action {
- color: #7c3aed;
- background: rgba(255, 255, 255, 0.95);
- }
- }
- // Annual
- &--annual {
- background: linear-gradient(135deg, #f59e0b, #d97706);
- .vip-card__title, .vip-card__desc { color: #fff; }
- .vip-card__action {
- color: #d97706;
- background: rgba(255, 255, 255, 0.95);
- }
- }
- // Expired
- &--expired {
- background: #E5E7EB;
- .vip-card__title { color: $text-secondary; }
- .vip-card__desc { color: $text-muted; }
- .vip-card__action {
- color: $text-secondary;
- background: $white;
- }
- }
- // Upsell (free user — two options)
- &--upsell {
- background: linear-gradient(135deg, $blue-dark, darken($blue-dark, 4%));
- flex-direction: column;
- align-items: flex-start;
- gap: 14px;
- .vip-card__body {
- width: 100%;
- }
- .vip-card__title {
- color: $gold;
- font-size: 16px;
- margin-bottom: 8px;
- }
- .vip-card__dual {
- display: flex;
- gap: 12px;
- width: 100%;
- }
- .vip-card__option {
- flex: 1;
- background: rgba(255, 255, 255, 0.06);
- border: 1px solid rgba(255, 255, 255, 0.08);
- border-radius: 12px;
- padding: 14px 12px;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 6px;
- cursor: pointer;
- }
- .vip-card__option:active {
- background: rgba(255, 255, 255, 0.1);
- }
- .vip-card__option .option-title {
- font-size: 15px;
- font-weight: 600;
- color: rgba(255, 255, 255, 0.9);
- }
- .vip-card__option .option-price {
- font-size: 18px;
- font-weight: 700;
- color: $gold;
- }
- .vip-card__option .option-desc {
- font-size: 11px;
- color: rgba(255, 255, 255, 0.45);
- }
- .vip-card__option .option-btn {
- width: 100%;
- border: none;
- border-radius: 8px;
- padding: 8px 0;
- font-size: 13px;
- font-weight: 600;
- cursor: pointer;
- margin-top: 4px;
- }
- .option-btn.light {
- background: rgba(255, 255, 255, 0.12);
- color: #fff;
- }
- .option-btn.primary {
- background: $gold;
- color: $blue-dark;
- }
- .vip-card__option--pro {
- border-color: rgba($gold, 0.2);
- background: rgba($gold, 0.06);
- }
- }
- }
- // Daily quota
- .quota-bar {
- background: $white;
- border-radius: 12px;
- padding: 16px;
- margin-bottom: 16px;
- display: flex;
- align-items: center;
- justify-content: space-around;
- &__item {
- text-align: center;
- }
- &__label {
- font-size: 12px;
- color: $text-secondary;
- display: block;
- margin-bottom: 4px;
- }
- &__value {
- font-size: 20px;
- font-weight: 700;
- color: $text-primary;
- }
- &__divider {
- width: 1px;
- height: 36px;
- background: #E5E7EB;
- }
- }
- // ========== Region 3: Feature Menu ==========
- .menu-section {
- background: $white;
- border-radius: 12px;
- margin-bottom: 16px;
- overflow: hidden;
- &__header {
- font-size: 12px;
- font-weight: 600;
- color: $text-muted;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- padding: 14px 16px 6px;
- }
- }
- .menu-item {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 14px 16px;
- border-bottom: 1px solid #F3F4F6;
- cursor: pointer;
- transition: background 0.15s;
- &:active {
- background: #F9FAFB;
- }
- &:last-child {
- border-bottom: none;
- }
- &__label {
- flex: 1;
- font-size: 15px;
- color: $text-primary;
- &--danger {
- color: $danger;
- }
- }
- &__badge {
- font-size: 10px;
- font-weight: 700;
- padding: 2px 6px;
- border-radius: 4px;
- &--new {
- background: #FEF3C7;
- color: #D97706;
- }
- }
- &__arrow {
- color: $text-muted;
- font-size: 18px;
- }
- &--danger {
- justify-content: center;
- border-bottom: none;
- }
- }
- // Promotion tools card
- .promo-card {
- margin: 0 16px 14px;
- background: #FFFBEB;
- border: 1px solid #FDE68A;
- border-radius: 10px;
- overflow: hidden;
- &__code-area {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 10px;
- padding: 14px 16px 8px;
- }
- &__prefix {
- font-size: 13px;
- color: $text-secondary;
- }
- &__code {
- font-size: 22px;
- font-weight: 700;
- color: $blue-dark;
- letter-spacing: 3px;
- font-family: monospace;
- }
- &__actions {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 10px;
- padding: 6px 16px 14px;
- }
- &__btn {
- font-size: 12px;
- color: $blue-dark;
- font-weight: 500;
- cursor: pointer;
- }
- &__divider {
- color: #D1D5DB;
- font-size: 12px;
- }
- }
- .poster-canvas {
- position: fixed;
- left: -9999px;
- top: -9999px;
- width: 375px;
- height: 670px;
- }
- </style>
|