feat:兑换中心
This commit is contained in:
@@ -19,7 +19,8 @@
|
|||||||
"pages/profile/favorites/index",
|
"pages/profile/favorites/index",
|
||||||
"pages/profile/posts/index",
|
"pages/profile/posts/index",
|
||||||
"pages/profile/about/index",
|
"pages/profile/about/index",
|
||||||
"pages/profile/exchange/index"
|
"pages/profile/exchange/index",
|
||||||
|
"pages/profile/exchange/orders/index"
|
||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"backgroundTextStyle": "light",
|
"backgroundTextStyle": "light",
|
||||||
|
|||||||
@@ -1 +1,196 @@
|
|||||||
Page({});
|
import request from '../../../utils/request';
|
||||||
|
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
items: [],
|
||||||
|
currentSunlight: 0,
|
||||||
|
isLoading: true,
|
||||||
|
hasMore: true,
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
activeType: 'PHYSICAL',
|
||||||
|
// Redeem popup
|
||||||
|
showRedeemPopup: false,
|
||||||
|
selectedItem: null,
|
||||||
|
redeemForm: {
|
||||||
|
recipientName: '',
|
||||||
|
phone: '',
|
||||||
|
address: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
this.fetchProfile();
|
||||||
|
this.fetchItems();
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
this.fetchProfile();
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchProfile() {
|
||||||
|
try {
|
||||||
|
const res = await request.get('/profile/detail');
|
||||||
|
if (res) {
|
||||||
|
this.setData({ currentSunlight: res.currentSunlight || 0 });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Silent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchItems(append = false) {
|
||||||
|
if (!append) {
|
||||||
|
this.setData({ isLoading: true, current: 1, items: [] });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await request.get('/exchange/list', {
|
||||||
|
current: this.data.current,
|
||||||
|
pageSize: this.data.pageSize,
|
||||||
|
type: this.data.activeType
|
||||||
|
});
|
||||||
|
const rawList = (res && res.list) ? res.list : [];
|
||||||
|
const total = (res && res.total) ? res.total : 0;
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
const list = rawList.map(item => {
|
||||||
|
const hasStart = !!item.startTime;
|
||||||
|
const hasEnd = !!item.endTime;
|
||||||
|
const startTs = hasStart ? new Date(item.startTime).getTime() : 0;
|
||||||
|
const endTs = hasEnd ? new Date(item.endTime).getTime() : 0;
|
||||||
|
|
||||||
|
const notStarted = hasStart && now < startTs;
|
||||||
|
const hasEnded = hasEnd && now > endTs;
|
||||||
|
const isActive = !notStarted && !hasEnded;
|
||||||
|
|
||||||
|
let timeLabel = '';
|
||||||
|
if (hasStart && hasEnd) {
|
||||||
|
timeLabel = this.formatDate(item.startTime) + ' ~ ' + this.formatDate(item.endTime);
|
||||||
|
} else if (hasStart) {
|
||||||
|
timeLabel = this.formatDate(item.startTime) + ' 起';
|
||||||
|
} else if (hasEnd) {
|
||||||
|
timeLabel = '截止 ' + this.formatDate(item.endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
hasTimeLimit: hasStart || hasEnd,
|
||||||
|
timeLabel,
|
||||||
|
notStarted,
|
||||||
|
hasEnded,
|
||||||
|
isActive
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
items: append ? [...this.data.items, ...list] : list,
|
||||||
|
hasMore: this.data.items.length + list.length < total
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Fetch exchange items failed', e);
|
||||||
|
} finally {
|
||||||
|
this.setData({ isLoading: false });
|
||||||
|
wx.stopPullDownRefresh();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onReachBottom() {
|
||||||
|
if (!this.data.hasMore || this.data.isLoading) return;
|
||||||
|
this.setData({ current: this.data.current + 1 });
|
||||||
|
this.fetchItems(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
onPullDownRefresh() {
|
||||||
|
this.fetchProfile();
|
||||||
|
this.fetchItems();
|
||||||
|
},
|
||||||
|
|
||||||
|
switchType(e) {
|
||||||
|
const key = e.currentTarget.dataset.key;
|
||||||
|
if (key === this.data.activeType) return;
|
||||||
|
this.setData({ activeType: key });
|
||||||
|
this.fetchItems();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Redeem Flow
|
||||||
|
onItemTap(e) {
|
||||||
|
const item = e.currentTarget.dataset.item;
|
||||||
|
if (item.notStarted) {
|
||||||
|
wx.showToast({ title: '活动尚未开始', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (item.hasEnded) {
|
||||||
|
wx.showToast({ title: '活动已结束', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (item.stock === 0) {
|
||||||
|
wx.showToast({ title: '已兑完', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setData({
|
||||||
|
selectedItem: item,
|
||||||
|
showRedeemPopup: true,
|
||||||
|
redeemForm: { recipientName: '', phone: '', address: '' }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onPopupClose() {
|
||||||
|
this.setData({ showRedeemPopup: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
onFormInput(e) {
|
||||||
|
const field = e.currentTarget.dataset.field;
|
||||||
|
this.setData({ [`redeemForm.${field}`]: e.detail.value });
|
||||||
|
},
|
||||||
|
|
||||||
|
async confirmRedeem() {
|
||||||
|
const item = this.data.selectedItem;
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
// Check sunlight
|
||||||
|
if (this.data.currentSunlight < item.costSunlight) {
|
||||||
|
wx.showToast({ title: '阳光值不足', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Physical items require address
|
||||||
|
if (item.type === 'PHYSICAL') {
|
||||||
|
const { recipientName, phone, address } = this.data.redeemForm;
|
||||||
|
if (!recipientName || !phone || !address) {
|
||||||
|
wx.showToast({ title: '请填写完整收货信息', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wx.showLoading({ title: '兑换中...', mask: true });
|
||||||
|
try {
|
||||||
|
await request.post('/exchange/redeem', {
|
||||||
|
itemId: item.id,
|
||||||
|
quantity: 1,
|
||||||
|
...this.data.redeemForm
|
||||||
|
});
|
||||||
|
wx.hideLoading();
|
||||||
|
wx.showToast({ title: '兑换成功!', icon: 'success' });
|
||||||
|
this.setData({ showRedeemPopup: false });
|
||||||
|
// Refresh data
|
||||||
|
this.fetchProfile();
|
||||||
|
this.fetchItems();
|
||||||
|
} catch (e) {
|
||||||
|
wx.hideLoading();
|
||||||
|
wx.showToast({ title: e.message || '兑换失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
goToOrders() {
|
||||||
|
wx.navigateTo({ url: '/pages/profile/exchange/orders/index' });
|
||||||
|
},
|
||||||
|
|
||||||
|
formatDate(dateStr) {
|
||||||
|
if (!dateStr) return '';
|
||||||
|
const d = new Date(dateStr);
|
||||||
|
const m = (d.getMonth() + 1).toString().padStart(2, '0');
|
||||||
|
const day = d.getDate().toString().padStart(2, '0');
|
||||||
|
return m + '.' + day;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
"navigationBarTitleText": "兑换中心",
|
"navigationBarTitleText": "兑换中心",
|
||||||
|
"disableScroll": true,
|
||||||
"usingComponents": {
|
"usingComponents": {
|
||||||
"t-empty": "tdesign-miniprogram/empty/empty"
|
"t-empty": "tdesign-miniprogram/empty/empty",
|
||||||
|
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||||
|
"t-loading": "tdesign-miniprogram/loading/loading",
|
||||||
|
"t-tag": "tdesign-miniprogram/tag/tag",
|
||||||
|
"t-popup": "tdesign-miniprogram/popup/popup"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,141 @@
|
|||||||
<view class="exchange-page">
|
<view class="exchange-page">
|
||||||
<t-empty icon="info-circle-filled" description="兑换中心功能正在开发中,敬请期待" />
|
|
||||||
|
<!-- Balance Header (sticky) -->
|
||||||
|
<view class="balance-header">
|
||||||
|
<view class="balance-card">
|
||||||
|
<view class="balance-left">
|
||||||
|
<text class="balance-label">我的阳光值</text>
|
||||||
|
<view class="balance-value-row">
|
||||||
|
<text class="balance-emoji">☀️</text>
|
||||||
|
<text class="balance-value">{{currentSunlight}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="balance-right" bindtap="goToOrders">
|
||||||
|
<t-icon name="assignment" size="40rpx" color="#558B2F" />
|
||||||
|
<text class="orders-text">兑换记录</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Scrollable Content -->
|
||||||
|
<scroll-view scroll-y class="page-scroll" show-scrollbar="{{false}}" enhanced="{{true}}" lower-threshold="200" bindscrolltolower="onReachBottom">
|
||||||
|
|
||||||
|
<!-- Items Grid -->
|
||||||
|
<view class="items-grid" wx:if="{{items.length > 0}}">
|
||||||
|
<view class="item-card {{!item.isActive ? 'inactive' : ''}}" wx:for="{{items}}" wx:key="id" bindtap="onItemTap" data-item="{{item}}">
|
||||||
|
<view class="item-image-wrap">
|
||||||
|
<image wx:if="{{item.image}}" src="{{item.image.url}}" mode="aspectFill" class="item-img" lazy-load />
|
||||||
|
<view wx:else class="item-img-placeholder">
|
||||||
|
<t-icon name="gift" size="64rpx" color="#C5E1A5" />
|
||||||
|
</view>
|
||||||
|
<!-- Stock Badge -->
|
||||||
|
<view class="stock-badge" wx:if="{{item.isActive && item.stock >= 0 && item.stock <= 10 && item.stock > 0}}">
|
||||||
|
<text>仅剩 {{item.stock}}</text>
|
||||||
|
</view>
|
||||||
|
<!-- Status overlays -->
|
||||||
|
<view class="sold-out-mask" wx:if="{{item.stock === 0}}">
|
||||||
|
<text>已兑完</text>
|
||||||
|
</view>
|
||||||
|
<view class="sold-out-mask not-started" wx:elif="{{item.notStarted}}">
|
||||||
|
<text>未开始</text>
|
||||||
|
</view>
|
||||||
|
<view class="sold-out-mask ended" wx:elif="{{item.hasEnded}}">
|
||||||
|
<text>已结束</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="item-info">
|
||||||
|
<text class="item-name">{{item.name}}</text>
|
||||||
|
<view class="item-price-row">
|
||||||
|
<text class="price-sun">☀️</text>
|
||||||
|
<text class="price-val">{{item.costSunlight}}</text>
|
||||||
|
</view>
|
||||||
|
<view class="item-time" wx:if="{{item.hasTimeLimit}}">
|
||||||
|
<t-icon name="time" size="24rpx" color="#9CA3AF" />
|
||||||
|
<text>{{item.timeLabel}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Loading / Empty States -->
|
||||||
|
<view class="state-footer" wx:if="{{isLoading}}">
|
||||||
|
<t-loading theme="circular" size="40rpx" text="加载中..." inherit-color />
|
||||||
|
</view>
|
||||||
|
<view class="empty-state" wx:elif="{{!isLoading && items.length === 0}}">
|
||||||
|
<text class="empty-icon">🎁</text>
|
||||||
|
<text class="empty-title">暂无可兑换商品</text>
|
||||||
|
<text class="empty-desc">新的好礼正在路上,敬请期待</text>
|
||||||
|
</view>
|
||||||
|
<view class="state-footer" wx:elif="{{!hasMore && items.length > 0}}">
|
||||||
|
<text class="no-more">— 已经到底啦 —</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Spacer -->
|
||||||
|
<view style="height: 60rpx;"></view>
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- Redeem Popup -->
|
||||||
|
<t-popup visible="{{showRedeemPopup}}" bind:visible-change="onPopupClose" placement="bottom">
|
||||||
|
<view class="redeem-popup" wx:if="{{selectedItem}}">
|
||||||
|
<view class="popup-header">
|
||||||
|
<text class="popup-title">确认兑换</text>
|
||||||
|
<view class="popup-close" bindtap="onPopupClose">
|
||||||
|
<t-icon name="close" size="40rpx" color="#999" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Item Preview -->
|
||||||
|
<view class="redeem-item-preview">
|
||||||
|
<view class="preview-img-wrap">
|
||||||
|
<image wx:if="{{selectedItem.image}}" src="{{selectedItem.image.url}}" mode="aspectFill" class="preview-img" />
|
||||||
|
<view wx:else class="preview-img-placeholder">
|
||||||
|
<t-icon name="gift" size="48rpx" color="#C5E1A5" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="preview-info">
|
||||||
|
<text class="preview-name">{{selectedItem.name}}</text>
|
||||||
|
<text class="preview-desc">{{selectedItem.description}}</text>
|
||||||
|
<view class="preview-cost">
|
||||||
|
<text class="cost-sun">☀️</text>
|
||||||
|
<text class="cost-val">{{selectedItem.costSunlight}}</text>
|
||||||
|
<text class="cost-unit">阳光值</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Address Form (Physical only) -->
|
||||||
|
<view class="address-form" wx:if="{{selectedItem.type === 'PHYSICAL'}}">
|
||||||
|
<view class="form-title">收货信息</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">姓名</text>
|
||||||
|
<input placeholder="请输入收货人姓名" value="{{redeemForm.recipientName}}"
|
||||||
|
bindinput="onFormInput" data-field="recipientName" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">电话</text>
|
||||||
|
<input type="number" placeholder="请输入联系电话" value="{{redeemForm.phone}}"
|
||||||
|
bindinput="onFormInput" data-field="phone" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="form-label">地址</text>
|
||||||
|
<input placeholder="请输入详细收货地址" value="{{redeemForm.address}}"
|
||||||
|
bindinput="onFormInput" data-field="address" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Balance Check -->
|
||||||
|
<view class="balance-check {{currentSunlight >= selectedItem.costSunlight ? '' : 'insufficient'}}">
|
||||||
|
<text>当前余额: ☀️ {{currentSunlight}}</text>
|
||||||
|
<text wx:if="{{currentSunlight < selectedItem.costSunlight}}" class="insufficient-tip">余额不足</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Confirm Button -->
|
||||||
|
<view class="redeem-btn {{currentSunlight >= selectedItem.costSunlight && selectedItem.stock !== 0 ? '' : 'disabled'}}"
|
||||||
|
bindtap="confirmRedeem">
|
||||||
|
<text>立即兑换</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</t-popup>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,9 +1,447 @@
|
|||||||
page {
|
page {
|
||||||
background: #F4F6F0;
|
background: #F4F6F0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.exchange-page {
|
.exchange-page {
|
||||||
display: flex;
|
height: 100vh;
|
||||||
align-items: center;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
height: 100vh;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-scroll {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Balance Header ========== */
|
||||||
|
.balance-header {
|
||||||
|
background: linear-gradient(180deg, #E8F5E9 0%, #F4F6F0 100%);
|
||||||
|
padding: 32rpx 32rpx 24rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 32rpx 36rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-label {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-value-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-emoji {
|
||||||
|
font-size: 36rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-value {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #558B2F;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
background: #F1F8E9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-text {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #558B2F;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Type Tabs ========== */
|
||||||
|
.type-tabs {
|
||||||
|
display: flex;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 20rpx 32rpx;
|
||||||
|
gap: 16rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-tab {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10rpx 28rpx;
|
||||||
|
background: #fff;
|
||||||
|
border: 2rpx solid #E5E7EB;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #6B7280;
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-tab.active {
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Items Grid ========== */
|
||||||
|
.items-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
|
||||||
|
transition: transform 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-card:active {
|
||||||
|
transform: scale(0.97);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-image-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
background: #F8FAF5;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-img-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #F1F8E9, #E8F5E9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 12rpx;
|
||||||
|
right: 12rpx;
|
||||||
|
background: rgba(239, 83, 80, 0.85);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 4rpx 14rpx;
|
||||||
|
border-radius: 14rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sold-out-mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
background: rgba(0,0,0,0.45);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sold-out-mask.not-started {
|
||||||
|
background: rgba(33, 150, 243, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sold-out-mask.ended {
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-card.inactive {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-info {
|
||||||
|
padding: 16rpx 20rpx 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1F2937;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-price-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-sun {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-val {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #E65100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-time {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Empty & Footer States ========== */
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120rpx 40rpx 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 100rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #374151;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-footer {
|
||||||
|
padding: 40rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #D1D5DB;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Redeem Popup ========== */
|
||||||
|
.redeem-popup {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 32rpx 32rpx 0 0;
|
||||||
|
padding: 36rpx;
|
||||||
|
padding-bottom: calc(36rpx + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-title {
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1F2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-close {
|
||||||
|
padding: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Item Preview in Popup */
|
||||||
|
.redeem-item-preview {
|
||||||
|
display: flex;
|
||||||
|
gap: 24rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
background: #F8FAF5;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
margin-bottom: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-img-wrap {
|
||||||
|
width: 140rpx;
|
||||||
|
height: 140rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #E8F5E9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-img-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1F2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-cost {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-sun { font-size: 24rpx; }
|
||||||
|
.cost-val { font-size: 32rpx; font-weight: 800; color: #E65100; }
|
||||||
|
.cost-unit { font-size: 22rpx; color: #9CA3AF; margin-left: 2rpx; }
|
||||||
|
|
||||||
|
/* Address Form */
|
||||||
|
.address-form {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #374151;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #F9FAFB;
|
||||||
|
border: 2rpx solid #F3F4F6;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #6B7280;
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #1F2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Balance Check */
|
||||||
|
.balance-check {
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
background: #F1F8E9;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #558B2F;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-check.insufficient {
|
||||||
|
background: #FFF3E0;
|
||||||
|
color: #E65100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insufficient-tip {
|
||||||
|
color: #EF5350;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Redeem Button */
|
||||||
|
.redeem-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 92rpx;
|
||||||
|
background: linear-gradient(135deg, #558B2F, #689F38);
|
||||||
|
border-radius: 46rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
transition: all 0.2s;
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(85, 139, 47, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.redeem-btn:active {
|
||||||
|
transform: scale(0.97);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redeem-btn.disabled {
|
||||||
|
background: #E5E7EB;
|
||||||
|
color: #9CA3AF;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import request from '../../../../utils/request';
|
||||||
|
|
||||||
|
const STATUS_MAP = {
|
||||||
|
1: { text: '待处理', theme: 'warning' },
|
||||||
|
2: { text: '处理中', theme: 'primary' },
|
||||||
|
3: { text: '已发货', theme: 'primary' },
|
||||||
|
4: { text: '已完成', theme: 'success' },
|
||||||
|
5: { text: '已取消', theme: 'default' }
|
||||||
|
};
|
||||||
|
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
orders: [],
|
||||||
|
isLoading: true,
|
||||||
|
hasMore: true,
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
activeStatus: 0,
|
||||||
|
statusTabs: [
|
||||||
|
{ key: 0, label: '全部' },
|
||||||
|
{ key: 1, label: '待处理' },
|
||||||
|
{ key: 4, label: '已完成' },
|
||||||
|
{ key: 5, label: '已取消' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
this.fetchOrders();
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
this.fetchOrders();
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchOrders(append = false) {
|
||||||
|
if (!append) {
|
||||||
|
this.setData({ isLoading: true, current: 1, orders: [] });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
current: this.data.current,
|
||||||
|
pageSize: this.data.pageSize
|
||||||
|
};
|
||||||
|
if (this.data.activeStatus) {
|
||||||
|
params.status = this.data.activeStatus;
|
||||||
|
}
|
||||||
|
const res = await request.get('/exchange/orders', params);
|
||||||
|
let list = (res && res.list) ? res.list : [];
|
||||||
|
const total = (res && res.total) ? res.total : 0;
|
||||||
|
|
||||||
|
// Enrich with status display info
|
||||||
|
list = list.map(order => ({
|
||||||
|
...order,
|
||||||
|
statusInfo: STATUS_MAP[order.status] || { text: '未知', theme: 'default' }
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
orders: append ? [...this.data.orders, ...list] : list,
|
||||||
|
hasMore: this.data.orders.length + list.length < total
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Fetch orders failed', e);
|
||||||
|
} finally {
|
||||||
|
this.setData({ isLoading: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
switchStatus(e) {
|
||||||
|
const key = e.currentTarget.dataset.key;
|
||||||
|
if (key === this.data.activeStatus) return;
|
||||||
|
this.setData({ activeStatus: key });
|
||||||
|
this.fetchOrders();
|
||||||
|
},
|
||||||
|
|
||||||
|
onReachBottom() {
|
||||||
|
if (!this.data.hasMore || this.data.isLoading) return;
|
||||||
|
this.setData({ current: this.data.current + 1 });
|
||||||
|
this.fetchOrders(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "兑换记录",
|
||||||
|
"disableScroll": true,
|
||||||
|
"usingComponents": {
|
||||||
|
"t-empty": "tdesign-miniprogram/empty/empty",
|
||||||
|
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||||
|
"t-loading": "tdesign-miniprogram/loading/loading",
|
||||||
|
"t-tag": "tdesign-miniprogram/tag/tag"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<view class="orders-page">
|
||||||
|
|
||||||
|
<!-- Status Tabs -->
|
||||||
|
<view class="status-tabs-wrap">
|
||||||
|
<scroll-view class="status-tabs" scroll-x enable-flex show-scrollbar="{{false}}">
|
||||||
|
<view wx:for="{{statusTabs}}" wx:key="key"
|
||||||
|
class="status-tab {{activeStatus === item.key ? 'active' : ''}}"
|
||||||
|
bindtap="switchStatus" data-key="{{item.key}}">
|
||||||
|
<text>{{item.label}}</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<scroll-view scroll-y class="page-scroll" show-scrollbar="{{false}}" enhanced="{{true}}" bindscrolltolower="onReachBottom">
|
||||||
|
|
||||||
|
<view class="orders-list" wx:if="{{orders.length > 0}}">
|
||||||
|
<view class="order-card" wx:for="{{orders}}" wx:key="id">
|
||||||
|
<view class="order-header">
|
||||||
|
<text class="order-time">{{item.createdAtStr}}</text>
|
||||||
|
<t-tag size="small" variant="light" theme="{{item.statusInfo.theme}}">
|
||||||
|
{{item.statusInfo.text}}
|
||||||
|
</t-tag>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="order-body">
|
||||||
|
<view class="order-img-wrap">
|
||||||
|
<image wx:if="{{item.item && item.item.image}}" src="{{item.item.image.url}}" mode="aspectFill" class="order-img" />
|
||||||
|
<view wx:else class="order-img-placeholder">
|
||||||
|
<t-icon name="gift" size="48rpx" color="#C5E1A5" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="order-info">
|
||||||
|
<text class="order-item-name">{{item.itemName}}</text>
|
||||||
|
<text class="order-type-tag">{{item.itemType === 'PHYSICAL' ? '实物' : (item.itemType === 'VIRTUAL' ? '虚拟' : '优惠券')}}</text>
|
||||||
|
<view class="order-cost-row">
|
||||||
|
<text class="order-cost-sun">☀️</text>
|
||||||
|
<text class="order-cost-val">-{{item.costSunlight}}</text>
|
||||||
|
<text class="order-qty" wx:if="{{item.quantity > 1}}">x{{item.quantity}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Shipping Info (if physical and shipped) -->
|
||||||
|
<view class="order-shipping" wx:if="{{item.trackingNo}}">
|
||||||
|
<t-icon name="deliver" size="28rpx" color="#558B2F" />
|
||||||
|
<text class="tracking-text">快递单号: {{item.trackingNo}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Loading / Empty -->
|
||||||
|
<view class="state-footer" wx:if="{{isLoading}}">
|
||||||
|
<t-loading theme="circular" size="40rpx" text="加载中..." inherit-color />
|
||||||
|
</view>
|
||||||
|
<view class="empty-state" wx:elif="{{!isLoading && orders.length === 0}}">
|
||||||
|
<text class="empty-icon">📦</text>
|
||||||
|
<text class="empty-title">暂无兑换记录</text>
|
||||||
|
<text class="empty-desc">去兑换中心挑选心仪好礼吧</text>
|
||||||
|
</view>
|
||||||
|
<view class="state-footer" wx:elif="{{!hasMore && orders.length > 0}}">
|
||||||
|
<text class="no-more">— 已经到底啦 —</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view style="height: 60rpx;"></view>
|
||||||
|
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
page {
|
||||||
|
background: #F4F6F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orders-page {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-scroll {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Status Tabs ========== */
|
||||||
|
.status-tabs-wrap {
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #F4F6F0;
|
||||||
|
padding: 20rpx 0 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tabs {
|
||||||
|
display: flex;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-left: 32rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tab {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 28rpx;
|
||||||
|
height: 72rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 36rpx;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #546E7A;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.04);
|
||||||
|
transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 2rpx solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tab:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tab.active {
|
||||||
|
background: #558B2F;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(85, 139, 47, 0.3);
|
||||||
|
border-color: #558B2F;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Orders List ========== */
|
||||||
|
.orders-list {
|
||||||
|
padding: 12rpx 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 28rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-bottom: 16rpx;
|
||||||
|
border-bottom: 1rpx solid #F3F4F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-time {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-body {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-img-wrap {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #F8FAF5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-img-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #F1F8E9, #E8F5E9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-item-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1F2937;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-type-tag {
|
||||||
|
display: inline-block;
|
||||||
|
width: fit-content;
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #6B7280;
|
||||||
|
background: #F3F4F6;
|
||||||
|
padding: 2rpx 14rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-cost-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-cost-sun {
|
||||||
|
font-size: 22rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-cost-val {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #E65100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-qty {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shipping */
|
||||||
|
.order-shipping {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
padding-top: 16rpx;
|
||||||
|
border-top: 1rpx solid #F3F4F6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracking-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #558B2F;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== Empty & Footer States ========== */
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120rpx 40rpx 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 100rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #374151;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-footer {
|
||||||
|
padding: 40rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #D1D5DB;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"navigationBarTitleText": "我的收藏",
|
"navigationBarTitleText": "我的收藏",
|
||||||
|
"disableScroll": true,
|
||||||
"usingComponents": {
|
"usingComponents": {
|
||||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||||
"t-image": "tdesign-miniprogram/image/image",
|
"t-image": "tdesign-miniprogram/image/image",
|
||||||
|
|||||||
@@ -46,7 +46,6 @@
|
|||||||
<text class="menu-text">兑换中心</text>
|
<text class="menu-text">兑换中心</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="menu-right-info">
|
<view class="menu-right-info">
|
||||||
<text class="menu-badge-text">开发中</text>
|
|
||||||
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
Reference in New Issue
Block a user