2697 lines
79 KiB
Vue
2697 lines
79 KiB
Vue
<template>
|
||
<view class="wrap">
|
||
<!-- 头部区域 -->
|
||
<view class="header-section">
|
||
<view class="user-info">
|
||
<image class="avatar" :src="avatar || '/static/logo.png'" mode="aspectFill"></image>
|
||
<view class="user-meta">
|
||
<view class="name-row">
|
||
<text class="nickname">{{ nickname || '未登录' }}</text>
|
||
<view class="level-badge" v-if="nickname">
|
||
<image class="level-icon" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRjZCMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNNiAzSDE4TDIxIDlMOSAyMSAzIDlMNiAzWiIgLz48L3N2Zz4=" mode="aspectFit"></image>
|
||
<text class="level-text">Lv1 青铜</text>
|
||
</view>
|
||
</view>
|
||
<view class="progress-container" v-if="nickname">
|
||
<view class="progress-bar">
|
||
<view class="progress-fill" style="width: 20%;"></view>
|
||
</view>
|
||
<text class="progress-text">100/5000 升级Lv2</text>
|
||
</view>
|
||
<view class="userid" v-else>ID: {{ userId || '-' }}</view>
|
||
</view>
|
||
<view class="join-btn" @click="handleJoin" v-if="!nickname">立即登录</view>
|
||
</view>
|
||
|
||
<!-- 数据统计栏 (Modified: Points / Coupons / Item Cards) -->
|
||
<view class="stats-row">
|
||
<view class="stat-item" @click="showPointsPopup">
|
||
<text class="stat-num">{{ pointsBalance || 0 }}</text>
|
||
<text class="stat-label">积分</text>
|
||
</view>
|
||
<view class="stat-item" @click="showCouponsPopup">
|
||
<text class="stat-num">{{ stats.coupon_count || 0 }}</text>
|
||
<text class="stat-label">优惠券</text>
|
||
</view>
|
||
<view class="stat-item" @click="showItemCardsPopup">
|
||
<text class="stat-num">{{ stats.item_card_count || 0 }}</text>
|
||
<text class="stat-label">道具卡</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 邀请Banner (Relocated & Re-designed) -->
|
||
<view class="invite-banner" @click="handleInvite">
|
||
<view class="invite-info">
|
||
<view class="invite-tag">好礼相送</view>
|
||
<view class="invite-title">邀请好友送好礼</view>
|
||
<view class="invite-desc">全新版本等你来玩,奖励拿到手软</view>
|
||
</view>
|
||
<view class="invite-action">
|
||
<text class="invite-btn-text">立即邀请</text>
|
||
<image class="invite-arrow" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMxQTFBMUEiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSc5IDE4bDYtNi02LTYnIC8+PC9zdmc+" mode="aspectFit"></image>
|
||
</view>
|
||
<image class="invite-bg-icon" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMxQTFBMUEiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMjEgMTF2OGEyIDIgMCAwIDEtMiAySDVhMiAyIDAgMCAxLTItMnYtOGw5LTYgOSA2eiIgLz48L3N2Zz4=" mode="aspectFit"></image>
|
||
</view>
|
||
|
||
<!-- 我的订单 -->
|
||
<view class="card-section">
|
||
<view class="section-title">我的订单</view>
|
||
<view class="grid-row">
|
||
<view class="grid-item" @click="toOrders('pending')">
|
||
<image class="grid-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRjZCMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMSA4YTIgMiAwIDAgMC0xLTEuNzNsLTctNGEyIDIgMCAwIDAtMiAwbC03IDRBMiAyIDAgMCAwIDMgOHY4YTIgMiAwIDAgMCAxIDEuNzNsNyA0YTIgMiAwIDAgMCAyIDBsNy00QTIgMiAwIDAgMCAyMSAxNlY4eiIvPjxwYXRoIGQ9Ik0zLjI3IDYuOTZMMTIgMTIuMDFsMTAgLTUuMDUiLz48cGF0aCBkPSJNMTIgMjIuMDhWMTIiLz48L3N2Zz4=" mode="aspectFit"></image>
|
||
<text class="grid-label">盒柜</text>
|
||
</view>
|
||
<view class="grid-item" @click="toOrders('pending')">
|
||
<image class="grid-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRjZCMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjEiIHk9IjQiIHdpZHRoPSIyMiIgaGVpZ2h0PSIxNiIgcng9IjIiIHJ5PSIyIj48L3JlY3Q+PGxpbmUgeDE9IjEiIHkxPSIxMCIgeDI9IjIzIiB5Mj0iMTAiPjwvbGluZT48L3N2Zz4=" mode="aspectFit"></image>
|
||
<text class="grid-label">待付款</text>
|
||
</view>
|
||
<view class="grid-item" @click="toOrders('pending')">
|
||
<image class="grid-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRjZCMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xMiAyMmg1LjlhMiAyIDAgMCAwIDEuOS0xLjQybDMuMTMtMTEuMDlhMiAyIDAgMCAwLTEuOS0yLjUxaC00LjE2IiAvPjxwYXRoIGQ9Ik0xOC40MiA5aC02LjMyIiAvPjxwYXRoIGQ9Ik0xNSA2VjNhMSAxIDAgMCAwLTEtMUg2YTEgMSAwIDAgMC0xIDF2MTQiIC8+PHBhdGggZD0iTTkgMTNoNyIgLz48L3N2Zz4=" mode="aspectFit"></image>
|
||
<text class="grid-label">待发货</text>
|
||
</view>
|
||
<view class="grid-item" @click="toOrders('completed')">
|
||
<image class="grid-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRjZCMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjEiIHk9IjMiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxMyIgcng9IjIiIHJ5PSIyIj48L3JlY3Q+PHBvbHlsaW5lIHBvaW50cz0iMTYgOCAyMCA4IDIwIDIxIDIgMjEgMiAxNiA2IDE2IiAvPjwvc3ZnPg==" mode="aspectFit"></image>
|
||
<text class="grid-label">已发货</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 功能菜单 -->
|
||
<view class="card-section">
|
||
<view class="grid-menu">
|
||
<view class="menu-item" @click="showCouponsPopup">
|
||
<image class="menu-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMSA2SDNhMiAyIDAgMCAwLTIgMnY4YTIgMiAwIDAgMCAyIDJoMThhMiAyIDAgMCAwIDItMnYtOGEyIDIgMCAwIDAtMi0yWiIgLz48cGF0aCBkPSJNNiAxMnYtMiIgLz48cGF0aCBkPSJNNiAxNnYtMiIgLz48cGF0aCBkPSJNMTYgNnYxMiIgLz48L3N2Zz4=" mode="aspectFit"></image>
|
||
<text class="menu-label">优惠券</text>
|
||
</view>
|
||
<view class="menu-item" @click="showItemCardsPopup">
|
||
<image class="menu-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHg9IjMiIHk9IjMiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgcng9IjIiIHJ5PSIyIj48L3JlY3Q+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iNCIgLz48cGF0aCBkPSJNMjEgMTVsLTUuNDMtMy4yM2EyIDIgMCAwIDAtMS44NCAwbC0yLjMxIDEuMzciIC8+PC9zdmc+" mode="aspectFit"></image>
|
||
<text class="menu-label">道具卡</text>
|
||
</view>
|
||
<view class="menu-item" @click="showTasksPopup">
|
||
<image class="menu-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik05IDExbDMgM0wyMiA0Ii8+PHBhdGggZD0iTTIxIDEydjdhMiAyIDAgMCAxLTIgMkg1YTIgMiAwIDAgMS0yLTJWNWEyIDIgMCAwIDEgMi0yaDExIi8+PC9zdmc+" mode="aspectFit"></image>
|
||
<text class="menu-label">任务中心</text>
|
||
</view>
|
||
<view class="menu-item" @click="showInvitesPopup">
|
||
<image class="menu-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xNyAyMXYtMmE0IDQgMCAwIDAtNC00SDdhNCA0IDAgMCAwLTQgNHYyIj48L3BhdGg+PGNpcmNsZSBjeD0iMTAiIGN5PSI3IiByPSI0Ij48L2NpcmNsZT48cGF0aCBkPSJNMjMgMjF2LTJhNCA0IDAgMCAwLTMtMy44NyI+PC9wYXRoPjxwYXRoIGQ9Ik0xNiAzLjEzYTQgNCAwIDAgMSAwIDcuNzUiPjwvcGF0aD48L3N2Zz4=" mode="aspectFit"></image>
|
||
<text class="menu-label">邀请记录</text>
|
||
</view>
|
||
<view class="menu-item" @click="toAddresses">
|
||
<image class="menu-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMSAxMGMwIDctOSAxMy05IDEzcy05LTYtOS0xM2E5IDkgMCAwIDEgMTggMHoiIC8+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMCIgcj0iMyIgLz48L3N2Zz4=" mode="aspectFit"></image>
|
||
<text class="menu-label">收货地址</text>
|
||
</view>
|
||
<view class="menu-item" @click="toHelp">
|
||
<image class="menu-icon-img" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIi8+PHBhdGggZD0iTTkuMDkgOWEzIDMgMCAwIDEgNS44MyAxYzAgMi0zIDMtMyAzIi8+PHBhdGggZD0iTTEyIDE3aC4wMSIvPjwvc3ZnPg==" mode="aspectFit"></image>
|
||
<text class="menu-label">帮助中心</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 弹窗组件保持原有逻辑 -->
|
||
<!-- 积分明细弹窗 -->
|
||
<view class="popup-mask" v-if="pointsVisible" @tap="closePointsPopup">
|
||
<view class="popup-content" @tap.stop>
|
||
<view class="popup-header">
|
||
<text class="popup-title">积分明细</text>
|
||
<text class="close-btn" @tap="closePointsPopup">×</text>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="points-list" @scrolltolower="loadMorePoints">
|
||
<view v-if="pointsLoading && pointsList.length === 0" class="status-text">加载中...</view>
|
||
<view v-else-if="!pointsList || pointsList.length === 0" class="status-text">暂无积分记录</view>
|
||
|
||
<view v-for="(item, index) in pointsList" :key="index" class="point-item">
|
||
<view class="point-left">
|
||
<text class="point-desc">{{ getActionText(item.action) }}</text>
|
||
<text class="point-time">{{ formatDate(item.created_at) }}</text>
|
||
</view>
|
||
<view class="point-right">
|
||
<text class="point-amount" :class="{ 'positive': Number(item.points) > 0, 'negative': Number(item.points) < 0 }">
|
||
{{ Number(item.points) > 0 ? '+' : '' }}{{ item.points }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="pointsLoading && pointsList.length > 0" class="loading-more">加载更多...</view>
|
||
<view v-if="!pointsHasMore && pointsList.length > 0" class="no-more">没有更多了</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 优惠券弹窗 -->
|
||
<view class="popup-mask" v-if="couponsVisible" @tap="closeCouponsPopup">
|
||
<view class="popup-content coupon-popup" @tap.stop>
|
||
<view class="popup-header">
|
||
<text class="popup-title">我的优惠券</text>
|
||
<text class="close-btn" @tap="closeCouponsPopup">×</text>
|
||
</view>
|
||
|
||
<view class="popup-tabs coupon-tabs-3">
|
||
<view class="popup-tab" :class="{ active: couponsTab === 1 }" @tap="switchCouponsTab(1)">未使用</view>
|
||
<view class="popup-tab" :class="{ active: couponsTab === 2 }" @tap="switchCouponsTab(2)">已使用</view>
|
||
<view class="popup-tab" :class="{ active: couponsTab === 3 }" @tap="switchCouponsTab(3)">已过期</view>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="coupon-scroll" @scrolltolower="loadMoreCoupons">
|
||
<view v-if="couponsLoading && couponsList.length === 0" class="status-text">加载中...</view>
|
||
<view v-else-if="!couponsList || couponsList.length === 0" class="empty-state">
|
||
<text class="empty-icon">🎟️</text>
|
||
<text class="empty-text">{{ getCouponEmptyText() }}</text>
|
||
</view>
|
||
|
||
<view v-for="(item, index) in couponsList" :key="item.id || index" class="coupon-ticket-v2" :class="getCouponCardClass(item)">
|
||
<!-- 左侧金额区域 -->
|
||
<view class="coupon-left-v2">
|
||
<view class="coupon-remaining">
|
||
<text class="coupon-symbol">¥</text>
|
||
<text class="coupon-amount-num">{{ formatCouponValue(item.remaining ?? item.amount ?? 0) }}</text>
|
||
</view>
|
||
<text class="coupon-label">{{ couponsTab === 1 ? '可用' : (couponsTab === 2 ? '已用' : '过期') }}</text>
|
||
</view>
|
||
|
||
<!-- 中间分割线 -->
|
||
<view class="coupon-divider-v2">
|
||
<view class="divider-notch top"></view>
|
||
<view class="divider-dash"></view>
|
||
<view class="divider-notch bottom"></view>
|
||
</view>
|
||
|
||
<!-- 右侧信息区域 -->
|
||
<view class="coupon-right-v2">
|
||
<view class="coupon-header-row">
|
||
<text class="coupon-name-v2">{{ item.name || '优惠券' }}</text>
|
||
<view class="coupon-original" v-if="item.amount && item.remaining !== undefined && item.remaining !== item.amount">
|
||
<text>原值 ¥{{ formatCouponValue(item.amount) }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<text class="coupon-rules">{{ item.rules || '全场通用' }}</text>
|
||
|
||
<!-- 使用进度条 (仅当有使用记录时显示) -->
|
||
<view class="coupon-progress-wrap" v-if="item.amount && item.remaining !== undefined && item.remaining < item.amount">
|
||
<view class="coupon-progress-bar">
|
||
<view class="coupon-progress-fill" :style="{ width: getCouponUsedPercent(item) + '%' }"></view>
|
||
</view>
|
||
<text class="coupon-progress-text">已用 {{ formatCouponValue(item.amount - item.remaining) }} ({{ getCouponUsedPercent(item) }}%)</text>
|
||
</view>
|
||
|
||
<view class="coupon-footer-row">
|
||
<text class="coupon-expire-v2">{{ formatCouponExpiry(item) }}</text>
|
||
<view class="coupon-action-v2" v-if="couponsTab === 1">
|
||
<text class="use-btn-v2">去使用</text>
|
||
</view>
|
||
<view class="coupon-status-v2" v-else>
|
||
<text class="status-tag" :class="couponsTab === 2 ? 'used' : 'expired'">{{ couponsTab === 2 ? '已使用' : '已过期' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 使用时间 (已使用状态) -->
|
||
<text class="coupon-used-time" v-if="couponsTab === 2 && item.used_at">使用时间:{{ formatDateTime(item.used_at) }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="couponsLoading && couponsList.length > 0" class="loading-more">加载更多...</view>
|
||
<view v-if="!couponsHasMore && couponsList.length > 0" class="no-more">没有更多了</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 道具卡弹窗 -->
|
||
<view class="popup-mask" v-if="itemCardsVisible" @tap="closeItemCardsPopup">
|
||
<view class="popup-content item-cards-popup" @tap.stop>
|
||
<view class="popup-header">
|
||
<text class="popup-title">我的道具卡</text>
|
||
<text class="close-btn" @tap="closeItemCardsPopup">×</text>
|
||
</view>
|
||
|
||
<view class="popup-tabs">
|
||
<view class="popup-tab" :class="{ active: itemCardsTab === 0 }" @tap="switchItemCardsTab(0)">未使用</view>
|
||
<view class="popup-tab" :class="{ active: itemCardsTab === 1 }" @tap="switchItemCardsTab(1)">已使用</view>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="item-cards-scroll">
|
||
<view v-if="itemCardsLoading && itemCardsList.length === 0" class="status-text">加载中...</view>
|
||
<view v-else-if="!itemCardsList || itemCardsList.length === 0" class="empty-state">
|
||
<text class="empty-icon">🃏</text>
|
||
<text class="empty-text">{{ itemCardsTab === 0 ? '暂无可用道具卡' : '暂无使用记录' }}</text>
|
||
</view>
|
||
|
||
<view class="item-cards-grid">
|
||
<view v-for="(item, index) in itemCardsList" :key="index" class="item-card" :class="{ 'used': itemCardsTab === 1 }">
|
||
<view class="card-icon-wrap">
|
||
<text class="card-icon">{{ getCardIcon(item.type || item.name) }}</text>
|
||
</view>
|
||
<view class="card-info">
|
||
<text class="card-name">{{ item.name || item.title || '道具卡' }}</text>
|
||
<text class="card-desc">{{ item.description || item.rules || '可在抽奖时使用' }}</text>
|
||
<text class="card-use-time" v-if="itemCardsTab === 1 && item.used_at">使用时间:{{ formatDate(item.used_at) }}</text>
|
||
</view>
|
||
<view class="card-count-badge" v-if="itemCardsTab === 0">
|
||
<text class="count-num">×{{ item.remaining ?? item.count ?? 1 }}</text>
|
||
</view>
|
||
<view class="card-used-badge" v-else>
|
||
<text class="used-text">已使用</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 任务中心弹窗 -->
|
||
<view class="popup-mask" v-if="tasksVisible" @tap="closeTasksPopup">
|
||
<view class="popup-content task-center-popup" @tap.stop>
|
||
<view class="popup-header">
|
||
<text class="popup-title">任务中心</text>
|
||
<text class="close-btn" @tap="closeTasksPopup">×</text>
|
||
</view>
|
||
|
||
<!-- 总体进度指示器 -->
|
||
<view class="overall-progress">
|
||
<view class="progress-circle">
|
||
<text class="progress-percent">{{ getOverallProgress() }}%</text>
|
||
<text class="progress-label">总完成率</text>
|
||
</view>
|
||
<view class="progress-stats">
|
||
<view class="stat-row">
|
||
<view class="stat-dot done"></view>
|
||
<text class="stat-text">已完成 {{ tasksList.filter(t => t.status === 2).length }} 个</text>
|
||
</view>
|
||
<view class="stat-row">
|
||
<view class="stat-dot ongoing"></view>
|
||
<text class="stat-text">进行中 {{ tasksList.filter(t => t.status === 1).length }} 个</text>
|
||
</view>
|
||
<view class="stat-row">
|
||
<view class="stat-dot waiting"></view>
|
||
<text class="stat-text">未开始 {{ tasksList.filter(t => t.status !== 1 && t.status !== 2).length }} 个</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="task-scroll" @scrolltolower="loadMoreTasks">
|
||
<view v-if="tasksLoading && tasksList.length === 0" class="status-text">加载中...</view>
|
||
<view v-else-if="!tasksList || tasksList.length === 0" class="empty-state">
|
||
<text class="empty-icon">📋</text>
|
||
<text class="empty-text">暂无任务</text>
|
||
</view>
|
||
|
||
<view v-for="(task, idx) in tasksList" :key="task.id || idx" class="task-card-v2" :class="getTaskCardClass(task.status)">
|
||
<!-- 左侧状态指示器 -->
|
||
<view class="task-status-indicator" :class="getTaskStatusClass(task.status)">
|
||
<text class="status-icon">{{ getStatusIcon(task.status) }}</text>
|
||
</view>
|
||
|
||
<view class="task-content">
|
||
<view class="task-title-row">
|
||
<text class="task-title">{{ task.name || '任务' }}</text>
|
||
</view>
|
||
<text class="task-desc-v2">{{ task.description || '完成任务获取奖励' }}</text>
|
||
|
||
<!-- 进度显示 - 始终可见 -->
|
||
<view class="task-progress-v2">
|
||
<view class="progress-bar-v2">
|
||
<view class="progress-fill-v2" :style="{ width: getTaskProgressV2(task) + '%' }"></view>
|
||
</view>
|
||
<text class="progress-num">{{ getTaskProgressV2(task) }}%</text>
|
||
</view>
|
||
|
||
<!-- 奖励显示 -->
|
||
<view class="task-reward-row" v-if="Array.isArray(task.rewards) && task.rewards.length">
|
||
<text class="reward-label">奖励:</text>
|
||
<view class="reward-items">
|
||
<text class="reward-item" v-for="(rw, ri) in task.rewards" :key="ri">
|
||
{{ getRewardIcon(rw.reward_type) }} {{ rw.quantity || 0 }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 右侧操作按钮 -->
|
||
<view class="task-btn-wrap">
|
||
<button class="task-btn" :class="getTaskBtnClass(task.status)" @tap="goToTask(task)">
|
||
{{ getTaskBtnText(task.status) }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="tasksLoading && tasksList.length > 0" class="loading-more">加载更多...</view>
|
||
<view v-if="!tasksHasMore && tasksList.length > 0" class="no-more">没有更多了</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 邀请记录弹窗 (Redesigned) -->
|
||
<view class="popup-mask" v-if="invitesVisible" @tap="closeInvitesPopup">
|
||
<view class="popup-content invites-popup" @tap.stop>
|
||
<view class="invites-popup-header">
|
||
<text class="invites-title-text">✨ 我的邀请团 ✨</text>
|
||
<view class="invites-close" @tap="closeInvitesPopup">
|
||
<image class="invites-close-icon" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM5OTkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMTggNkw2IDE4TTYgNmwxMiAxMiIvPjwvc3ZnPg==" mode="aspectFit"></image>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 头部资产卡片 -->
|
||
<view class="invite-prime-card">
|
||
<!-- 装饰圆环 -->
|
||
<view class="card-deco-circle c1"></view>
|
||
<view class="card-deco-circle c2"></view>
|
||
|
||
<view class="prime-card-content">
|
||
<view class="my-code-section" @tap="copyInviteCode">
|
||
<text class="code-label">我的邀请码</text>
|
||
<view class="code-value-row">
|
||
<text class="code-text">{{ inviteCode || '加载中...' }}</text>
|
||
<view class="copy-icon-btn">
|
||
<text class="copy-text">复制</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="card-divider-h"></view>
|
||
|
||
<view class="stats-mini-row">
|
||
<view class="stat-mini-item">
|
||
<text class="mini-num">{{ invitesTotal }}</text>
|
||
<text class="mini-label">累计邀请 (人)</text>
|
||
</view>
|
||
<view class="stat-mini-sep"></view>
|
||
<view class="stat-mini-item">
|
||
<text class="mini-num">Lv.1</text>
|
||
<text class="mini-label">邀请等级</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 列表标题 -->
|
||
<view class="list-section-header">
|
||
<text class="list-title">好友列表</text>
|
||
<text class="list-subtitle">邀请好友,一起欧气满满</text>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="invites-scroll-refined" @scrolltolower="loadMoreInvites">
|
||
<view v-if="invitesLoading && invitesList.length === 0" class="status-loading-refined">
|
||
<view class="loading-spinner"></view>
|
||
<text>正在加载好友...</text>
|
||
</view>
|
||
|
||
<view v-else-if="!invitesList || invitesList.length === 0" class="empty-state-refined">
|
||
<image class="empty-img-refined" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0REQURFNSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIi8+PHBhdGggZD0iTTE2IDE2czEtMy00LTctNCA3LTQgNyIvPjxsaW5lIHgxPSI5IiB5MT0iOSIgeDI9IjkuMDEiIHkyPSI5Ii8+PGxpbmUgeDE9IjE1IiB5MT0iOSIgeDI9IjE1LjAxIiB5Mj0iOSIvPjwvc3ZnPg==" mode="aspectFit"></image>
|
||
<text class="empty-text-refined">暂无邀请记录</text>
|
||
<text class="empty-sub-refined">每邀请一位好友,双方都能获得奖励哦</text>
|
||
</view>
|
||
|
||
<view v-for="(item, index) in invitesList" :key="item.id || index" class="friend-card">
|
||
<view class="friend-avatar-wrap">
|
||
<image class="friend-avatar" :src="item.avatar || '/static/logo.png'" mode="aspectFill"></image>
|
||
</view>
|
||
<view class="friend-info">
|
||
<view class="friend-name-row">
|
||
<text class="friend-name">{{ item.nickname || '神秘好友' }}</text>
|
||
<view class="friend-tag">新晋</view>
|
||
</view>
|
||
<text class="friend-meta">ID: {{ item.id }} · 邀请码: {{ item.invite_code || '-' }}</text>
|
||
</view>
|
||
<view class="friend-status">
|
||
<text class="status-dot success"></text>
|
||
<text class="status-val">已激活</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="invitesLoading && invitesList.length > 0" class="loading-more-text">加载更多...</view>
|
||
<view v-if="!invitesHasMore && invitesList.length > 0" class="no-more-text">- 到底啦 -</view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部悬浮按钮 -->
|
||
<view class="popup-footer-action">
|
||
<button class="btn-share-gradient" open-type="share">
|
||
<text>立即邀请好友</text>
|
||
<image class="btn-arrow-icon" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNNSAxMmgyNDBNMTIgNWw3IDctNyA3Ii8+PC9zdmc+" mode="aspectFit"></image>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue'
|
||
import { onShow, onLoad, onShareAppMessage } from '@dcloudio/uni-app'
|
||
import { getUserStats, getPointsBalance, getUserPoints, getUserCoupons, redeemCoupon, getItemCards, getTasks, getUserInvites } from '../../api/appUser'
|
||
|
||
const avatar = ref(uni.getStorageSync('avatar') || '')
|
||
const nickname = ref(uni.getStorageSync('nickname') || '')
|
||
const userId = ref(uni.getStorageSync('user_id') || '')
|
||
const phoneNumber = ref(uni.getStorageSync('phone_number') || '')
|
||
const inviteCode = ref(uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || '')
|
||
const pointsBalance = ref(uni.getStorageSync('points_balance') || 0)
|
||
const stats = ref(uni.getStorageSync('user_stats') || {})
|
||
const loading = ref(false)
|
||
const error = ref('')
|
||
|
||
// 积分弹窗相关状态
|
||
const pointsVisible = ref(false)
|
||
const pointsList = ref([])
|
||
const pointsLoading = ref(false)
|
||
const pointsPage = ref(1)
|
||
const pointsPageSize = ref(20)
|
||
const pointsHasMore = ref(true)
|
||
|
||
// 优惠券弹窗相关状态
|
||
const couponsVisible = ref(false)
|
||
const couponsTab = ref(1) // 1: 未使用, 2: 已使用, 3: 已过期
|
||
const couponsList = ref([])
|
||
const couponsLoading = ref(false)
|
||
const couponsPage = ref(1)
|
||
const couponsHasMore = ref(true)
|
||
const redeemCode = ref('')
|
||
|
||
// 道具卡弹窗相关状态
|
||
const itemCardsVisible = ref(false)
|
||
const itemCardsList = ref([])
|
||
const itemCardsLoading = ref(false)
|
||
const itemCardsTab = ref(0) // 0: 未使用, 1: 已使用
|
||
|
||
const tasksVisible = ref(false)
|
||
const tasksList = ref([])
|
||
const tasksLoading = ref(false)
|
||
const tasksPage = ref(1)
|
||
const tasksPageSize = ref(20)
|
||
const tasksHasMore = ref(true)
|
||
|
||
// 邀请记录弹窗相关状态
|
||
const invitesVisible = ref(false)
|
||
const invitesList = ref([])
|
||
const invitesLoading = ref(false)
|
||
const invitesPage = ref(1)
|
||
const invitesPageSize = ref(20)
|
||
const invitesHasMore = ref(true)
|
||
const invitesTotal = ref(0)
|
||
|
||
async function refresh() {
|
||
const token = uni.getStorageSync('token')
|
||
// 允许未登录状态浏览基本页面结构,用户信息显示“未登录”
|
||
if (!token) {
|
||
nickname.value = ''
|
||
avatar.value = ''
|
||
return
|
||
}
|
||
const user_id = uni.getStorageSync('user_id')
|
||
loading.value = true
|
||
try {
|
||
const s = await getUserStats(user_id)
|
||
stats.value = s || {}
|
||
uni.setStorageSync('user_stats', stats.value)
|
||
const b = await getPointsBalance(user_id)
|
||
const balance = b && b.balance !== undefined ? b.balance : b
|
||
pointsBalance.value = balance || 0
|
||
uni.setStorageSync('points_balance', pointsBalance.value)
|
||
} catch (e) {
|
||
console.error(e)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function handleJoin() {
|
||
const token = uni.getStorageSync('token')
|
||
if (!token) {
|
||
uni.navigateTo({ url: '/pages/login/index' })
|
||
} else {
|
||
uni.showToast({ title: '已登录', icon: 'none' })
|
||
}
|
||
}
|
||
|
||
function signCheckin() {
|
||
uni.showToast({ title: '签到功能开发中', icon: 'none' })
|
||
}
|
||
|
||
function toOrders(status) {
|
||
const s = status === 'completed' ? 'completed' : 'pending' // 简单映射
|
||
uni.navigateTo({ url: `/pages/orders/index?status=${s}` })
|
||
}
|
||
|
||
function toAddresses() {
|
||
uni.navigateTo({ url: '/pages/address/index' })
|
||
}
|
||
|
||
function toHelp() {
|
||
uni.navigateTo({ url: '/pages/help/index' })
|
||
}
|
||
|
||
function handleInvite() {
|
||
// Trigger WeChat share menu for inviting friends
|
||
// #ifdef MP-WEIXIN
|
||
wx.showShareMenu({
|
||
withShareTicket: true,
|
||
menus: ['shareAppMessage', 'shareTimeline']
|
||
})
|
||
uni.showToast({ title: '点击右上角分享给好友', icon: 'none', duration: 2000 })
|
||
// #endif
|
||
// #ifndef MP-WEIXIN
|
||
uni.showToast({ title: '请点击右上角分享', icon: 'none' })
|
||
// #endif
|
||
}
|
||
|
||
// 积分弹窗逻辑
|
||
function showPointsPopup() {
|
||
pointsVisible.value = true
|
||
if (pointsList.value.length === 0) {
|
||
pointsPage.value = 1
|
||
pointsHasMore.value = true
|
||
loadPoints()
|
||
}
|
||
}
|
||
function closePointsPopup() { pointsVisible.value = false }
|
||
async function loadPoints() {
|
||
if (pointsLoading.value) return
|
||
pointsLoading.value = true
|
||
const user_id = uni.getStorageSync('user_id')
|
||
try {
|
||
const res = await getUserPoints(user_id, pointsPage.value, pointsPageSize.value)
|
||
let list = []
|
||
let total = 0
|
||
if (res && Array.isArray(res.list)) { list = res.list; total = res.total || 0 }
|
||
else if (res && Array.isArray(res.data)) { list = res.data; total = res.total || 0 }
|
||
else if (Array.isArray(res)) { list = res; total = res.length }
|
||
|
||
if (pointsPage.value === 1) pointsList.value = list
|
||
else pointsList.value = [...pointsList.value, ...list]
|
||
|
||
if (list.length < pointsPageSize.value || (pointsPage.value * pointsPageSize.value >= total && total > 0)) pointsHasMore.value = false
|
||
else pointsPage.value += 1
|
||
|
||
if (list.length === 0 && pointsPage.value === 1) pointsHasMore.value = false
|
||
} catch (e) {
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
pointsLoading.value = false
|
||
}
|
||
}
|
||
function loadMorePoints() { if (pointsHasMore.value && !pointsLoading.value) loadPoints() }
|
||
|
||
// 优惠券弹窗逻辑
|
||
function showCouponsPopup() {
|
||
couponsVisible.value = true
|
||
couponsTab.value = 1 // 默认显示未使用 (status=1)
|
||
redeemCode.value = ''
|
||
couponsList.value = []
|
||
couponsPage.value = 1
|
||
couponsHasMore.value = true
|
||
loadCoupons()
|
||
}
|
||
function closeCouponsPopup() { couponsVisible.value = false }
|
||
function switchCouponsTab(status) {
|
||
if (couponsTab.value === status) return
|
||
couponsTab.value = status
|
||
couponsList.value = []
|
||
couponsPage.value = 1
|
||
couponsHasMore.value = true
|
||
loadCoupons()
|
||
}
|
||
async function loadCoupons() {
|
||
if (couponsLoading.value) return
|
||
couponsLoading.value = true
|
||
const user_id = uni.getStorageSync('user_id')
|
||
try {
|
||
// status: 1=未使用, 2=已使用, 3=已过期
|
||
const res = await getUserCoupons(user_id, couponsTab.value, couponsPage.value, 20)
|
||
let list = []
|
||
let total = 0
|
||
if (res && Array.isArray(res.list)) { list = res.list; total = res.total || 0 }
|
||
else if (res && Array.isArray(res.data)) { list = res.data; total = res.total || 0 }
|
||
else if (Array.isArray(res)) { list = res; total = res.length }
|
||
|
||
if (couponsPage.value === 1) couponsList.value = list
|
||
else couponsList.value = [...couponsList.value, ...list]
|
||
|
||
if (list.length < 20 || (total > 0 && couponsList.value.length >= total)) couponsHasMore.value = false
|
||
else couponsPage.value += 1
|
||
|
||
if (list.length === 0 && couponsPage.value === 1) couponsHasMore.value = false
|
||
} catch (e) {
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
couponsLoading.value = false
|
||
}
|
||
}
|
||
function loadMoreCoupons() { if (couponsHasMore.value && !couponsLoading.value) loadCoupons() }
|
||
|
||
// 优惠券辅助函数
|
||
function getCouponEmptyText() {
|
||
if (couponsTab.value === 1) return '暂无可用优惠券'
|
||
if (couponsTab.value === 2) return '暂无已使用优惠券'
|
||
return '暂无已过期优惠券'
|
||
}
|
||
|
||
function getCouponCardClass(item) {
|
||
if (couponsTab.value === 3) return 'expired'
|
||
if (couponsTab.value === 2) return 'used'
|
||
return ''
|
||
}
|
||
|
||
function getCouponUsedPercent(item) {
|
||
if (!item.amount || item.remaining === undefined) return 0
|
||
const used = item.amount - item.remaining
|
||
return Math.round((used / item.amount) * 100)
|
||
}
|
||
|
||
function formatCouponExpiry(item) {
|
||
const end = item.valid_end || item.end_time
|
||
if (!end) return ''
|
||
return `有效期至:${formatDate(end)}`
|
||
}
|
||
|
||
function formatDateTime(dateStr) {
|
||
if (!dateStr) return ''
|
||
const date = new Date(dateStr)
|
||
if (isNaN(date.getTime())) return dateStr
|
||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
||
}
|
||
|
||
async function handleRedeem() {
|
||
if (!redeemCode.value) { uni.showToast({ title: '请输入兑换码', icon: 'none' }); return }
|
||
const user_id = uni.getStorageSync('user_id')
|
||
try {
|
||
await redeemCoupon(user_id, redeemCode.value)
|
||
uni.showToast({ title: '兑换成功', icon: 'success' })
|
||
redeemCode.value = ''
|
||
refresh()
|
||
} catch (e) {
|
||
uni.showToast({ title: e.message || '兑换失败', icon: 'none' })
|
||
}
|
||
}
|
||
|
||
// 道具卡弹窗逻辑
|
||
function showItemCardsPopup() {
|
||
itemCardsVisible.value = true
|
||
itemCardsTab.value = 0
|
||
itemCardsList.value = []
|
||
loadItemCards()
|
||
}
|
||
function closeItemCardsPopup() { itemCardsVisible.value = false }
|
||
function switchItemCardsTab(index) {
|
||
if (itemCardsTab.value === index) return
|
||
itemCardsTab.value = index
|
||
itemCardsList.value = []
|
||
loadItemCards()
|
||
}
|
||
async function loadItemCards() {
|
||
if (itemCardsLoading.value) return
|
||
itemCardsLoading.value = true
|
||
const user_id = uni.getStorageSync('user_id')
|
||
try {
|
||
const status = itemCardsTab.value // 0: 未使用, 1: 已使用
|
||
const res = await getItemCards(user_id, status)
|
||
let list = []
|
||
if (res && Array.isArray(res.list)) list = res.list
|
||
else if (res && Array.isArray(res.data)) list = res.data
|
||
else if (Array.isArray(res)) list = res
|
||
itemCardsList.value = list
|
||
} catch (e) {
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
itemCardsLoading.value = false
|
||
}
|
||
}
|
||
|
||
function showTasksPopup() {
|
||
tasksVisible.value = true
|
||
if (tasksList.value.length === 0) {
|
||
tasksPage.value = 1
|
||
tasksHasMore.value = true
|
||
loadTasks()
|
||
}
|
||
}
|
||
function closeTasksPopup() { tasksVisible.value = false }
|
||
async function loadTasks() {
|
||
if (tasksLoading.value) return
|
||
tasksLoading.value = true
|
||
try {
|
||
const res = await getTasks(tasksPage.value, tasksPageSize.value)
|
||
let list = []
|
||
let total = 0
|
||
if (res && Array.isArray(res.list)) { list = res.list; total = res.total || 0 }
|
||
else if (res && Array.isArray(res.data)) { list = res.data; total = res.total || 0 }
|
||
else if (Array.isArray(res)) { list = res; total = res.length }
|
||
if (tasksPage.value === 1) tasksList.value = list
|
||
else tasksList.value = [...tasksList.value, ...list]
|
||
if (list.length < tasksPageSize.value || (tasksPage.value * tasksPageSize.value >= total && total > 0)) tasksHasMore.value = false
|
||
else tasksPage.value += 1
|
||
} catch (e) {
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
tasksLoading.value = false
|
||
}
|
||
}
|
||
function loadMoreTasks() { if (tasksHasMore.value && !tasksLoading.value) loadTasks() }
|
||
|
||
// 邀请记录弹窗逻辑
|
||
function showInvitesPopup() {
|
||
const token = uni.getStorageSync('token')
|
||
if (!token) {
|
||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||
return
|
||
}
|
||
invitesVisible.value = true
|
||
invitesList.value = []
|
||
invitesPage.value = 1
|
||
invitesHasMore.value = true
|
||
loadInvites()
|
||
}
|
||
|
||
function closeInvitesPopup() { invitesVisible.value = false }
|
||
|
||
async function loadInvites() {
|
||
if (invitesLoading.value) return
|
||
invitesLoading.value = true
|
||
const user_id = uni.getStorageSync('user_id')
|
||
try {
|
||
const res = await getUserInvites(user_id, invitesPage.value, invitesPageSize.value)
|
||
let list = []
|
||
let total = 0
|
||
if (res && Array.isArray(res.list)) {
|
||
list = res.list
|
||
total = res.total || 0
|
||
} else if (res && Array.isArray(res.data)) {
|
||
list = res.data
|
||
total = res.total || 0
|
||
} else if (Array.isArray(res)) {
|
||
list = res
|
||
total = res.length
|
||
}
|
||
|
||
invitesTotal.value = total
|
||
|
||
if (invitesPage.value === 1) invitesList.value = list
|
||
else invitesList.value = [...invitesList.value, ...list]
|
||
|
||
if (list.length < invitesPageSize.value || (invitesPage.value * invitesPageSize.value >= total && total > 0)) {
|
||
invitesHasMore.value = false
|
||
} else {
|
||
invitesPage.value += 1
|
||
}
|
||
|
||
if (list.length === 0 && invitesPage.value === 1) invitesHasMore.value = false
|
||
} catch (e) {
|
||
console.error('加载邀请记录失败:', e)
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
invitesLoading.value = false
|
||
}
|
||
}
|
||
|
||
function loadMoreInvites() {
|
||
if (invitesHasMore.value && !invitesLoading.value) loadInvites()
|
||
}
|
||
|
||
function copyInviteCode() {
|
||
if (!inviteCode.value) {
|
||
uni.showToast({ title: '暂无邀请码', icon: 'none' })
|
||
return
|
||
}
|
||
uni.setClipboardData({
|
||
data: inviteCode.value,
|
||
success: () => {
|
||
uni.showToast({ title: '复制成功', icon: 'success' })
|
||
},
|
||
fail: () => {
|
||
uni.showToast({ title: '复制失败', icon: 'none' })
|
||
}
|
||
})
|
||
}
|
||
|
||
// 辅助函数
|
||
function formatCouponValue(value) {
|
||
const num = Number(value || 0)
|
||
return num.toFixed(0)
|
||
}
|
||
|
||
function getCardIcon(typeOrName) {
|
||
const name = String(typeOrName || '').toLowerCase()
|
||
if (name.includes('双倍') || name.includes('double')) return '✨'
|
||
if (name.includes('保底') || name.includes('guarantee')) return '🛡️'
|
||
if (name.includes('折扣') || name.includes('discount')) return '💰'
|
||
if (name.includes('免费') || name.includes('free')) return '🎁'
|
||
if (name.includes('加成') || name.includes('boost')) return '🚀'
|
||
return '🃏'
|
||
}
|
||
|
||
// 任务中心辅助函数
|
||
function getTaskIcon(typeOrName) {
|
||
const name = String(typeOrName || '').toLowerCase()
|
||
if (name.includes('签到') || name.includes('checkin')) return '📅'
|
||
if (name.includes('邀请') || name.includes('invite')) return '👥'
|
||
if (name.includes('分享') || name.includes('share')) return '📤'
|
||
if (name.includes('抽奖') || name.includes('lottery') || name.includes('draw')) return '🎲'
|
||
if (name.includes('充值') || name.includes('recharge')) return '💳'
|
||
if (name.includes('购买') || name.includes('buy')) return '🛒'
|
||
if (name.includes('完善') || name.includes('profile')) return '📝'
|
||
if (name.includes('新人') || name.includes('新手')) return '🌟'
|
||
return '📋'
|
||
}
|
||
|
||
function getTaskStatusClass(status) {
|
||
const n = Number(status)
|
||
if (n === 1) return 'status-ongoing'
|
||
if (n === 2) return 'status-done'
|
||
return 'status-waiting'
|
||
}
|
||
|
||
function getTaskProgress(task) {
|
||
if (task.progress !== undefined) return Math.min(100, Math.max(0, task.progress))
|
||
if (task.current !== undefined && task.target) {
|
||
return Math.min(100, Math.max(0, (task.current / task.target) * 100))
|
||
}
|
||
return 0
|
||
}
|
||
|
||
function getRewardIcon(rewardType) {
|
||
const type = String(rewardType || '').toLowerCase()
|
||
if (type.includes('积分') || type.includes('point')) return '💰'
|
||
if (type.includes('优惠券') || type.includes('coupon')) return '🎟️'
|
||
if (type.includes('道具') || type.includes('card') || type.includes('prop')) return '🃏'
|
||
if (type.includes('现金') || type.includes('cash')) return '💵'
|
||
return '🎁'
|
||
}
|
||
|
||
function goToTask(task) {
|
||
const status = Number(task.status)
|
||
|
||
// 已完成的任务不跳转
|
||
if (status === 2) {
|
||
uni.showToast({ title: '任务已完成', icon: 'success' })
|
||
return
|
||
}
|
||
|
||
closeTasksPopup()
|
||
|
||
const name = String(task.name || '').toLowerCase()
|
||
const type = String(task.type || '').toLowerCase()
|
||
|
||
// 根据任务类型跳转到对应页面
|
||
if (name.includes('邀请') || type.includes('invite')) {
|
||
handleInvite()
|
||
} else if (name.includes('签到') || type.includes('checkin')) {
|
||
uni.showToast({ title: '签到功能开发中', icon: 'none' })
|
||
} else if (name.includes('抽奖') || name.includes('抽取') || type.includes('draw') || type.includes('lottery')) {
|
||
uni.switchTab({ url: '/pages/index/index' })
|
||
} else if (name.includes('充值') || type.includes('recharge')) {
|
||
uni.navigateTo({ url: '/pages/points/index' })
|
||
} else if (name.includes('商城') || name.includes('购买') || type.includes('shop') || type.includes('buy')) {
|
||
uni.switchTab({ url: '/pages/shop/index' })
|
||
} else if (name.includes('完善') || name.includes('资料') || type.includes('profile')) {
|
||
uni.showToast({ title: '请完善个人资料', icon: 'none' })
|
||
} else {
|
||
// 默认跳转到首页
|
||
uni.switchTab({ url: '/pages/index/index' })
|
||
}
|
||
}
|
||
|
||
// 任务中心新增辅助函数
|
||
function getOverallProgress() {
|
||
if (!tasksList.value || tasksList.value.length === 0) return 0
|
||
const completed = tasksList.value.filter(t => t.status === 2).length
|
||
return Math.round((completed / tasksList.value.length) * 100)
|
||
}
|
||
|
||
function getTaskProgressV2(task) {
|
||
const status = Number(task.status)
|
||
if (status === 2) return 100
|
||
if (task.progress !== undefined) return Math.min(100, Math.max(0, Math.round(task.progress)))
|
||
if (task.current !== undefined && task.target) {
|
||
return Math.min(100, Math.max(0, Math.round((task.current / task.target) * 100)))
|
||
}
|
||
if (status === 1) return 50 // 进行中默认50%
|
||
return 0
|
||
}
|
||
|
||
function getStatusIcon(status) {
|
||
const n = Number(status)
|
||
if (n === 2) return '✓'
|
||
if (n === 1) return '▶'
|
||
return '○'
|
||
}
|
||
|
||
function getTaskCardClass(status) {
|
||
const n = Number(status)
|
||
if (n === 2) return 'task-completed'
|
||
if (n === 1) return 'task-ongoing'
|
||
return 'task-waiting'
|
||
}
|
||
|
||
function getTaskBtnClass(status) {
|
||
const n = Number(status)
|
||
if (n === 2) return 'btn-done'
|
||
if (n === 1) return 'btn-go'
|
||
return 'btn-waiting'
|
||
}
|
||
|
||
function getTaskBtnText(status) {
|
||
const n = Number(status)
|
||
if (n === 2) return '已完成'
|
||
if (n === 1) return '去完成'
|
||
return '未开始'
|
||
}
|
||
|
||
function formatDate(dateStr) {
|
||
if (!dateStr) return ''
|
||
const date = new Date(dateStr)
|
||
if (isNaN(date.getTime())) return dateStr
|
||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
||
}
|
||
function getActionText(action) {
|
||
const map = { 'manual_add': '商品兑换积分', 'manual_sub': '系统扣除', 'register': '注册奖励', 'lottery_cost': '抽奖消耗', 'checkin': '签到奖励' }
|
||
return map[action] || action || '积分变动'
|
||
}
|
||
function formatStatus(s) {
|
||
const n = Number(s)
|
||
if (n === 1) return '进行中'
|
||
if (n === 2) return '已结束'
|
||
if (n === 0) return '未开始'
|
||
return '未知'
|
||
}
|
||
|
||
onShow(() => {
|
||
avatar.value = uni.getStorageSync('avatar') || avatar.value
|
||
nickname.value = uni.getStorageSync('nickname') || nickname.value
|
||
userId.value = uni.getStorageSync('user_id') || userId.value
|
||
refresh()
|
||
})
|
||
onLoad(() => { refresh() })
|
||
onShareAppMessage(() => {
|
||
return { title: '邀请你一起来加入', path: `/pages/index/index?invite_code=${inviteCode.value}`, imageUrl: '/static/logo.png' }
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* Page Wrap - Creamy Background */
|
||
.wrap {
|
||
min-height: 100vh;
|
||
background: #F8F5F2; /* Cream background close to reference */
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
/* Header Section */
|
||
.header-section {
|
||
padding: 40rpx 32rpx 32rpx;
|
||
background: #F8F5F2;
|
||
}
|
||
|
||
.user-info {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
.avatar {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 50%;
|
||
border: 4rpx solid #FFFFFF;
|
||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
|
||
background: #eee;
|
||
}
|
||
.user-meta {
|
||
flex: 1;
|
||
margin-left: 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.name-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
.nickname {
|
||
font-size: 36rpx;
|
||
font-weight: 800;
|
||
color: #1A1A1A;
|
||
margin-right: 16rpx;
|
||
}
|
||
.level-badge {
|
||
display: flex;
|
||
align-items: center;
|
||
background: #FFF0E5;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
.level-icon { width: 24rpx; height: 24rpx; margin-right: 4rpx; }
|
||
.level-text { font-size: 22rpx; color: #FF6B00; font-weight: 700; }
|
||
|
||
.progress-container {
|
||
width: 100%;
|
||
}
|
||
.progress-bar {
|
||
height: 8rpx;
|
||
background: #EAEAEA;
|
||
border-radius: 4rpx;
|
||
overflow: hidden;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
.progress-fill {
|
||
background: #FFD700;
|
||
height: 100%;
|
||
border-radius: 4rpx;
|
||
}
|
||
.progress-text {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
.userid {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.join-btn {
|
||
background: #FFD700;
|
||
color: #1A1A1A;
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
padding: 12rpx 32rpx;
|
||
border-radius: 999rpx;
|
||
border: 2rpx solid #1A1A1A;
|
||
box-shadow: 2rpx 2rpx 0 #1A1A1A;
|
||
}
|
||
.join-btn:active { transform: translate(1rpx, 1rpx); box-shadow: none; }
|
||
|
||
/* Stats Row */
|
||
.stats-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 0 32rpx; /* Increased padding */
|
||
}
|
||
.stat-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
flex: 1;
|
||
}
|
||
.stat-num {
|
||
font-size: 36rpx;
|
||
font-weight: 800;
|
||
color: #1A1A1A;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
.stat-label {
|
||
font-size: 24rpx;
|
||
color: #888;
|
||
}
|
||
|
||
/* VIP Banner (Removed) */
|
||
|
||
/* Invite Banner */
|
||
.invite-banner {
|
||
margin: 0 32rpx 32rpx;
|
||
background: linear-gradient(135deg, #FFEFD5 0%, #FFF5E6 50%, #FFFFFF 100%);
|
||
border-radius: 24rpx;
|
||
padding: 32rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
position: relative;
|
||
overflow: hidden;
|
||
box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.05);
|
||
}
|
||
.invite-info { z-index: 2; flex: 1; }
|
||
.invite-tag { background: #FF6B00; color: #fff; font-size: 20rpx; padding: 4rpx 12rpx; border-radius: 8rpx; display: inline-block; margin-bottom: 8rpx; }
|
||
.invite-title { font-size: 32rpx; font-weight: 800; color: #1A1A1A; margin-bottom: 8rpx; }
|
||
.invite-desc { font-size: 24rpx; color: #666; }
|
||
.invite-action { display: flex; align-items: center; background: #FFD700; padding: 12rpx 24rpx; border-radius: 999rpx; z-index: 2; box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.4); }
|
||
.invite-btn-text { font-size: 24rpx; font-weight: 700; color: #1A1A1A; margin-right: 4rpx; }
|
||
.invite-arrow { width: 24rpx; height: 24rpx; }
|
||
.invite-bg-icon { position: absolute; right: 0; bottom: -10rpx; width: 140rpx; height: 140rpx; opacity: 0.1; transform: rotate(-15deg); pointer-events: none; }
|
||
|
||
/* Card Section */
|
||
.card-section {
|
||
background: #FFFFFF;
|
||
margin: 0 32rpx 32rpx;
|
||
border-radius: 24rpx;
|
||
padding: 32rpx 24rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.03);
|
||
}
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #1A1A1A;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
/* Grid Layout */
|
||
.grid-row, .grid-menu {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
.grid-menu {
|
||
margin-top: -16rpx; /* Adjust for spacing */
|
||
}
|
||
|
||
.grid-item {
|
||
width: 25%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
.menu-item {
|
||
width: 25%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-top: 32rpx;
|
||
}
|
||
|
||
.grid-icon-img, .menu-icon-img {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
margin-bottom: 16rpx;
|
||
/* background-color: #FFF0E5; */
|
||
/* border-radius: 12rpx; */
|
||
/* padding: 8rpx; */
|
||
/* This can be uncommented for background circles */
|
||
}
|
||
|
||
.grid-label, .menu-label {
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
}
|
||
|
||
/* Popup Styles (simplified/inherited) */
|
||
.popup-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); z-index: 999; display: flex; align-items: center; justify-content: center; }
|
||
.popup-content { width: 600rpx; max-height: 80vh; background: #FFF; border-radius: 24rpx; padding: 32rpx; display: flex; flex-direction: column; }
|
||
.popup-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24rpx; }
|
||
.popup-title { font-size: 32rpx; font-weight: 700; }
|
||
.close-btn { font-size: 40rpx; padding: 0 10rpx; color: #999; }
|
||
.points-list { flex: 1; height: 0; min-height: 400rpx; overflow-y: scroll; }
|
||
.status-text { text-align: center; color: #999; padding: 40rpx 0; }
|
||
.point-item, .coupon-item { display: flex; justify-content: space-between; padding: 24rpx 0; border-bottom: 1rpx solid #eee; }
|
||
.coupon-item { align-items: flex-start; }
|
||
.coupon-left { flex: 1; display: flex; flex-direction: column; }
|
||
.point-desc, .coupon-name { font-size: 28rpx; color: #333; margin-bottom: 8rpx; }
|
||
.task-name { font-size: 30rpx; font-weight: 700; }
|
||
.point-time, .coupon-time, .coupon-desc, .task-desc { font-size: 22rpx; color: #999; }
|
||
.point-amount { font-size: 32rpx; font-weight: 700; color: #333; }
|
||
.positive { color: #ff6b00; }
|
||
.coupon-right, .task-right { display: flex; flex-direction: column; align-items: flex-end; justify-content: center; }
|
||
.coupon-amount-wrapper { display: flex; align-items: baseline; color: #ff4d4f; }
|
||
.symbol { font-size: 24rpx; }
|
||
.amount-value { font-size: 40rpx; font-weight: 700; }
|
||
.coupon-status, .task-status { font-size: 22rpx; color: #666; margin-top: 8rpx; background: #eee; padding: 4rpx 12rpx; border-radius: 8rpx;}
|
||
.popup-tabs { display: flex; margin-bottom: 24rpx; border-bottom: 1rpx solid #eee; }
|
||
.popup-tab { flex: 1; text-align: center; padding: 16rpx 0; font-size: 28rpx; color: #666; position: relative; }
|
||
.popup-tab.active { color: #ff6b00; font-weight: 700; }
|
||
.popup-tab.active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 40rpx; height: 4rpx; background: #ff6b00; border-radius: 2rpx; }
|
||
.redeem-container { display: flex; margin-top: 24rpx; }
|
||
.redeem-input { flex: 1; background: #f5f5f5; height: 80rpx; border-radius: 12rpx; padding: 0 24rpx; font-size: 28rpx; }
|
||
.redeem-btn { margin-left: 16rpx; background: #ff6b00; color: #fff; font-size: 28rpx; height: 80rpx; line-height: 80rpx; padding: 0 32rpx; border-radius: 12rpx; }
|
||
.reward-list { margin-top: 8rpx; display: flex; align-items: center; }
|
||
.reward-label { font-size: 20rpx; color: #ff6b00; margin-right: 8rpx; background: #FFF0E5; padding: 2rpx 8rpx; border-radius: 6rpx; }
|
||
.reward-tags { display: flex; flex-wrap: wrap; gap: 8rpx; }
|
||
.reward-item { font-size: 20rpx; color: #666; background: #f0f0f0; padding: 2rpx 8rpx; border-radius: 6rpx; }
|
||
|
||
/* ============================================
|
||
优惠券弹窗样式
|
||
============================================ */
|
||
.coupon-popup { width: 680rpx; }
|
||
.coupon-scroll { flex: 1; min-height: 400rpx; max-height: 60vh; }
|
||
|
||
/* 空状态 */
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 80rpx 0;
|
||
}
|
||
.empty-icon { font-size: 80rpx; margin-bottom: 24rpx; }
|
||
.empty-text { font-size: 28rpx; color: #999; }
|
||
|
||
/* 兑换区域 */
|
||
.redeem-section {
|
||
padding: 24rpx 0;
|
||
}
|
||
.redeem-input-wrap {
|
||
background: #F5F5F5;
|
||
border-radius: 16rpx;
|
||
padding: 4rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
.redeem-section .redeem-input {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
background: transparent;
|
||
padding: 0 24rpx;
|
||
font-size: 30rpx;
|
||
}
|
||
.redeem-section .redeem-btn {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
background: linear-gradient(135deg, #FF9F43, #FF6B35);
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
border-radius: 44rpx;
|
||
text-align: center;
|
||
border: none;
|
||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 53, 0.3);
|
||
}
|
||
.redeem-tips {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 32rpx;
|
||
padding: 20rpx;
|
||
background: #FFF8F3;
|
||
border-radius: 12rpx;
|
||
}
|
||
.tip-icon { font-size: 32rpx; margin-right: 12rpx; }
|
||
.tip-text { font-size: 24rpx; color: #FF6B35; }
|
||
|
||
/* 优惠券票券样式 */
|
||
.coupon-ticket {
|
||
display: flex;
|
||
background: linear-gradient(135deg, #FFF8F3, #FFFFFF);
|
||
border-radius: 16rpx;
|
||
margin-bottom: 20rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
position: relative;
|
||
}
|
||
.coupon-ticket.used {
|
||
background: #F5F5F5;
|
||
opacity: 0.7;
|
||
}
|
||
.coupon-ticket.used .coupon-left-section {
|
||
background: #E0E0E0;
|
||
}
|
||
|
||
.coupon-left-section {
|
||
width: 180rpx;
|
||
background: linear-gradient(135deg, #FF9F43, #FF6B35);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 24rpx 16rpx;
|
||
}
|
||
.coupon-value {
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
.coupon-symbol {
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
font-weight: 700;
|
||
}
|
||
.coupon-amount {
|
||
font-size: 56rpx;
|
||
font-weight: 800;
|
||
color: #fff;
|
||
}
|
||
.coupon-condition {
|
||
font-size: 20rpx;
|
||
color: rgba(255,255,255,0.85);
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.coupon-divider {
|
||
width: 24rpx;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
.divider-circle {
|
||
width: 24rpx;
|
||
height: 12rpx;
|
||
background: #F8F5F2;
|
||
position: absolute;
|
||
}
|
||
.divider-circle.top { top: -1rpx; border-radius: 0 0 12rpx 12rpx; }
|
||
.divider-circle.bottom { bottom: -1rpx; border-radius: 12rpx 12rpx 0 0; }
|
||
.divider-line {
|
||
width: 2rpx;
|
||
flex: 1;
|
||
background: repeating-linear-gradient(to bottom, #E5E5E5 0, #E5E5E5 8rpx, transparent 8rpx, transparent 16rpx);
|
||
margin: 16rpx 0;
|
||
}
|
||
|
||
.coupon-right-section {
|
||
flex: 1;
|
||
padding: 20rpx 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
}
|
||
.coupon-right-section .coupon-name {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #1F2937;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
.coupon-right-section .coupon-desc {
|
||
font-size: 22rpx;
|
||
color: #6B7280;
|
||
margin-bottom: 6rpx;
|
||
}
|
||
.coupon-expire {
|
||
font-size: 20rpx;
|
||
color: #9CA3AF;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
.coupon-action {
|
||
align-self: flex-end;
|
||
}
|
||
.use-btn {
|
||
display: inline-block;
|
||
background: linear-gradient(135deg, #FFD166, #FF9F43);
|
||
color: #1F2937;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
padding: 10rpx 24rpx;
|
||
border-radius: 20rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 159, 67, 0.3);
|
||
}
|
||
.coupon-status-badge {
|
||
align-self: flex-end;
|
||
background: #E5E5E5;
|
||
color: #999;
|
||
font-size: 20rpx;
|
||
padding: 6rpx 16rpx;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
/* ============================================
|
||
优惠券 V2 样式 (余额型优惠券)
|
||
============================================ */
|
||
.coupon-tabs-3 {
|
||
display: flex;
|
||
gap: 0;
|
||
}
|
||
.coupon-tabs-3 .popup-tab {
|
||
flex: 1;
|
||
}
|
||
|
||
.coupon-ticket-v2 {
|
||
display: flex;
|
||
background: #FFFFFF;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 20rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
position: relative;
|
||
}
|
||
|
||
.coupon-ticket-v2.used {
|
||
background: #F9FAFB;
|
||
}
|
||
.coupon-ticket-v2.used .coupon-left-v2 {
|
||
background: linear-gradient(135deg, #9CA3AF, #6B7280);
|
||
}
|
||
|
||
.coupon-ticket-v2.expired {
|
||
background: #F3F4F6;
|
||
opacity: 0.75;
|
||
}
|
||
.coupon-ticket-v2.expired .coupon-left-v2 {
|
||
background: linear-gradient(135deg, #D1D5DB, #9CA3AF);
|
||
}
|
||
|
||
/* 左侧金额区域 */
|
||
.coupon-left-v2 {
|
||
width: 160rpx;
|
||
background: linear-gradient(135deg, #FF9F43, #FF6B35);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 24rpx 12rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.coupon-remaining {
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
|
||
.coupon-left-v2 .coupon-symbol {
|
||
font-size: 24rpx;
|
||
color: #fff;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.coupon-amount-num {
|
||
font-size: 48rpx;
|
||
font-weight: 800;
|
||
color: #fff;
|
||
line-height: 1;
|
||
}
|
||
|
||
.coupon-label {
|
||
font-size: 20rpx;
|
||
color: rgba(255,255,255,0.9);
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
/* 中间分割线 V2 */
|
||
.coupon-divider-v2 {
|
||
width: 20rpx;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.divider-notch {
|
||
width: 20rpx;
|
||
height: 10rpx;
|
||
background: #F8F5F2;
|
||
position: absolute;
|
||
}
|
||
.divider-notch.top { top: -1rpx; border-radius: 0 0 10rpx 10rpx; }
|
||
.divider-notch.bottom { bottom: -1rpx; border-radius: 10rpx 10rpx 0 0; }
|
||
|
||
.divider-dash {
|
||
width: 2rpx;
|
||
flex: 1;
|
||
background: repeating-linear-gradient(to bottom, #E5E7EB 0, #E5E7EB 6rpx, transparent 6rpx, transparent 12rpx);
|
||
margin: 14rpx 0;
|
||
}
|
||
|
||
/* 右侧信息区域 */
|
||
.coupon-right-v2 {
|
||
flex: 1;
|
||
padding: 16rpx 20rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
min-width: 0;
|
||
}
|
||
|
||
.coupon-header-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 6rpx;
|
||
}
|
||
|
||
.coupon-name-v2 {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #1F2937;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.coupon-original {
|
||
background: #FEF3C7;
|
||
padding: 4rpx 10rpx;
|
||
border-radius: 8rpx;
|
||
margin-left: 12rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
.coupon-original text {
|
||
font-size: 18rpx;
|
||
color: #D97706;
|
||
text-decoration: line-through;
|
||
}
|
||
|
||
.coupon-rules {
|
||
font-size: 22rpx;
|
||
color: #6B7280;
|
||
margin-bottom: 8rpx;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* 使用进度条 */
|
||
.coupon-progress-wrap {
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.coupon-progress-bar {
|
||
height: 8rpx;
|
||
background: #E5E7EB;
|
||
border-radius: 4rpx;
|
||
overflow: hidden;
|
||
margin-bottom: 4rpx;
|
||
}
|
||
|
||
.coupon-progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #10B981, #34D399);
|
||
border-radius: 4rpx;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.coupon-progress-text {
|
||
font-size: 18rpx;
|
||
color: #10B981;
|
||
}
|
||
|
||
/* 底部行 */
|
||
.coupon-footer-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.coupon-expire-v2 {
|
||
font-size: 20rpx;
|
||
color: #9CA3AF;
|
||
flex: 1;
|
||
}
|
||
|
||
.coupon-action-v2 {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.use-btn-v2 {
|
||
display: inline-block;
|
||
background: linear-gradient(135deg, #FFD166, #FF9F43);
|
||
color: #1F2937;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 16rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 159, 67, 0.25);
|
||
}
|
||
|
||
.coupon-status-v2 {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.status-tag {
|
||
font-size: 20rpx;
|
||
padding: 6rpx 14rpx;
|
||
border-radius: 10rpx;
|
||
}
|
||
.status-tag.used {
|
||
background: #DBEAFE;
|
||
color: #2563EB;
|
||
}
|
||
.status-tag.expired {
|
||
background: #F3F4F6;
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
.coupon-used-time {
|
||
font-size: 18rpx;
|
||
color: #9CA3AF;
|
||
margin-top: 6rpx;
|
||
}
|
||
|
||
/* ============================================
|
||
道具卡弹窗样式
|
||
============================================ */
|
||
.item-cards-popup { width: 680rpx; }
|
||
.item-cards-scroll { flex: 1; min-height: 400rpx; max-height: 60vh; }
|
||
|
||
.item-cards-grid {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.item-card {
|
||
display: flex;
|
||
align-items: center;
|
||
background: linear-gradient(135deg, #FFFFFF, #FAFAFA);
|
||
border-radius: 20rpx;
|
||
padding: 24rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.item-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 6rpx;
|
||
height: 100%;
|
||
background: linear-gradient(180deg, #FF9F43, #FF6B35);
|
||
}
|
||
|
||
.card-icon-wrap {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
background: linear-gradient(135deg, #FFF4E6, #FFEDD5);
|
||
border-radius: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 20rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
.card-icon {
|
||
font-size: 40rpx;
|
||
}
|
||
|
||
.card-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.card-name {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #1F2937;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
.card-desc {
|
||
font-size: 22rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.card-count-badge {
|
||
background: linear-gradient(135deg, #FF9F43, #FF6B35);
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3);
|
||
}
|
||
.count-num {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
}
|
||
|
||
/* 已使用道具卡样式 */
|
||
.item-card.used {
|
||
opacity: 0.7;
|
||
background: linear-gradient(135deg, #F3F4F6, #E5E7EB);
|
||
}
|
||
.item-card.used::before {
|
||
background: linear-gradient(180deg, #9CA3AF, #6B7280);
|
||
}
|
||
.item-card.used .card-icon-wrap {
|
||
background: linear-gradient(135deg, #E5E7EB, #D1D5DB);
|
||
}
|
||
.card-use-time {
|
||
font-size: 20rpx;
|
||
color: #9CA3AF;
|
||
margin-top: 6rpx;
|
||
}
|
||
.card-used-badge {
|
||
background: #E5E7EB;
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
}
|
||
.used-text {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.loading-more, .no-more {
|
||
text-align: center;
|
||
color: #9CA3AF;
|
||
padding: 24rpx 0;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
/* ============================================
|
||
任务中心弹窗样式
|
||
============================================ */
|
||
.task-center-popup { width: 700rpx; }
|
||
.task-scroll { flex: 1; min-height: 400rpx; max-height: 55vh; }
|
||
|
||
/* 任务统计栏 */
|
||
.task-stats-bar {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
align-items: center;
|
||
background: linear-gradient(135deg, #FFF8F3, #FFEDD5);
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
.stats-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
.stats-num {
|
||
font-size: 40rpx;
|
||
font-weight: 800;
|
||
color: #FF6B35;
|
||
}
|
||
.stats-label {
|
||
font-size: 22rpx;
|
||
color: #6B7280;
|
||
margin-top: 4rpx;
|
||
}
|
||
.stats-divider {
|
||
width: 2rpx;
|
||
height: 48rpx;
|
||
background: #E5E5E5;
|
||
}
|
||
|
||
/* 任务卡片 */
|
||
.task-card {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
background: #FFFFFF;
|
||
border-radius: 20rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 16rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.task-card.completed {
|
||
opacity: 0.7;
|
||
background: #FAFAFA;
|
||
}
|
||
.task-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 6rpx;
|
||
height: 100%;
|
||
background: linear-gradient(180deg, #FF9F43, #FF6B35);
|
||
}
|
||
.task-card.completed::before {
|
||
background: #D1D5DB;
|
||
}
|
||
|
||
.task-icon-wrap {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
background: linear-gradient(135deg, #FFF4E6, #FFEDD5);
|
||
border-radius: 18rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 20rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
.task-icon {
|
||
font-size: 36rpx;
|
||
}
|
||
|
||
.task-main {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.task-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
.task-card .task-name {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #1F2937;
|
||
flex: 1;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.task-card .task-desc {
|
||
font-size: 22rpx;
|
||
color: #6B7280;
|
||
margin-bottom: 12rpx;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 任务状态标签 */
|
||
.task-status-tag {
|
||
font-size: 20rpx;
|
||
font-weight: 600;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
flex-shrink: 0;
|
||
margin-left: 12rpx;
|
||
}
|
||
.status-ongoing {
|
||
background: #DBEAFE;
|
||
color: #2563EB;
|
||
}
|
||
.status-done {
|
||
background: #D1FAE5;
|
||
color: #059669;
|
||
}
|
||
.status-waiting {
|
||
background: #F3F4F6;
|
||
color: #6B7280;
|
||
}
|
||
|
||
/* 任务进度条 */
|
||
.task-progress {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
.task-progress .progress-bar {
|
||
flex: 1;
|
||
height: 12rpx;
|
||
background: #F3F4F6;
|
||
border-radius: 6rpx;
|
||
overflow: hidden;
|
||
margin-right: 12rpx;
|
||
}
|
||
.task-progress .progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #FF9F43, #FF6B35);
|
||
border-radius: 6rpx;
|
||
transition: width 0.3s ease;
|
||
}
|
||
.task-progress .progress-text {
|
||
font-size: 20rpx;
|
||
color: #9CA3AF;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 任务奖励 */
|
||
.task-rewards {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8rpx;
|
||
}
|
||
.reward-badge {
|
||
display: flex;
|
||
align-items: center;
|
||
background: linear-gradient(135deg, #FFF4E6, #FFEDD5);
|
||
padding: 6rpx 12rpx;
|
||
border-radius: 10rpx;
|
||
}
|
||
.reward-badge .reward-icon {
|
||
font-size: 22rpx;
|
||
margin-right: 4rpx;
|
||
}
|
||
.reward-badge .reward-value {
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
color: #FF6B35;
|
||
}
|
||
|
||
/* 任务操作按钮 */
|
||
.task-action {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-left: 16rpx;
|
||
}
|
||
.task-action .action-btn {
|
||
font-size: 24rpx;
|
||
font-weight: 700;
|
||
padding: 12rpx 24rpx;
|
||
border-radius: 20rpx;
|
||
background: linear-gradient(135deg, #FF9F43, #FF6B35);
|
||
color: #fff;
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3);
|
||
}
|
||
.task-action .action-btn.done {
|
||
background: #D1FAE5;
|
||
color: #059669;
|
||
box-shadow: none;
|
||
}
|
||
.task-action .action-btn.waiting {
|
||
background: #F3F4F6;
|
||
color: #9CA3AF;
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* ============================================
|
||
任务中心 V2 样式
|
||
============================================ */
|
||
.task-center-popup { width: 700rpx; }
|
||
.task-scroll { flex: 1; min-height: 350rpx; max-height: 50vh; }
|
||
|
||
/* 总体进度指示器 */
|
||
.overall-progress {
|
||
display: flex;
|
||
align-items: center;
|
||
background: linear-gradient(135deg, #FFF8F3, #FFEDD5);
|
||
border-radius: 20rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 20rpx;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.progress-circle {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
background: linear-gradient(135deg, #FF9F43, #FF6B35);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 53, 0.3);
|
||
}
|
||
|
||
.progress-percent {
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
color: #fff;
|
||
}
|
||
|
||
.overall-progress .progress-label {
|
||
font-size: 18rpx;
|
||
color: rgba(255,255,255,0.9);
|
||
}
|
||
|
||
.progress-stats {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.stat-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.stat-dot {
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
border-radius: 50%;
|
||
}
|
||
.stat-dot.done { background: #10B981; }
|
||
.stat-dot.ongoing { background: #3B82F6; }
|
||
.stat-dot.waiting { background: #9CA3AF; }
|
||
|
||
.stat-text {
|
||
font-size: 24rpx;
|
||
color: #4B5563;
|
||
}
|
||
|
||
/* 任务卡片 V2 */
|
||
.task-card-v2 {
|
||
display: flex;
|
||
align-items: stretch;
|
||
background: #FFFFFF;
|
||
border-radius: 16rpx;
|
||
padding: 20rpx;
|
||
margin-bottom: 16rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||
border-left: 6rpx solid #E5E7EB;
|
||
}
|
||
|
||
.task-card-v2.task-completed {
|
||
border-left-color: #10B981;
|
||
background: #F0FDF4;
|
||
}
|
||
.task-card-v2.task-ongoing {
|
||
border-left-color: #3B82F6;
|
||
background: #EFF6FF;
|
||
}
|
||
.task-card-v2.task-waiting {
|
||
border-left-color: #E5E7EB;
|
||
}
|
||
|
||
/* 状态指示器 */
|
||
.task-status-indicator {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 16rpx;
|
||
flex-shrink: 0;
|
||
align-self: flex-start;
|
||
margin-top: 4rpx;
|
||
}
|
||
.task-status-indicator.status-done {
|
||
background: #10B981;
|
||
}
|
||
.task-status-indicator.status-ongoing {
|
||
background: #3B82F6;
|
||
}
|
||
.task-status-indicator.status-waiting {
|
||
background: #E5E7EB;
|
||
}
|
||
|
||
.status-icon {
|
||
font-size: 24rpx;
|
||
color: #fff;
|
||
font-weight: bold;
|
||
}
|
||
.status-waiting .status-icon {
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
/* 任务内容 */
|
||
.task-content {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.task-title-row {
|
||
margin-bottom: 6rpx;
|
||
}
|
||
|
||
.task-title {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.task-desc-v2 {
|
||
font-size: 22rpx;
|
||
color: #6B7280;
|
||
margin-bottom: 12rpx;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 1;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 进度条 V2 */
|
||
.task-progress-v2 {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.progress-bar-v2 {
|
||
flex: 1;
|
||
height: 12rpx;
|
||
background: #E5E7EB;
|
||
border-radius: 6rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.progress-fill-v2 {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #FF9F43, #FF6B35);
|
||
border-radius: 6rpx;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.task-completed .progress-fill-v2 {
|
||
background: linear-gradient(90deg, #34D399, #10B981);
|
||
}
|
||
|
||
.task-ongoing .progress-fill-v2 {
|
||
background: linear-gradient(90deg, #60A5FA, #3B82F6);
|
||
}
|
||
|
||
.progress-num {
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
color: #6B7280;
|
||
min-width: 60rpx;
|
||
text-align: right;
|
||
}
|
||
|
||
/* 奖励行 */
|
||
.task-reward-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.task-reward-row .reward-label {
|
||
font-size: 22rpx;
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
.reward-items {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.task-reward-row .reward-item {
|
||
font-size: 22rpx;
|
||
color: #FF6B35;
|
||
font-weight: 600;
|
||
background: #FFF0E5;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
/* 按钮区域 */
|
||
.task-btn-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-left: 12rpx;
|
||
}
|
||
|
||
.task-btn {
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
padding: 0 24rpx;
|
||
height: 56rpx;
|
||
line-height: 56rpx;
|
||
border-radius: 28rpx;
|
||
border: none;
|
||
min-width: 120rpx;
|
||
}
|
||
|
||
.task-btn.btn-go {
|
||
background: linear-gradient(135deg, #FF9F43, #FF6B35);
|
||
color: #fff;
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3);
|
||
}
|
||
|
||
.task-btn.btn-done {
|
||
background: #D1FAE5;
|
||
color: #059669;
|
||
}
|
||
|
||
.task-btn.btn-waiting {
|
||
background: #F3F4F6;
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
/* ============================================
|
||
用户中心 UX/UI 增强
|
||
============================================ */
|
||
|
||
/* 入场动画定义 */
|
||
@keyframes fadeInUp {
|
||
from { opacity: 0; transform: translateY(30rpx); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes scaleIn {
|
||
from { opacity: 0; transform: scale(0.95); }
|
||
to { opacity: 1; transform: scale(1); }
|
||
}
|
||
|
||
@keyframes glowPulse {
|
||
0%, 100% { box-shadow: 0 0 0 0 rgba(255, 159, 67, 0.4); }
|
||
50% { box-shadow: 0 0 20rpx 8rpx rgba(255, 159, 67, 0.2); }
|
||
}
|
||
|
||
@keyframes bounce {
|
||
0%, 100% { transform: scale(1); }
|
||
50% { transform: scale(1.1); }
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from { opacity: 0; transform: translateY(100rpx); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
/* 头像发光效果 */
|
||
.avatar {
|
||
animation: glowPulse 3s ease-in-out infinite;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.avatar:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
/* 用户信息入场动画 */
|
||
.user-info {
|
||
animation: fadeInUp 0.4s ease-out;
|
||
}
|
||
|
||
/* 等级徽章动画 */
|
||
.level-badge {
|
||
transition: all 0.3s ease;
|
||
}
|
||
.level-badge:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
/* 统计栏入场动画 */
|
||
.stats-row {
|
||
animation: fadeInUp 0.4s ease-out 0.1s both;
|
||
}
|
||
|
||
|
||
|
||
/* 统计项点击效果 */
|
||
.stat-item {
|
||
transition: all 0.2s ease;
|
||
padding: 16rpx 8rpx;
|
||
border-radius: 16rpx;
|
||
cursor: pointer;
|
||
}
|
||
.stat-item:active {
|
||
background: rgba(255, 159, 67, 0.1);
|
||
transform: scale(0.95);
|
||
}
|
||
.stat-num {
|
||
transition: all 0.3s ease;
|
||
}
|
||
.stat-item:active .stat-num {
|
||
color: #FF6B35;
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
/* 邀请Banner入场动画 */
|
||
.invite-banner {
|
||
animation: fadeInUp 0.4s ease-out 0.15s both;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.invite-banner:active {
|
||
transform: scale(0.98);
|
||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08);
|
||
}
|
||
|
||
/* 卡片区域入场动画 */
|
||
.card-section {
|
||
animation: fadeInUp 0.4s ease-out 0.2s both;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.card-section:active {
|
||
transform: scale(0.99);
|
||
}
|
||
|
||
/* 订单图标点击效果 */
|
||
.grid-item {
|
||
transition: all 0.2s ease;
|
||
padding: 12rpx;
|
||
border-radius: 16rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
.grid-item:active {
|
||
background: rgba(255, 159, 67, 0.08);
|
||
transform: scale(0.92);
|
||
}
|
||
.grid-icon-img {
|
||
transition: all 0.2s ease;
|
||
}
|
||
.grid-item:active .grid-icon-img {
|
||
transform: scale(1.15);
|
||
}
|
||
|
||
/* 菜单项点击效果 */
|
||
.menu-item {
|
||
transition: all 0.2s ease;
|
||
padding: 16rpx 8rpx;
|
||
border-radius: 16rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
.menu-item:active {
|
||
background: rgba(0, 0, 0, 0.04);
|
||
transform: scale(0.92);
|
||
}
|
||
.menu-icon-img {
|
||
transition: all 0.2s ease;
|
||
}
|
||
.menu-item:active .menu-icon-img {
|
||
transform: scale(1.15) rotate(-5deg);
|
||
}
|
||
|
||
/* 弹窗动画增强 */
|
||
.popup-mask {
|
||
animation: fadeIn 0.2s ease-out;
|
||
}
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.popup-content {
|
||
animation: slideUp 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||
}
|
||
|
||
/* 关闭按钮效果 */
|
||
.close-btn {
|
||
transition: all 0.2s ease;
|
||
}
|
||
.close-btn:active {
|
||
transform: scale(0.85) rotate(90deg);
|
||
color: #FF6B35;
|
||
}
|
||
|
||
/* 登录按钮动画 */
|
||
.join-btn {
|
||
transition: all 0.2s ease;
|
||
animation: bounce 2s ease-in-out infinite;
|
||
}
|
||
.join-btn:active {
|
||
animation: none;
|
||
}
|
||
|
||
/* 邀请按钮增强 */
|
||
.invite-action {
|
||
transition: all 0.2s ease;
|
||
}
|
||
.invite-action:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
/* ============================================
|
||
邀请记录弹窗样式 (Redesigned)
|
||
============================================ */
|
||
.invites-popup {
|
||
width: 680rpx;
|
||
background: #F8F9FB; /* 浅灰底色,更显卡片质感 */
|
||
padding: 0;
|
||
border-radius: 32rpx;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
max-height: 85vh;
|
||
}
|
||
|
||
/* 顶部标题栏 */
|
||
.invites-popup-header {
|
||
height: 110rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
background: #fff;
|
||
border-bottom: 1rpx solid rgba(0,0,0,0.03);
|
||
}
|
||
|
||
.invites-title-text {
|
||
font-size: 34rpx;
|
||
font-weight: 800;
|
||
color: #1A1A1A;
|
||
}
|
||
|
||
.invites-close {
|
||
position: absolute;
|
||
right: 24rpx;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
padding: 12rpx;
|
||
}
|
||
|
||
.invites-close-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
opacity: 0.6;
|
||
}
|
||
|
||
/* 核心资产卡片 */
|
||
.invite-prime-card {
|
||
margin: 32rpx 32rpx 0;
|
||
height: 280rpx;
|
||
background: linear-gradient(135deg, #2A2A2A 0%, #1A1A1A 100%); /* 黑金风格或品牌橙色 */
|
||
background: linear-gradient(135deg, #FF9F43 0%, #FF6B35 100%); /* 选用品牌活力橙 */
|
||
border-radius: 24rpx;
|
||
position: relative;
|
||
overflow: hidden;
|
||
box-shadow: 0 12rpx 32rpx rgba(255, 107, 53, 0.25);
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 装饰圆环 */
|
||
.card-deco-circle {
|
||
position: absolute;
|
||
border-radius: 50%;
|
||
border: 2rpx solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
.card-deco-circle.c1 { width: 300rpx; height: 300rpx; top: -100rpx; right: -60rpx; }
|
||
.card-deco-circle.c2 { width: 400rpx; height: 400rpx; bottom: -120rpx; left: -80rpx; opacity: 0.15; background: radial-gradient(circle, rgba(255,255,255,0.2), transparent); }
|
||
|
||
.prime-card-content {
|
||
position: relative;
|
||
z-index: 2;
|
||
padding: 0 40rpx;
|
||
}
|
||
|
||
.my-code-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.code-label {
|
||
font-size: 24rpx;
|
||
color: rgba(255, 255, 255, 0.85);
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.code-value-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.code-text {
|
||
font-size: 48rpx;
|
||
font-weight: 800;
|
||
color: #fff;
|
||
font-family: monospace; /* 等宽字体显得更像邀请码 */
|
||
letter-spacing: 2rpx;
|
||
text-shadow: 0 2rpx 4rpx rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.copy-icon-btn {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
backdrop-filter: blur(10px);
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 999rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
border: 1rpx solid rgba(255,255,255,0.3);
|
||
}
|
||
|
||
.copy-text {
|
||
font-size: 24rpx;
|
||
color: #fff;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.card-divider-h {
|
||
height: 1rpx;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.stats-mini-row {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.stat-mini-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.mini-num {
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
color: #fff;
|
||
}
|
||
|
||
.mini-label {
|
||
font-size: 20rpx;
|
||
color: rgba(255, 255, 255, 0.7);
|
||
margin-top: 2rpx;
|
||
}
|
||
|
||
.stat-mini-sep {
|
||
width: 1rpx;
|
||
height: 40rpx;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
margin: 0 40rpx;
|
||
}
|
||
|
||
/* 列表区域 */
|
||
.list-section-header {
|
||
padding: 40rpx 32rpx 20rpx;
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
|
||
.list-title {
|
||
font-size: 30rpx;
|
||
font-weight: 800;
|
||
color: #333;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.list-subtitle {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.invites-scroll-refined {
|
||
flex: 1;
|
||
height: 0; /* Important for flex layout scrolling */
|
||
min-height: 400rpx;
|
||
padding: 0 32rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 列表卡片 */
|
||
.friend-card {
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
|
||
border: 1rpx solid rgba(0,0,0,0.02);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.friend-avatar-wrap {
|
||
position: relative;
|
||
margin-right: 24rpx;
|
||
}
|
||
|
||
.friend-avatar {
|
||
width: 88rpx;
|
||
height: 88rpx;
|
||
border-radius: 50%;
|
||
background: #f0f0f0;
|
||
border: 2rpx solid #fff;
|
||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.friend-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.friend-name-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.friend-name {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #333;
|
||
margin-right: 12rpx;
|
||
max-width: 200rpx;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.friend-tag {
|
||
font-size: 18rpx;
|
||
color: #FF6B35;
|
||
background: rgba(255, 107, 53, 0.1);
|
||
padding: 2rpx 8rpx;
|
||
border-radius: 6rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.friend-meta {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.friend-status {
|
||
display: flex;
|
||
align-items: center;
|
||
background: #F0FDF4;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
.status-dot {
|
||
width: 10rpx;
|
||
height: 10rpx;
|
||
border-radius: 50%;
|
||
margin-right: 8rpx;
|
||
}
|
||
.status-dot.success { background: #38ef7d; box-shadow: 0 0 6rpx rgba(56, 239, 125, 0.6); }
|
||
|
||
.status-val {
|
||
font-size: 20rpx;
|
||
color: #11998e;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* 空状态优化 */
|
||
.empty-state-refined {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 80rpx 0;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.empty-img-refined {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
margin-bottom: 24rpx;
|
||
opacity: 0.5; /* 只有SVG没有颜色时 */
|
||
}
|
||
|
||
.empty-text-refined {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
font-weight: 700;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.empty-sub-refined {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* 底部操作区 */
|
||
.popup-footer-action {
|
||
padding: 24rpx 32rpx 32rpx;
|
||
background: #fff;
|
||
border-top: 1rpx solid rgba(0,0,0,0.03);
|
||
z-index: 10;
|
||
}
|
||
|
||
.btn-share-gradient {
|
||
background: linear-gradient(90deg, #FF6B35, #FF9F43);
|
||
color: #fff;
|
||
height: 96rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
border-radius: 48rpx;
|
||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 53, 0.3);
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.btn-share-gradient:active {
|
||
transform: scale(0.98);
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.2);
|
||
}
|
||
|
||
.btn-arrow-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
margin-left: 12rpx;
|
||
}
|
||
|
||
/* Loading States */
|
||
.status-loading-refined {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 60rpx 0;
|
||
color: #999;
|
||
font-size: 24rpx;
|
||
}
|
||
.loading-spinner {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border: 4rpx solid #EAEAEA;
|
||
border-top-color: #FF6B35;
|
||
border-radius: 50%;
|
||
margin-bottom: 16rpx;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
||
|
||
.loading-more-text { text-align: center; padding: 24rpx; color: #999; font-size: 22rpx; }
|
||
.no-more-text { text-align: center; padding: 24rpx; color: #ddd; font-size: 22rpx; margin-bottom: 24rpx; }
|
||
|
||
</style>
|