Files
huangjingfen/pro_v3.5.1/view/uniapp_v2/pages/queue/status.vue
panchengyong ec56ae3286 fix(fsgx): 修复 issues-0325-1 前端与后端问题
UniApp:会员码图片兜底、海报下载 Promise、账单移除公排退款、
佣金状态与资产页 NavBar、资产接口 total_points_earned。

后端:推荐人须自报单才得周期佣金;升级前快照等级再发积分;
积分按报单商品数量倍乘;伞下级差按伞下基数传递;直推/伞下任务
统计补充 refund_status;周期佣金在事务内锁推荐人行防竞态;
新增 hjf:verify-agent-config 命令做等级与任务 e2e 验收。

Made-with: Cursor
2026-03-28 10:23:20 +08:00

275 lines
8.6 KiB
Vue

<template>
<view class="brokerage-page" :style="colorStyle">
<!-- #ifdef MP -->
<NavBar titleText="佣金状态" :iconColor="iconColor" :textColor="iconColor" showBack :isScrolling="isScrolling"></NavBar>
<!-- #endif -->
<!-- 顶部渐变区域 -->
<view class="header-gradient">
<view class="header-gradient__circle header-gradient__circle--1"></view>
<view class="header-gradient__circle header-gradient__circle--2"></view>
<view class="header-card">
<view class="header-card__label">累计佣金收入</view>
<view class="header-card__amount">
<text class="header-card__currency">¥</text>
<text class="header-card__value">{{ progressData.total_brokerage || '0.00' }}</text>
</view>
<!-- 佣金周期进度环 -->
<HjfQueueProgress
:cycle-current="progressData.cycle_current || 0"
:cycle-total="progressData.cycle_total || 3"
:cycle-rates="progressData.cycle_rates || [20, 30, 50]"
/>
</view>
</view>
<!-- 佣金记录列表 -->
<view class="records-section">
<view class="section-header">
<view class="section-header__bar"></view>
<text class="section-header__title">佣金记录</text>
<text class="section-header__more" @tap="goToCommissionDetail">查看全部</text>
</view>
<view v-if="loading" class="loading-wrap">
<view class="loading-dot loading-dot--1"></view>
<view class="loading-dot loading-dot--2"></view>
<view class="loading-dot loading-dot--3"></view>
</view>
<view v-else-if="records.length === 0" class="empty-wrap">
<emptyPage title="暂无佣金记录" src="/statics/images/noOrder.gif" />
</view>
<view v-else class="record-list">
<view
v-for="(item, index) in records"
:key="item.id || index"
class="record-item"
>
<view class="record-item__left">
<view class="record-item__avatar-wrap">
<image
class="record-item__avatar"
:src="item.avatar || '/statics/images/avatar.png'"
mode="aspectFill"
/>
</view>
<view class="record-item__info">
<text class="record-item__name">{{ item.nickname || '用户' }}</text>
<text class="record-item__time">{{ item.time }}</text>
</view>
</view>
<view class="record-item__right">
<text class="record-item__amount">+¥{{ item.number }}</text>
<text class="record-item__type">{{ item.title || '推荐佣金' }}</text>
</view>
</view>
</view>
</view>
<!-- 上拉加载 -->
<view v-if="records.length > 0" class="load-more">
<text v-if="loadingMore" class="load-more__text">加载中...</text>
<text v-else-if="finished" class="load-more__text"> 没有更多了 </text>
</view>
<!-- 佣金到账通知 -->
<HjfRefundNotice
:visible="showNotice"
:amount="noticeData.amount"
:order-id="noticeData.orderId"
@close="showNotice = false"
/>
</view>
</template>
<script>
import { getBrokerageProgress } from '@/api/hjfQueue.js';
import HjfQueueProgress from '@/components/HjfQueueProgress.vue';
import HjfRefundNotice from '@/components/HjfRefundNotice.vue';
import emptyPage from '@/components/emptyPage.vue';
import colors from '@/mixins/color.js';
import { spreadOrder } from '@/api/user.js';
// #ifdef MP
import NavBar from '@/components/NavBar.vue';
// #endif
export default {
name: 'BrokerageStatus',
mixins: [colors],
components: {
HjfQueueProgress,
HjfRefundNotice,
emptyPage,
// #ifdef MP
NavBar,
// #endif
},
data() {
return {
iconColor: '#FFFFFF',
isScrolling: false,
progressData: {},
records: [],
loading: false,
loadingMore: false,
finished: false,
page: 1,
limit: 15,
showNotice: false,
noticeData: { amount: 0, orderId: '' }
};
},
onPageScroll(e) {
if (e.scrollTop > 50) {
this.isScrolling = true;
this.iconColor = '#333333';
} else {
this.isScrolling = false;
this.iconColor = '#FFFFFF';
}
},
onLoad() {
this.loadProgress();
this.loadRecords();
},
onReachBottom() {
if (!this.loadingMore && !this.finished) this.loadRecords();
},
methods: {
loadProgress() {
getBrokerageProgress()
.then(res => {
if (res && res.data) this.progressData = res.data;
})
.catch(() => {});
},
loadRecords() {
if (this.loading || this.loadingMore || this.finished) return;
const isFirst = this.page === 1;
if (isFirst) this.loading = true; else this.loadingMore = true;
spreadOrder({ page: this.page, limit: this.limit, category: 'now_money', type: 'brokerage' })
.then(res => {
const list = (res && res.data && res.data.list) ? res.data.list : [];
if (list.length < this.limit) this.finished = true;
if (isFirst) this.records = list;
else this.records = [...this.records, ...list];
this.page++;
})
.catch(() => {
if (isFirst) this.records = [];
})
.finally(() => {
this.loading = false;
this.loadingMore = false;
});
},
goToCommissionDetail() {
uni.navigateTo({ url: '/pages/users/user_spread_money/index' });
}
}
};
</script>
<style scoped lang="scss">
.brokerage-page {
min-height: 100vh;
background: #f4f5f7;
padding-bottom: 60rpx;
}
.header-gradient {
background: linear-gradient(135deg, var(--view-theme, #e93323) 0%, var(--view-gradient, #f76b1c) 100%);
padding: 40rpx 30rpx 56rpx;
position: relative;
overflow: hidden;
}
.header-gradient__circle {
position: absolute;
border-radius: 50%;
background: #fff;
opacity: 0.07;
}
.header-gradient__circle--1 { width: 400rpx; height: 400rpx; top: -150rpx; right: -80rpx; }
.header-gradient__circle--2 { width: 240rpx; height: 240rpx; bottom: -60rpx; left: -50rpx; }
.header-card {
position: relative;
z-index: 1;
background: rgba(255,255,255,0.14);
border-radius: 28rpx;
padding: 36rpx 32rpx;
}
.header-card__label { font-size: 26rpx; color: rgba(255,255,255,0.8); margin-bottom: 8rpx; }
.header-card__amount { display: flex; align-items: baseline; margin-bottom: 28rpx; }
.header-card__currency { font-size: 36rpx; color: #fff; font-weight: 600; margin-right: 4rpx; }
.header-card__value { font-size: 72rpx; font-weight: 700; color: #fff; line-height: 1; }
.records-section {
margin: -16rpx 20rpx 0;
position: relative;
z-index: 2;
background: #fff;
border-radius: 24rpx;
padding: 0 0 20rpx;
overflow: hidden;
box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.06);
}
.section-header {
display: flex;
align-items: center;
padding: 28rpx 30rpx 20rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.section-header__bar {
width: 6rpx; height: 28rpx; border-radius: 3rpx;
background: var(--view-theme, #e93323); margin-right: 14rpx;
}
.section-header__title { font-size: 30rpx; font-weight: 600; color: #333; flex: 1; }
.section-header__more { font-size: 24rpx; color: var(--view-theme, #e93323); }
.loading-wrap {
display: flex; justify-content: center; align-items: center; gap: 12rpx;
padding: 60rpx 0;
}
.loading-dot {
width: 14rpx; height: 14rpx; border-radius: 50%;
background: var(--view-theme, #e93323);
animation: dot-bounce 1.2s infinite ease-in-out;
}
.loading-dot--2 { animation-delay: 0.2s; }
.loading-dot--3 { animation-delay: 0.4s; }
@keyframes dot-bounce {
0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
40% { opacity: 1; transform: scale(1.2); }
}
.empty-wrap { padding: 40rpx 0; }
.record-item {
display: flex; align-items: center; justify-content: space-between;
padding: 28rpx 30rpx;
border-bottom: 1rpx solid #f8f8f8;
}
.record-item__left { display: flex; align-items: center; gap: 20rpx; }
.record-item__avatar-wrap { width: 76rpx; height: 76rpx; border-radius: 50%; overflow: hidden; flex-shrink: 0; }
.record-item__avatar { width: 100%; height: 100%; }
.record-item__name { font-size: 28rpx; color: #333; font-weight: 500; display: block; }
.record-item__time { font-size: 22rpx; color: #999; display: block; margin-top: 6rpx; }
.record-item__right { text-align: right; }
.record-item__amount { font-size: 34rpx; font-weight: 700; color: #e93323; display: block; }
.record-item__type { font-size: 22rpx; color: #aaa; display: block; margin-top: 4rpx; }
.load-more { padding: 32rpx 0 20rpx; text-align: center; }
.load-more__text { font-size: 24rpx; color: #bbb; }
</style>