init
This commit is contained in:
421
miniprogram/pages/points/index.js
Normal file
421
miniprogram/pages/points/index.js
Normal file
@@ -0,0 +1,421 @@
|
||||
import {
|
||||
getUserPoints,
|
||||
getPointsOverview,
|
||||
getPointsHistory,
|
||||
getPointsRules,
|
||||
getPointsExchangeList,
|
||||
exchangePoints,
|
||||
getUserExchangeRecords,
|
||||
dailyCheckin
|
||||
} from '../../services/points/index';
|
||||
|
||||
const weChatAuthService = require('../../services/auth/wechat');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
userPoints: 0,
|
||||
pointsOverview: {
|
||||
total_points: 0,
|
||||
available_points: 0,
|
||||
frozen_points: 0,
|
||||
total_earned: 0,
|
||||
total_spent: 0,
|
||||
this_month_earned: 0,
|
||||
this_month_spent: 0
|
||||
},
|
||||
pointsHistory: [],
|
||||
pointsRules: [],
|
||||
exchangeItems: [],
|
||||
exchangeRecords: [],
|
||||
currentTab: 0,
|
||||
showExchangeModal: false,
|
||||
selectedItem: null,
|
||||
loading: false,
|
||||
historyPage: 1,
|
||||
exchangePage: 1,
|
||||
recordsPage: 1,
|
||||
hasMoreHistory: true,
|
||||
hasMoreExchange: true,
|
||||
hasMoreRecords: true,
|
||||
tabList: [
|
||||
{ text: '积分明细', key: 0 },
|
||||
{ text: '积分规则', key: 1 },
|
||||
{ text: '积分兑换', key: 2 }
|
||||
]
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.init();
|
||||
},
|
||||
|
||||
async init() {
|
||||
console.log('积分页面初始化');
|
||||
this.setData({ loading: true });
|
||||
|
||||
// 检查登录状态
|
||||
if (!weChatAuthService.isLoggedIn()) {
|
||||
console.log('用户未登录,跳转到登录页面');
|
||||
wx.showToast({
|
||||
title: '请先登录',
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
wx.navigateTo({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
}, 1500);
|
||||
this.setData({ loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 并行加载基础数据
|
||||
await Promise.all([
|
||||
this.loadUserPoints(),
|
||||
this.loadPointsOverview(),
|
||||
this.loadPointsHistory(),
|
||||
this.loadPointsRules(),
|
||||
this.loadExchangeItems()
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error('积分页面初始化失败:', error);
|
||||
|
||||
// 检查是否是认证错误
|
||||
if (error.message && error.message.includes('未授权')) {
|
||||
wx.showToast({
|
||||
title: '登录已过期,请重新登录',
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
wx.navigateTo({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
}, 1500);
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: '加载失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
this.setData({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
// 加载用户积分
|
||||
async loadUserPoints() {
|
||||
try {
|
||||
console.log('开始获取用户积分...');
|
||||
const response = await getUserPoints();
|
||||
console.log('用户积分API响应:', response);
|
||||
|
||||
if (response.code === 200) {
|
||||
const points = response.data.points || 0;
|
||||
console.log('设置用户积分:', points);
|
||||
this.setData({
|
||||
userPoints: points
|
||||
});
|
||||
} else {
|
||||
console.error('获取用户积分失败 - 业务错误:', response.message);
|
||||
throw new Error(response.message || '获取积分失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户积分失败:', error);
|
||||
throw error; // 重新抛出错误,让上层处理
|
||||
}
|
||||
},
|
||||
|
||||
// 加载积分概览
|
||||
async loadPointsOverview() {
|
||||
try {
|
||||
console.log('开始获取积分概览...');
|
||||
const response = await getPointsOverview();
|
||||
console.log('积分概览API响应:', response);
|
||||
|
||||
if (response.code === 200) {
|
||||
console.log('设置积分概览数据:', response.data);
|
||||
this.setData({
|
||||
pointsOverview: response.data
|
||||
});
|
||||
} else {
|
||||
console.error('获取积分概览失败 - 业务错误:', response.message);
|
||||
throw new Error(response.message || '获取积分概览失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取积分概览失败:', error);
|
||||
throw error; // 重新抛出错误,让上层处理
|
||||
}
|
||||
},
|
||||
|
||||
// 加载积分历史记录
|
||||
async loadPointsHistory(page = 1, append = false) {
|
||||
try {
|
||||
const response = await getPointsHistory({ page, pageSize: 10 });
|
||||
if (response.code === 200) {
|
||||
const newHistory = response.data.list.map(item => ({
|
||||
...item,
|
||||
type: item.type === 1 ? 'earn' : 'spend', // 转换类型:1=获得(earn), 2=消费(spend)
|
||||
date: this.formatDate(item.created_at),
|
||||
time: this.formatTime(item.created_at)
|
||||
}));
|
||||
|
||||
this.setData({
|
||||
pointsHistory: append ? [...this.data.pointsHistory, ...newHistory] : newHistory,
|
||||
historyPage: page,
|
||||
hasMoreHistory: newHistory.length === 10
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取积分历史记录失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 加载积分规则
|
||||
async loadPointsRules() {
|
||||
try {
|
||||
const response = await getPointsRules();
|
||||
if (response.code === 200) {
|
||||
this.setData({
|
||||
pointsRules: response.data.map(rule => ({
|
||||
id: rule.id,
|
||||
title: rule.title, // 后端返回的字段名是title,不是name
|
||||
description: rule.description
|
||||
}))
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取积分规则失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 加载兑换商品
|
||||
async loadExchangeItems(page = 1, append = false) {
|
||||
try {
|
||||
const response = await getPointsExchangeList({ page, pageSize: 10 });
|
||||
if (response.code === 200) {
|
||||
// 后端直接返回数组,不是包含list的对象
|
||||
const dataArray = Array.isArray(response.data) ? response.data : (response.data.list || []);
|
||||
const newItems = dataArray.map(item => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
points: item.points, // 后端返回的字段名是points,不是points_required
|
||||
image: item.image,
|
||||
stock: item.stock
|
||||
}));
|
||||
|
||||
this.setData({
|
||||
exchangeItems: append ? [...this.data.exchangeItems, ...newItems] : newItems,
|
||||
exchangePage: page,
|
||||
hasMoreExchange: newItems.length === 10
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取兑换商品失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 加载兑换记录
|
||||
async loadExchangeRecords(page = 1, append = false) {
|
||||
try {
|
||||
const response = await getUserExchangeRecords({ page, pageSize: 10 });
|
||||
if (response.code === 200) {
|
||||
const newRecords = response.data.list.map(record => ({
|
||||
...record,
|
||||
date: this.formatDate(record.created_at)
|
||||
}));
|
||||
|
||||
this.setData({
|
||||
exchangeRecords: append ? [...this.data.exchangeRecords, ...newRecords] : newRecords,
|
||||
recordsPage: page,
|
||||
hasMoreRecords: newRecords.length === 10
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取兑换记录失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 格式化日期
|
||||
formatDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return date.toISOString().split('T')[0];
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return date.toTimeString().split(' ')[0].substring(0, 5); // 返回 HH:MM 格式
|
||||
},
|
||||
|
||||
onTabChange(e) {
|
||||
const { value } = e.detail;
|
||||
this.setData({
|
||||
currentTab: value
|
||||
});
|
||||
|
||||
// 切换到兑换记录标签时加载数据
|
||||
if (value === 3 && this.data.exchangeRecords.length === 0) {
|
||||
this.loadExchangeRecords();
|
||||
}
|
||||
},
|
||||
|
||||
onExchangeItem(e) {
|
||||
const { item } = e.currentTarget.dataset;
|
||||
const { userPoints } = this.data;
|
||||
|
||||
// 检查积分是否足够
|
||||
if (userPoints < item.points) {
|
||||
wx.showToast({
|
||||
title: '积分不足',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查库存
|
||||
if (item.stock <= 0) {
|
||||
wx.showToast({
|
||||
title: '商品已售罄',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示兑换确认弹窗
|
||||
wx.showModal({
|
||||
title: '确认兑换',
|
||||
content: `确定要用${item.points}积分兑换${item.name}吗?\n\n兑换后积分将立即扣除,请确认操作。`,
|
||||
confirmText: '确认兑换',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.doExchangePoints(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async doExchangePoints(item) {
|
||||
try {
|
||||
wx.showLoading({
|
||||
title: '兑换中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
const response = await exchangePoints(item.id, 1);
|
||||
|
||||
if (response.code === 200) {
|
||||
wx.hideLoading();
|
||||
|
||||
// 显示成功提示
|
||||
wx.showToast({
|
||||
title: '兑换成功!',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
// 延迟刷新数据,让用户看到成功提示
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
// 刷新用户积分和相关数据
|
||||
await Promise.all([
|
||||
this.loadUserPoints(),
|
||||
this.loadPointsOverview(),
|
||||
this.loadPointsHistory(1, false),
|
||||
this.loadExchangeItems(1, false)
|
||||
]);
|
||||
} catch (refreshError) {
|
||||
console.error('刷新数据失败:', refreshError);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
} else {
|
||||
throw new Error(response.message || '兑换失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('积分兑换失败:', error);
|
||||
wx.hideLoading();
|
||||
|
||||
// 根据错误类型显示不同的提示
|
||||
let errorMessage = '兑换失败,请重试';
|
||||
if (error.message.includes('积分不足')) {
|
||||
errorMessage = '积分不足,无法兑换';
|
||||
} else if (error.message.includes('库存')) {
|
||||
errorMessage = '商品库存不足';
|
||||
} else if (error.message.includes('网络')) {
|
||||
errorMessage = '网络异常,请检查网络连接';
|
||||
}
|
||||
|
||||
wx.showToast({
|
||||
title: errorMessage,
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
async onPullDownRefresh() {
|
||||
try {
|
||||
await this.init();
|
||||
wx.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
wx.showToast({
|
||||
title: '刷新失败',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
wx.stopPullDownRefresh();
|
||||
}
|
||||
},
|
||||
|
||||
// 上拉加载更多
|
||||
onReachBottom() {
|
||||
const { currentTab } = this.data;
|
||||
|
||||
if (currentTab === 0 && this.data.hasMoreHistory) {
|
||||
// 加载更多积分历史
|
||||
this.loadPointsHistory(this.data.historyPage + 1, true);
|
||||
} else if (currentTab === 2 && this.data.hasMoreExchange) {
|
||||
// 加载更多兑换商品
|
||||
this.loadExchangeItems(this.data.exchangePage + 1, true);
|
||||
} else if (currentTab === 3 && this.data.hasMoreRecords) {
|
||||
// 加载更多兑换记录
|
||||
this.loadExchangeRecords(this.data.recordsPage + 1, true);
|
||||
}
|
||||
},
|
||||
|
||||
async onShow() {
|
||||
// 未登录则不触发签到与刷新
|
||||
if (!weChatAuthService.isLoggedIn()) {
|
||||
return;
|
||||
}
|
||||
// 每日签到:页面显示时尝试发放当天首次登录积分
|
||||
try {
|
||||
const res = await dailyCheckin();
|
||||
console.log('[积分] 每日签到结果:', res);
|
||||
} catch (e) {
|
||||
console.warn('[积分] 每日签到失败或未登录:', e);
|
||||
}
|
||||
// 刷新积分相关数据
|
||||
try {
|
||||
await Promise.all([
|
||||
this.loadUserPoints(),
|
||||
this.loadPointsOverview(),
|
||||
this.loadPointsHistory(1, false)
|
||||
]);
|
||||
} catch (err) {
|
||||
console.warn('[积分] 刷新数据失败:', err);
|
||||
}
|
||||
},
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: '我的积分',
|
||||
path: '/pages/points/index'
|
||||
};
|
||||
}
|
||||
});
|
||||
14
miniprogram/pages/points/index.json
Normal file
14
miniprogram/pages/points/index.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"navigationBarTitleText": "我的积分",
|
||||
"enablePullDownRefresh": true,
|
||||
"backgroundColor": "#f5f5f5",
|
||||
"usingComponents": {
|
||||
"t-tabs": "tdesign-miniprogram/tabs/tabs",
|
||||
"t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel",
|
||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||
"t-button": "tdesign-miniprogram/button/button",
|
||||
"t-cell": "tdesign-miniprogram/cell/cell",
|
||||
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
|
||||
"t-tag": "tdesign-miniprogram/tag/tag"
|
||||
}
|
||||
}
|
||||
156
miniprogram/pages/points/index.wxml
Normal file
156
miniprogram/pages/points/index.wxml
Normal file
@@ -0,0 +1,156 @@
|
||||
<view class="points-page">
|
||||
<!-- 积分总览 -->
|
||||
<view class="points-header">
|
||||
<view class="points-card">
|
||||
<view class="points-title">我的积分</view>
|
||||
<view class="points-amount">{{userPoints}}</view>
|
||||
<view class="points-desc">积分可用于兑换优惠券和商品</view>
|
||||
|
||||
<!-- 积分概览统计 -->
|
||||
<view class="points-stats">
|
||||
<view class="stat-item">
|
||||
<view class="stat-value">{{pointsOverview.total_earned}}</view>
|
||||
<view class="stat-label">累计获得</view>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-value">{{pointsOverview.total_spent}}</view>
|
||||
<view class="stat-label">累计消费</view>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<view class="stat-value">{{pointsOverview.this_month_earned}}</view>
|
||||
<view class="stat-label">本月获得</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 标签页 -->
|
||||
<view class="tabs-container">
|
||||
<t-tabs
|
||||
defaultValue="{{currentTab}}"
|
||||
bind:change="onTabChange"
|
||||
tabList="{{tabList}}"
|
||||
t-class="tabs-external__inner"
|
||||
t-class-item="tabs-external__item"
|
||||
t-class-active="tabs-external__active"
|
||||
t-class-track="tabs-external__track"
|
||||
>
|
||||
<t-tab-panel
|
||||
wx:for="{{tabList}}"
|
||||
wx:for-index="index"
|
||||
wx:for-item="tab"
|
||||
wx:key="key"
|
||||
label="{{tab.text}}"
|
||||
value="{{tab.key}}"
|
||||
/>
|
||||
</t-tabs>
|
||||
</view>
|
||||
|
||||
<!-- 积分明细 -->
|
||||
<view class="tab-content" wx:if="{{currentTab === 0}}">
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-container" wx:if="{{loading}}">
|
||||
<t-loading theme="circular" size="40rpx" text="加载中..." />
|
||||
</view>
|
||||
|
||||
<!-- 积分历史列表 -->
|
||||
<view class="points-history" wx:elif="{{pointsHistory.length > 0}}">
|
||||
<view class="history-item" wx:for="{{pointsHistory}}" wx:key="id">
|
||||
<view class="history-left">
|
||||
<view class="history-icon {{item.type === 'earn' ? 'earn' : 'spend'}}">
|
||||
<t-icon name="{{item.type === 'earn' ? 'add' : 'remove'}}" size="16" color="#fff" />
|
||||
</view>
|
||||
<view class="history-info">
|
||||
<view class="history-desc">{{item.description}}</view>
|
||||
<view class="history-time">{{item.date}} {{item.time}}</view>
|
||||
<view class="history-detail" wx:if="{{item.orderId}}">订单号:{{item.orderId}}</view>
|
||||
<view class="history-detail" wx:if="{{item.couponName}}">{{item.couponName}}</view>
|
||||
<view class="history-detail" wx:if="{{item.productName}}">商品:{{item.productName}}</view>
|
||||
<view class="history-detail" wx:if="{{item.days}}">连续签到{{item.days}}天</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="history-amount {{item.type === 'earn' ? 'earn' : 'spend'}}">
|
||||
{{item.type === 'earn' ? '+' : ''}}{{item.points}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多提示 -->
|
||||
<view class="load-more" wx:if="{{hasMoreHistory}}">
|
||||
<t-loading theme="circular" size="32rpx" text="加载更多..." />
|
||||
</view>
|
||||
<view class="no-more" wx:else>
|
||||
<text>没有更多记录了</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" wx:else>
|
||||
<view class="empty-icon">
|
||||
<t-icon name="file" size="80" color="#ccc" />
|
||||
</view>
|
||||
<view class="empty-text">暂无积分记录</view>
|
||||
<view class="empty-desc">快去购物赚取积分吧!</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 积分规则 -->
|
||||
<view class="tab-content" wx:if="{{currentTab === 1}}">
|
||||
<view class="rules-container">
|
||||
<view class="rule-item" wx:for="{{pointsRules}}" wx:key="title">
|
||||
<view class="rule-icon">
|
||||
<t-icon name="{{item.icon}}" size="24" color="#fa4126" />
|
||||
</view>
|
||||
<view class="rule-content">
|
||||
<view class="rule-title">{{item.title}}</view>
|
||||
<view class="rule-desc">{{item.description}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 积分兑换 -->
|
||||
<view class="tab-content" wx:if="{{currentTab === 2}}">
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-container" wx:if="{{loading}}">
|
||||
<t-loading theme="circular" size="40rpx" text="加载中..." />
|
||||
</view>
|
||||
|
||||
<!-- 兑换商品列表 -->
|
||||
<view class="exchange-container" wx:elif="{{exchangeItems.length > 0}}">
|
||||
<view class="exchange-item" wx:for="{{exchangeItems}}" wx:key="id" data-item="{{item}}" bind:tap="onExchangeItem">
|
||||
<view class="exchange-image">
|
||||
<t-icon name="{{item.type === 'coupon' ? 'coupon' : 'gift'}}" size="32" color="#fa4126" />
|
||||
</view>
|
||||
<view class="exchange-info">
|
||||
<view class="exchange-name">{{item.name}}</view>
|
||||
<view class="exchange-desc">{{item.description}}</view>
|
||||
<view class="exchange-stock" wx:if="{{item.stock <= 10}}">
|
||||
<text class="stock-warning">仅剩{{item.stock}}件</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="exchange-points">
|
||||
<view class="points-text">{{item.points}}积分</view>
|
||||
<t-button
|
||||
size="small"
|
||||
theme="{{userPoints >= item.points && item.stock > 0 ? 'primary' : 'default'}}"
|
||||
variant="{{userPoints >= item.points && item.stock > 0 ? 'base' : 'outline'}}"
|
||||
disabled="{{userPoints < item.points || item.stock <= 0}}"
|
||||
>
|
||||
{{item.stock <= 0 ? '已售罄' : (userPoints >= item.points ? '兑换' : '积分不足')}}
|
||||
</t-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" wx:else>
|
||||
<view class="empty-icon">
|
||||
<t-icon name="shop" size="80" color="#ccc" />
|
||||
</view>
|
||||
<view class="empty-text">暂无兑换商品</view>
|
||||
<view class="empty-desc">敬请期待更多精彩商品</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
340
miniprogram/pages/points/index.wxss
Normal file
340
miniprogram/pages/points/index.wxss
Normal file
@@ -0,0 +1,340 @@
|
||||
.points-page {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 积分头部 */
|
||||
.points-header {
|
||||
padding: 32rpx;
|
||||
background: linear-gradient(135deg, #fa4126 0%, #ff6b47 100%);
|
||||
}
|
||||
|
||||
.points-card {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16rpx;
|
||||
padding: 40rpx;
|
||||
text-align: center;
|
||||
backdrop-filter: blur(10rpx);
|
||||
}
|
||||
|
||||
.points-title {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.points-amount {
|
||||
color: #fff;
|
||||
font-size: 72rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.points-desc {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
/* 积分统计 */
|
||||
.points-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
margin-top: 32rpx;
|
||||
padding-top: 32rpx;
|
||||
border-top: 1rpx solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.stat-divider {
|
||||
width: 1rpx;
|
||||
height: 40rpx;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 标签页 */
|
||||
.tabs-container {
|
||||
background: #fff;
|
||||
margin-top: -20rpx;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
|
||||
.tabs-external__inner {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.tabs-external__item {
|
||||
color: #666;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.tabs-external__active {
|
||||
color: #fa4126;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tabs-external__track {
|
||||
background-color: #fa4126;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.tab-content {
|
||||
background: #fff;
|
||||
min-height: 60vh;
|
||||
}
|
||||
|
||||
/* 积分明细 */
|
||||
.points-history {
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.history-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.history-left {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.history-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.history-icon.earn {
|
||||
background-color: #52c41a;
|
||||
}
|
||||
|
||||
.history-icon.spend {
|
||||
background-color: #ff4d4f;
|
||||
}
|
||||
|
||||
.history-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.history-desc {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.history-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.history-detail {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.history-amount {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.history-amount.earn {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.history-amount.spend {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
/* 积分规则 */
|
||||
.rules-container {
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.rule-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 32rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.rule-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.rule-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #fff2f0;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.rule-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.rule-title {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.rule-desc {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 积分兑换 */
|
||||
.exchange-container {
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.exchange-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
border: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.exchange-image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background-color: #fff2f0;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.exchange-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.exchange-name {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.exchange-desc {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.exchange-points {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.points-text {
|
||||
font-size: 28rpx;
|
||||
color: #fa4126;
|
||||
font-weight: bold;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 120rpx 32rpx;
|
||||
}
|
||||
|
||||
/* 加载更多 */
|
||||
.load-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 32rpx;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.no-more {
|
||||
text-align: center;
|
||||
padding: 32rpx;
|
||||
color: #ccc;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 120rpx 32rpx;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin: 0 auto 32rpx;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 24rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/* 兑换相关样式 */
|
||||
.exchange-stock {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.stock-warning {
|
||||
color: #ff4d4f;
|
||||
font-size: 22rpx;
|
||||
background-color: #fff2f0;
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 4rpx;
|
||||
border: 1rpx solid #ffccc7;
|
||||
}
|
||||
Reference in New Issue
Block a user