- 从 main 获取 single_uniapp22miao 子项目 - dart-sass: /deep/ -> ::v-deep,calc 运算符加空格 - DEPLOY.md 采用 shccd159 版本(4 子项目架构说明) Made-with: Cursor
922 lines
24 KiB
Vue
922 lines
24 KiB
Vue
<template>
|
||
<view class="points-page">
|
||
<!-- 白色导航栏 -->
|
||
<view class="nav-bar">
|
||
<view class="nav-back" @click="goBack">
|
||
<text class="iconfont icon-fanhui"></text>
|
||
</view>
|
||
<text class="nav-title">我的奖金</text>
|
||
<view class="nav-right-btn" @click="goToRushBuy">
|
||
<text class="rush-btn-text"> 采购 </text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 奖金概览卡片 -->
|
||
<view class="balance-section">
|
||
<view class="balance-card">
|
||
<text class="balance-label">我的奖金:</text>
|
||
<text class="balance-total">{{ totalBalance }}</text>
|
||
|
||
<!-- 奖金和积分卡片 -->
|
||
<view class="balance-cards">
|
||
<!-- 奖金卡片 -->
|
||
<view class="card prize-card">
|
||
<text class="card-label">奖金</text>
|
||
<text class="card-amount prize-amount">¥{{ prizeAmount }}</text>
|
||
<text class="card-tip">可提现</text>
|
||
</view>
|
||
|
||
<!-- 易积分卡片 -->
|
||
<view class="card points-card">
|
||
<view class="card-top-row">
|
||
<text class="card-label">易积分</text>
|
||
<view class="exchange-btn" @click="goToShop">
|
||
<text class="exchange-text">兑换</text>
|
||
</view>
|
||
</view>
|
||
<text class="card-amount points-amount">{{ pointsInfo.available_points }}</text>
|
||
<text class="card-tip">可兑换商品</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 标签栏 -->
|
||
<view class="tabs-section">
|
||
<view
|
||
v-for="(tab, index) in tabs"
|
||
:key="index"
|
||
class="tab-btn"
|
||
:class="{ 'active': currentTab === index }"
|
||
@click="switchTab(index)"
|
||
>
|
||
<text class="tab-text">{{ tab.name }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 积分明细列表 -->
|
||
<scroll-view
|
||
class="points-list"
|
||
scroll-y
|
||
@scrolltolower="loadMore"
|
||
:refresher-enabled="true"
|
||
:refresher-triggered="refreshing"
|
||
@refresherrefresh="onRefresh"
|
||
>
|
||
<view v-if="pointsList.length > 0" class="list-container">
|
||
<view
|
||
v-for="item in pointsList"
|
||
:key="item.id"
|
||
class="point-item"
|
||
>
|
||
<view class="item-info">
|
||
<view class="item-title-row">
|
||
<text class="item-title">{{ item.title }}</text>
|
||
<text class="item-source" v-if="item.source === 'points'">积分</text>
|
||
<!-- <text class="item-source bonus" v-else-if="item.source === 'bonus'">奖金</text> -->
|
||
</view>
|
||
<text class="item-time">{{ item.created_at }}</text>
|
||
</view>
|
||
<view class="item-right">
|
||
<text
|
||
class="item-points"
|
||
:class="{ 'income': item.type === 1, 'expense': item.type === 2 }"
|
||
>
|
||
{{ item.type === 1 ? '+' : '-' }}{{ formatNumber(item.points) }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view v-else-if="!loading" class="empty-state">
|
||
<image class="empty-image" src="/static/images/empty.png" mode="aspectFit"></image>
|
||
<text class="empty-text">暂无积分记录</text>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<view v-if="pointsList.length > 0" class="load-more">
|
||
<text v-if="loading" class="load-text">加载中...</text>
|
||
<text v-else-if="noMore" class="load-text">没有更多了</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getWaUserInfo, getWaSelfBonusList, getIntegralList, loginV2, getUserInfo, getIntegralUserByAccount } from '@/api/user.js'
|
||
import { mapGetters } from 'vuex'
|
||
|
||
export default {
|
||
computed: {
|
||
...mapGetters(['isLogin', 'userInfo','uid']),
|
||
// 我的奖金 = 奖金(prizeAmount) + 易积分(available_points)
|
||
totalBalance() {
|
||
const p = Number(this.prizeAmount) || 0
|
||
const a = Number(this.pointsInfo.available_points) || 0
|
||
return (p + a).toFixed(3)
|
||
},
|
||
// 获取用户账号
|
||
userAccount() {
|
||
if (this.userInfo) {
|
||
return this.userInfo.account || this.userInfo.phone || this.userInfo.phoneNumber || ''
|
||
}
|
||
return ''
|
||
},
|
||
// 获取寄卖商城用户ID(优先使用wa_user_id,否则使用uid)
|
||
waUserId() {
|
||
// 优先使用明确的wa用户ID字段
|
||
if (this.userInfo && (this.userInfo.wa_user_id || this.userInfo.waUserId)) {
|
||
return this.userInfo.wa_user_id || this.userInfo.waUserId
|
||
}
|
||
// 使用store中的uid
|
||
if (this.uid) {
|
||
return this.uid
|
||
}
|
||
// 最后使用userInfo中的uid或id
|
||
if (this.userInfo) {
|
||
return this.userInfo.uid || this.userInfo.id
|
||
}
|
||
return null
|
||
},
|
||
// 获取用户手机号(用于WA系统关联)
|
||
userPhone() {
|
||
if (this.userInfo) {
|
||
return this.userInfo.phone || this.userInfo.mobile || this.userInfo.account
|
||
}
|
||
return null
|
||
}
|
||
},
|
||
|
||
data() {
|
||
return {
|
||
account: '', // 从URL获取的用户名
|
||
password: '123456',
|
||
prizeAmount: '0.00', // 可提现奖金(money)
|
||
pointsInfo: {
|
||
total_points: 0,
|
||
available_points: '0.00', // 易积分(score)
|
||
frozen_points: 0,
|
||
today_earn: 0
|
||
},
|
||
tabs: [
|
||
{ name: '收入明细', type: 1 },
|
||
{ name: '支出明细', type: 2 }
|
||
],
|
||
currentTab: 0,
|
||
pointsList: [],
|
||
page: 1,
|
||
limit: 20,
|
||
loading: false,
|
||
refreshing: false,
|
||
noMore: false
|
||
}
|
||
},
|
||
|
||
async onLoad(options) {
|
||
console.log('onLoad options:', options)
|
||
|
||
// 获取username:优先从options获取,否则从window.location获取(兼容iframe嵌入场景)
|
||
let username = (options && options.username) ? options.username : null
|
||
|
||
// 如果options中没有username,尝试从URL中获取(兼容H5 iframe嵌入)
|
||
if (!username) {
|
||
username = this.getUrlParam('username')
|
||
console.log('从URL获取username:', username)
|
||
}
|
||
|
||
// URL中有username参数时,必须调用autoLogin
|
||
if (username) {
|
||
console.log('获取到username:', username)
|
||
this.account = username
|
||
// 强制调用autoLogin,无论当前是否已登录都要重新登录刷新用户信息
|
||
await this.autoLogin()
|
||
console.log('autoLogin执行完成,isLogin:', this.isLogin)
|
||
} else {
|
||
// URL没有username时,尝试从已登录的用户信息获取账号
|
||
if (this.isLogin && this.userInfo && this.userInfo.account) {
|
||
this.account = this.userInfo.account
|
||
}
|
||
// 未登录且有账号则自动登录
|
||
if (!this.isLogin && this.account) {
|
||
await this.autoLogin()
|
||
}
|
||
}
|
||
|
||
this.getUserPoints()
|
||
await this.loadUserInfo()
|
||
this.loadPointsList()
|
||
},
|
||
|
||
onShow() {
|
||
// 如果已登录但account为空,从userInfo同步
|
||
if (this.isLogin && this.userInfo && this.userInfo.account && !this.account) {
|
||
this.account = this.userInfo.account
|
||
}
|
||
|
||
// 刷新用户信息和积分明细(需要登录后才能调用)
|
||
if (this.isLogin) {
|
||
this.getUserPoints()
|
||
this.loadUserInfo()
|
||
this.loadPointsList()
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
// 从URL中获取参数(兼容多种URL格式)
|
||
getUrlParam(name) {
|
||
// #ifdef H5
|
||
try {
|
||
// 尝试从当前页面URL获取
|
||
let url = window.location.href
|
||
|
||
// 如果在iframe中,尝试获取父页面URL
|
||
try {
|
||
if (window.parent && window.parent !== window) {
|
||
url = window.parent.location.href
|
||
}
|
||
} catch (e) {
|
||
// 跨域情况下无法访问parent.location
|
||
console.log('无法访问父页面URL')
|
||
}
|
||
|
||
console.log('解析URL:', url)
|
||
|
||
// 解析URL参数
|
||
const urlObj = new URL(url)
|
||
let value = urlObj.searchParams.get(name)
|
||
|
||
// 如果没找到,尝试从hash中获取(兼容hash路由)
|
||
if (!value && url.includes('#')) {
|
||
const hashPart = url.split('#')[1]
|
||
if (hashPart && hashPart.includes('?')) {
|
||
const hashParams = new URLSearchParams(hashPart.split('?')[1])
|
||
value = hashParams.get(name)
|
||
}
|
||
}
|
||
|
||
return value
|
||
} catch (e) {
|
||
console.error('解析URL参数失败:', e)
|
||
return null
|
||
}
|
||
// #endif
|
||
return null
|
||
},
|
||
|
||
// 自动登录
|
||
async autoLogin() {
|
||
console.log('autoLogin开始执行,account:', this.account)
|
||
|
||
// 检查账号是否存在
|
||
if (!this.account) {
|
||
console.warn('autoLogin: 账号为空,跳过登录')
|
||
return
|
||
}
|
||
|
||
try {
|
||
console.log('正在调用loginV2接口,account:', this.account)
|
||
const res = await loginV2({
|
||
account: this.account,
|
||
password: this.password,
|
||
spread_spid: this.$Cache.get("spread") || ''
|
||
})
|
||
console.log("loginV2返回结果:", res)
|
||
|
||
if (res.data && res.data.token) {
|
||
// 保存token到store
|
||
this.$store.commit("LOGIN", {
|
||
token: res.data.token
|
||
})
|
||
console.log('token已保存')
|
||
|
||
// 保存UID
|
||
if (res.data.uid) {
|
||
this.$store.commit("SETUID", res.data.uid)
|
||
console.log('uid已保存:', res.data.uid)
|
||
}
|
||
|
||
// 获取并保存用户信息
|
||
console.log('正在获取用户信息...')
|
||
const userInfoRes = await getUserInfo()
|
||
console.log('getUserInfo返回:', userInfoRes)
|
||
|
||
if (userInfoRes.data) {
|
||
// 更新vuex store(store内部会自动保存到Cache)
|
||
this.$store.commit("UPDATE_USERINFO", userInfoRes.data)
|
||
console.log('用户信息已更新到store:', userInfoRes.data)
|
||
|
||
// 同步account到本地变量(确保后续API调用可用)
|
||
if (userInfoRes.data.account) {
|
||
this.account = userInfoRes.data.account
|
||
} else if (userInfoRes.data.phone) {
|
||
this.account = userInfoRes.data.phone
|
||
}
|
||
}
|
||
|
||
console.log('自动登录成功完成')
|
||
} else {
|
||
console.warn('loginV2返回数据异常,无token:', res)
|
||
}
|
||
} catch (error) {
|
||
console.error('自动登录失败:', error)
|
||
uni.showToast({
|
||
title: '自动登录失败',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
} finally {
|
||
uni.hideLoading()
|
||
}
|
||
},
|
||
|
||
// 返回上一页
|
||
goBack() {
|
||
uni.navigateBack()
|
||
},
|
||
|
||
// 跳转到抢购页面
|
||
goToRushBuy() {
|
||
// #ifdef H5
|
||
// window.location.href = 'https://shop.wenjinhui.com/?#/pages/personal/index'
|
||
//window.location.href = 'https://anyue.szxingming.com/?#/pages/personal/index'
|
||
window.location.href = 'https://xiashengjun.com/?#/pages/personal/index'
|
||
// window.location.href = 'http://shop.bosenyuan.com/?#/pages/personal/index'
|
||
// #endif
|
||
// #ifndef H5
|
||
uni.navigateTo({
|
||
url: '/pages/web-view/index?url=' + encodeURIComponent('https://xiashengjun.com/?#/pages/personal/index')
|
||
})
|
||
// #endif
|
||
},
|
||
|
||
// 获取用户积分
|
||
async getUserPoints() {
|
||
// 优先使用登录后的userInfo.account,否则使用URL传入的account
|
||
const accountToUse = (this.userInfo && this.userInfo.account) || this.userAccount || this.account
|
||
if (!accountToUse) {
|
||
// 未提供账号时静默返回,不影响其他功能
|
||
return
|
||
}
|
||
|
||
try {
|
||
const res = await getIntegralUserByAccount(accountToUse)
|
||
if (res.data) {
|
||
this.pointsInfo.available_points = res.data.integral || 0
|
||
}
|
||
} catch (error) {
|
||
console.error('获取积分失败:', error)
|
||
}
|
||
},
|
||
|
||
// 加载寄卖商城用户信息
|
||
async loadUserInfo() {
|
||
console.log('当前userInfo:', this.userInfo)
|
||
console.log('waUserId:', this.waUserId)
|
||
console.log('userPhone:', this.userPhone)
|
||
|
||
// 优先使用waUserId,如果没有则使用手机号
|
||
const userId = this.waUserId || this.userPhone
|
||
|
||
if (!userId) {
|
||
console.warn('未获取到寄卖商城用户ID或手机号')
|
||
return
|
||
}
|
||
|
||
try {
|
||
console.log('调用getWaUserInfo,userId:', userId)
|
||
const res = await getWaUserInfo(userId)
|
||
|
||
// 接口返回 code: 0 表示成功
|
||
if (res.code === 0 && res.data) {
|
||
console.log('用户信息接口返回:', res.data)
|
||
const data = res.data
|
||
// 映射API返回数据到页面展示
|
||
// selfBonus -> 我的奖金
|
||
// this.totalBalance = this.formatDecimal(data.selfBonus || 0, 3)
|
||
// money -> 可提现奖金
|
||
this.prizeAmount = this.formatDecimal(data.selfBonus/2 || 0, 2)
|
||
}
|
||
} catch (error) {
|
||
console.error('加载用户信息失败:', error)
|
||
}
|
||
},
|
||
|
||
// 切换标签
|
||
switchTab(index) {
|
||
if (this.currentTab === index) return
|
||
this.currentTab = index
|
||
this.resetList()
|
||
this.loadPointsList()
|
||
},
|
||
|
||
// 重置列表
|
||
resetList() {
|
||
this.pointsList = []
|
||
this.page = 1
|
||
this.noMore = false
|
||
},
|
||
|
||
// 加载奖金明细列表
|
||
async loadPointsList() {
|
||
if (this.loading || this.noMore) return
|
||
|
||
// 优先使用waUserId,如果没有则使用手机号
|
||
const userId = this.waUserId || this.userPhone
|
||
|
||
if (!userId) {
|
||
console.warn('未获取到寄卖商城用户ID或手机号')
|
||
this.loading = false
|
||
return
|
||
}
|
||
|
||
this.loading = true
|
||
|
||
try {
|
||
const type = this.tabs[this.currentTab].type
|
||
|
||
// 如果是支出明细(type === 2),同时加载奖金支出和积分支出
|
||
if (type === 2) {
|
||
// 并行加载奖金明细和积分明细
|
||
const [bonusRes, pointsRes] = await Promise.all([
|
||
getWaSelfBonusList({
|
||
userId: userId,
|
||
type: type,
|
||
page: this.page,
|
||
limit: this.limit
|
||
}),
|
||
getIntegralList({
|
||
page: this.page,
|
||
limit: this.limit,
|
||
type: 2
|
||
})
|
||
])
|
||
|
||
let bonusList = []
|
||
let pointsList = []
|
||
|
||
// 处理奖金明细
|
||
if (bonusRes.code === 0 && bonusRes.data) {
|
||
bonusList = (bonusRes.data.list || []).map(item => ({
|
||
id: `bonus_${item.id}`,
|
||
source: 'bonus', // 标记来源:奖金
|
||
type: item.type,
|
||
points: Math.abs(parseFloat(item.money) || 0),
|
||
balance: item.after,
|
||
title: item.memo || '奖金支出',
|
||
remark: item.memo || '',
|
||
created_at: item.createdAt || ''
|
||
}))
|
||
}
|
||
|
||
// 处理积分明细(使用 getIntegralList,需要过滤出 type === 2 的支出记录)
|
||
if (pointsRes.code === 0 && pointsRes.data) {
|
||
// 过滤出支出记录(type === 2),兼容后端返回数字或字符串
|
||
const expenseRecords = (pointsRes.data.list || []).filter(item => Number(item.type) === 2)
|
||
pointsList = expenseRecords.map(item => ({
|
||
id: `points_${item.id}`,
|
||
source: 'points', // 标记来源:积分(与模板中的判断保持一致)
|
||
type: item.type || 2, // 积分明细的type,2=支出
|
||
points: Math.abs(parseFloat(item.integral) || 0), // getIntegralList 返回的是 integral 字段
|
||
balance: 0, // getIntegralList 可能没有 balance 字段,设为0
|
||
title: item.title || '积分支出',
|
||
remark: item.title || '',
|
||
created_at: item.updateTime || '' // getIntegralList 返回的是 updateTime 字段
|
||
}))
|
||
}
|
||
|
||
// 合并两个列表并按时间倒序排序
|
||
const mergedList = [...bonusList, ...pointsList].sort((a, b) => {
|
||
const timeA = new Date(a.created_at).getTime()
|
||
const timeB = new Date(b.created_at).getTime()
|
||
return timeB - timeA // 倒序:最新的在前
|
||
})
|
||
|
||
// 如果当前是第一页,直接替换;否则追加
|
||
if (this.page === 1) {
|
||
this.pointsList = mergedList
|
||
} else {
|
||
this.pointsList = [...this.pointsList, ...mergedList]
|
||
}
|
||
|
||
// 判断是否还有更多数据(两个接口都返回较少数据时认为没有更多)
|
||
const totalCount = bonusList.length + pointsList.length
|
||
if (totalCount < this.limit) {
|
||
this.noMore = true
|
||
}
|
||
} else {
|
||
// 收入明细只加载奖金明细
|
||
const res = await getWaSelfBonusList({
|
||
userId: userId,
|
||
type: type,
|
||
page: this.page,
|
||
limit: this.limit
|
||
})
|
||
|
||
// 接口返回 code: 0 表示成功
|
||
if (res.code === 0 && res.data) {
|
||
console.log('奖金明细接口返回:', res.data)
|
||
|
||
// 映射API返回数据到页面列表格式
|
||
const list = (res.data.list || []).map(item => ({
|
||
id: `bonus_${item.id}`,
|
||
source: 'bonus',
|
||
type: item.type,
|
||
points: Math.abs(parseFloat(item.money) || 0),
|
||
balance: item.after,
|
||
title: item.memo || (item.type === 1 ? '收入' : '支出'),
|
||
remark: item.memo || '',
|
||
created_at: item.createdAt || ''
|
||
}))
|
||
|
||
this.pointsList = this.page === 1 ? list : [...this.pointsList, ...list]
|
||
|
||
// 判断是否还有更多数据
|
||
if (list.length < this.limit || res.data.page >= res.data.totalPage) {
|
||
this.noMore = true
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('加载明细失败:', error)
|
||
uni.showToast({
|
||
title: '加载失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.loading = false
|
||
this.refreshing = false
|
||
}
|
||
},
|
||
|
||
// 下拉刷新
|
||
onRefresh() {
|
||
this.refreshing = true
|
||
this.getUserPoints()
|
||
this.loadUserInfo()
|
||
this.resetList()
|
||
this.loadPointsList()
|
||
},
|
||
|
||
// 加载更多
|
||
loadMore() {
|
||
if (!this.noMore && !this.loading) {
|
||
this.page++
|
||
this.loadPointsList()
|
||
}
|
||
},
|
||
|
||
// 跳转到积分规则
|
||
goToRules() {
|
||
uni.navigateTo({
|
||
url: '/pages/integral/rules'
|
||
})
|
||
},
|
||
|
||
// 跳转到商城
|
||
goToShop() {
|
||
uni.navigateTo({
|
||
url: '/pages/integral/index'
|
||
})
|
||
},
|
||
|
||
// 格式化数字(保留指定位小数)
|
||
formatNumber(num) {
|
||
if (num === null || num === undefined) return '0.000'
|
||
return Number(num).toFixed(3)
|
||
},
|
||
|
||
// 格式化小数(指定精度)
|
||
formatDecimal(num, precision = 3) {
|
||
if (num === null || num === undefined) return '0.' + '0'.repeat(precision)
|
||
return Number(num).toFixed(precision)
|
||
},
|
||
|
||
// 获取类型图标
|
||
getTypeIcon(type, title) {
|
||
if (title.includes('签到')) return '📅'
|
||
if (title.includes('购物')) return '🛍️'
|
||
if (title.includes('邀请')) return '👥'
|
||
if (title.includes('分享')) return '📤'
|
||
if (title.includes('兑换')) return '🎁'
|
||
if (title.includes('系统')) return '⚙️'
|
||
return type === 1 ? '📈' : '📉'
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.points-page {
|
||
min-height: 100vh;
|
||
background-color: #F9FAFB;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
// 白色导航栏
|
||
.nav-bar {
|
||
background-color: #FFFFFF;
|
||
height: 113.344rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 31.996rpx;
|
||
border-bottom: 1.344rpx solid rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.nav-back {
|
||
width: 72rpx;
|
||
height: 64rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.iconfont {
|
||
font-size: 36rpx;
|
||
color: #0A0A0A;
|
||
}
|
||
}
|
||
|
||
.nav-title {
|
||
flex: 1;
|
||
font-size: 32rpx;
|
||
color: #0A0A0A;
|
||
font-weight: normal;
|
||
text-align: center;
|
||
line-height: 48rpx;
|
||
}
|
||
|
||
.nav-right-btn {
|
||
background: linear-gradient(90deg, #FF6900 0%, #F54900 100%);
|
||
border-radius: 24rpx;
|
||
padding: 8rpx 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.rush-btn-text {
|
||
font-size: 28rpx;
|
||
color: #FFFFFF;
|
||
font-weight: 500;
|
||
line-height: 40rpx;
|
||
}
|
||
|
||
// 余额卡片区域
|
||
.balance-section {
|
||
padding: 30rpx 31rpx 0;
|
||
}
|
||
|
||
.balance-card {
|
||
background-color: #FFFFFF;
|
||
border: 1rpx solid rgba(0, 0, 0, 0.1);
|
||
border-radius: 28rpx;
|
||
padding: 40rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 30rpx;
|
||
}
|
||
|
||
.balance-label {
|
||
font-size: 32rpx;
|
||
color: #4A5565;
|
||
display: block;
|
||
line-height: 48rpx;
|
||
}
|
||
|
||
.balance-total {
|
||
font-size: 60rpx;
|
||
color: #0A0A0A;
|
||
font-weight: normal;
|
||
display: block;
|
||
line-height: 72rpx;
|
||
}
|
||
|
||
// 奖金和积分卡片
|
||
.balance-cards {
|
||
display: flex;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.card {
|
||
flex: 1;
|
||
border-radius: 20rpx;
|
||
padding: 31.996rpx;
|
||
min-height: 231.92rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15.988rpx;
|
||
}
|
||
|
||
.prize-card {
|
||
background: linear-gradient(140.01deg, #FFF7ED 0%, #FFEDD4 100%);
|
||
}
|
||
|
||
.points-card {
|
||
background: linear-gradient(140.01deg, #EFF6FF 0%, #DBEAFE 100%);
|
||
}
|
||
|
||
.card-top-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
width: 100%;
|
||
}
|
||
|
||
.card-label {
|
||
font-size: 28rpx;
|
||
color: #4A5565;
|
||
line-height: 40rpx;
|
||
display: block;
|
||
}
|
||
|
||
.card-amount {
|
||
font-size: 48rpx;
|
||
font-weight: normal;
|
||
line-height: 64rpx;
|
||
display: block;
|
||
}
|
||
|
||
.prize-amount {
|
||
color: #F54900;
|
||
}
|
||
|
||
.points-amount {
|
||
color: #155DFC;
|
||
}
|
||
|
||
.card-tip {
|
||
font-size: 24rpx;
|
||
color: #6A7282;
|
||
line-height: 32rpx;
|
||
}
|
||
|
||
.exchange-btn {
|
||
background-color: #155DFC;
|
||
border-radius: 16rpx;
|
||
padding: 0 24rpx;
|
||
height: 47.984rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.exchange-text {
|
||
font-size: 28rpx;
|
||
color: #FFFFFF;
|
||
font-weight: 500;
|
||
line-height: 40rpx;
|
||
}
|
||
|
||
// 标签栏(底部边框设计)
|
||
.tabs-section {
|
||
padding: 36rpx;
|
||
display: flex;
|
||
border-bottom: 2rpx solid rgba(0, 0, 0, 0.1);
|
||
position: relative;
|
||
}
|
||
|
||
.tab-btn {
|
||
flex: 1;
|
||
height: 95.99rpx;
|
||
line-height: 95.99rpx;
|
||
text-align: center;
|
||
background-color: transparent;
|
||
position: relative;
|
||
padding: 9.344rpx 17.344rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
&.active {
|
||
border-bottom: 2rpx solid #F54900;
|
||
border-left: 2rpx solid #F54900;
|
||
border-right: 2rpx solid #F54900;
|
||
border-top: 2rpx solid #F54900;
|
||
|
||
.tab-text {
|
||
color: #F54900;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
|
||
.tab-text {
|
||
font-size: 28rpx;
|
||
color: #0A0A0A;
|
||
line-height: 40rpx;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.points-list {
|
||
flex: 1;
|
||
padding: 1rpx 31.996rpx 0;
|
||
}
|
||
|
||
.list-container {
|
||
padding-bottom: 23.992rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 23.992rpx;
|
||
}
|
||
|
||
.point-item {
|
||
background-color: #FFFFFF;
|
||
border: 1.344rpx solid rgba(0, 0, 0, 0.1);
|
||
border-radius: 28rpx;
|
||
padding: 33.34rpx 33.34rpx 1.344rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.item-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 7.984rpx;
|
||
}
|
||
|
||
.item-title-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.item-title {
|
||
font-size: 32rpx;
|
||
color: #0A0A0A;
|
||
font-weight: normal;
|
||
line-height: 48rpx;
|
||
}
|
||
|
||
.item-source {
|
||
font-size: 20rpx;
|
||
color: #FFFFFF;
|
||
background-color: #155DFC;
|
||
border-radius: 8rpx;
|
||
padding: 4rpx 12rpx;
|
||
line-height: 28rpx;
|
||
|
||
&.bonus {
|
||
background-color: #F54900;
|
||
}
|
||
}
|
||
|
||
.item-time {
|
||
font-size: 28rpx;
|
||
color: #6A7282;
|
||
line-height: 40rpx;
|
||
}
|
||
|
||
.item-right {
|
||
margin-left: 40rpx;
|
||
}
|
||
|
||
.item-points {
|
||
font-size: 40rpx;
|
||
font-weight: normal;
|
||
line-height: 56rpx;
|
||
|
||
&.income {
|
||
color: #00A63E;
|
||
}
|
||
|
||
&.expense {
|
||
color: #0A0A0A;
|
||
}
|
||
}
|
||
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 200rpx 0;
|
||
}
|
||
|
||
.empty-image {
|
||
width: 300rpx;
|
||
height: 300rpx;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 28rpx;
|
||
color: #999999;
|
||
}
|
||
|
||
.load-more {
|
||
padding: 47.954rpx 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.load-text {
|
||
font-size: 32rpx;
|
||
color: #99A1AF;
|
||
line-height: 48rpx;
|
||
}
|
||
</style>
|
||
|