1. extract common onShow login check to dedicated method redirectTologinOnShowLifecycle 2. add fallback callback for login failure to navigate back on wechat mini program 3. update all page login redirect calls to use the new unified method 4. add debug logs for redirect and auth flow tracing
314 lines
6.9 KiB
Vue
314 lines
6.9 KiB
Vue
<template>
|
|
<view :style="themeColor">
|
|
<!-- 顶部统计 -->
|
|
<view class="stat-header">
|
|
<view class="stat-item">
|
|
<view class="stat-value">{{ cardStat.total_consume }}</view>
|
|
<view class="stat-label">{{ $lang('totalConsume') }}{{ $lang('statUnit', { unit: $lang('yuanSuffix') }) }}</view>
|
|
</view>
|
|
<view class="stat-divider"></view>
|
|
<view class="stat-item">
|
|
<view class="stat-value">{{ cardStat.balance }}</view>
|
|
<view class="stat-label">{{ $lang('cardBalance') }}{{ $lang('statUnit', { unit: $lang('yuanSuffix') }) }}</view>
|
|
</view>
|
|
<view class="stat-divider"></view>
|
|
<view class="stat-item">
|
|
<view class="stat-value">{{ cardStat.total_recharge }}</view>
|
|
<view class="stat-label">{{ $lang('totalRecharge') }}{{ $lang('statUnit', { unit: $lang('yuanSuffix') }) }}</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 筛选 tab -->
|
|
<scroll-view class="filter-tab" :scroll-x="true" :show-scrollbar="false">
|
|
<view v-for="(item, index) in filterList" :key="index"
|
|
class="tab-item" :class="{ active: currentFilter === item.value }"
|
|
@click="onFilterChange(item.value)">
|
|
<text>{{ item.label }}</text>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- 列表 -->
|
|
<mescroll-uni @getData="getData" ref="mescroll">
|
|
<block slot="list">
|
|
<block v-if="logList.length > 0">
|
|
<view class="log-list">
|
|
<view class="log-item" v-for="(item, index) in logList" :key="index" @click="goDetail(item)">
|
|
<view class="log-icon" :class="item.type">
|
|
<text class="iconfont" :class="getIconClass(item.type)"></text>
|
|
</view>
|
|
<view class="log-info">
|
|
<view class="log-title">{{ item.title }}</view>
|
|
<view class="log-desc">{{ item.desc }}</view>
|
|
<view class="log-time">{{ formatTime(item.create_time) }}</view>
|
|
</view>
|
|
<view class="log-amount">
|
|
<text :class="item.amount > 0 ? 'income' : 'expense'">
|
|
{{ item.amount > 0 ? '+' : '' }}{{ $lang('amountWithUnit', { amount: item.amount }) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</block>
|
|
<block v-else>
|
|
<ns-empty :isIndex="false" :text="$lang('emptyTips')"></ns-empty>
|
|
</block>
|
|
</block>
|
|
</mescroll-uni>
|
|
|
|
<ns-login ref="login"></ns-login>
|
|
<loading-cover ref="loadingCover"></loading-cover>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
cardStat: {
|
|
total_consume: '0.00',
|
|
balance: '0.00',
|
|
total_recharge: '0.00'
|
|
},
|
|
logList: [],
|
|
filterList: [
|
|
{ label: this.$lang('filterAll'), value: 'all' },
|
|
{ label: this.$lang('filterIncome'), value: 'income' },
|
|
{ label: this.$lang('filterExpense'), value: 'expense' },
|
|
{ label: this.$lang('filterRecharge'), value: 'recharge' },
|
|
{ label: this.$lang('filterConsume'), value: 'consume' },
|
|
{ label: this.$lang('filterGift'), value: 'gift' }
|
|
],
|
|
currentFilter: 'all'
|
|
};
|
|
},
|
|
onLoad() {},
|
|
onShow() {
|
|
if (!this.storeToken) {
|
|
this.redirectTologinOnShowLifecycle('/pages_tool/member/card_log');
|
|
return;
|
|
}
|
|
this.loadStat();
|
|
},
|
|
methods: {
|
|
// 加载统计数据
|
|
loadStat() {
|
|
this.$api.sendRequest({
|
|
url: '/api/membercard/stat',
|
|
success: res => {
|
|
if (res.code == 0 && res.data) {
|
|
this.cardStat = res.data;
|
|
}
|
|
}
|
|
});
|
|
},
|
|
// 筛选切换
|
|
onFilterChange(value) {
|
|
this.currentFilter = value;
|
|
this.$refs.mescroll.refresh();
|
|
},
|
|
// 获取图标 class
|
|
getIconClass(type) {
|
|
const iconMap = {
|
|
recharge: 'icon-chongzhi',
|
|
consume: 'icon-xiaofei',
|
|
gift: 'icon-liwu',
|
|
refund: 'icon-tuikuan'
|
|
};
|
|
return iconMap[type] || 'icon-jifen';
|
|
},
|
|
// 格式化时间
|
|
formatTime(timestamp) {
|
|
return this.$util.timeStampTurnTime(timestamp);
|
|
},
|
|
// 跳转详情
|
|
goDetail(item) {
|
|
if (item.related_type === 'order') {
|
|
this.$util.redirectTo('/pages_order/detail', { order_id: item.related_id });
|
|
}
|
|
},
|
|
// 获取列表数据
|
|
getData(mescroll) {
|
|
this.$api.sendRequest({
|
|
url: '/api/membercard/logPage',
|
|
data: {
|
|
page_size: mescroll.size,
|
|
page: mescroll.num,
|
|
type: this.currentFilter
|
|
},
|
|
success: res => {
|
|
let newArr = [];
|
|
if (res.code == 0 && res.data) {
|
|
newArr = res.data.list;
|
|
} else {
|
|
this.$util.showToast({
|
|
title: res.message
|
|
});
|
|
}
|
|
mescroll.endSuccess(newArr.length);
|
|
if (mescroll.num == 1) {
|
|
this.logList = [];
|
|
}
|
|
this.logList = this.logList.concat(newArr);
|
|
if (this.$refs.loadingCover) this.$refs.loadingCover.hide();
|
|
},
|
|
fail: res => {
|
|
mescroll.endErr();
|
|
if (this.$refs.loadingCover) this.$refs.loadingCover.hide();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.stat-header {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 30rpx;
|
|
background: linear-gradient(135deg, #e3b66b 0%, #d4a04a 100%);
|
|
color: #fff;
|
|
|
|
.stat-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
|
|
.stat-value {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 24rpx;
|
|
opacity: 0.85;
|
|
}
|
|
}
|
|
|
|
.stat-divider {
|
|
width: 1rpx;
|
|
height: 60rpx;
|
|
background: rgba(255, 255, 255, 0.3);
|
|
}
|
|
}
|
|
|
|
.filter-tab {
|
|
width: 100%;
|
|
white-space: nowrap;
|
|
background: #fff;
|
|
border-bottom: 1rpx solid $color-line;
|
|
|
|
.tab-item {
|
|
display: inline-block;
|
|
padding: 24rpx 32rpx;
|
|
font-size: $font-size-base;
|
|
color: $color-tip;
|
|
position: relative;
|
|
|
|
&.active {
|
|
color: $color-base-text;
|
|
font-weight: 500;
|
|
|
|
&::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 40rpx;
|
|
height: 4rpx;
|
|
background: $color-base-text;
|
|
border-radius: 2rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.log-list {
|
|
padding: 0 32rpx;
|
|
background: #fff;
|
|
|
|
.log-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 28rpx 0;
|
|
border-bottom: 1rpx solid $color-line;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.log-icon {
|
|
width: 72rpx;
|
|
height: 72rpx;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 24rpx;
|
|
font-size: 32rpx;
|
|
|
|
&.recharge {
|
|
background: rgba(76, 175, 80, 0.1);
|
|
color: #4caf50;
|
|
}
|
|
|
|
&.consume {
|
|
background: rgba(244, 67, 54, 0.1);
|
|
color: #f44336;
|
|
}
|
|
|
|
&.gift {
|
|
background: rgba(255, 152, 0, 0.1);
|
|
color: #ff9800;
|
|
}
|
|
|
|
&.refund {
|
|
background: rgba(33, 150, 243, 0.1);
|
|
color: #2196f3;
|
|
}
|
|
}
|
|
|
|
.log-info {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
|
|
.log-title {
|
|
font-size: $font-size-base;
|
|
color: $color-base-text;
|
|
font-weight: 500;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.log-desc {
|
|
font-size: $font-size-activity-tag;
|
|
color: $color-tip;
|
|
margin-bottom: 8rpx;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.log-time {
|
|
font-size: $font-size-activity-tag;
|
|
color: $color-tip;
|
|
}
|
|
}
|
|
|
|
.log-amount {
|
|
margin-left: 20rpx;
|
|
font-size: $font-size-toolbar;
|
|
font-weight: 500;
|
|
|
|
.income {
|
|
color: #4caf50;
|
|
}
|
|
|
|
.expense {
|
|
color: $color-base-text;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|