feat(commission): 收益中心新增30天保活状态与秒级动态预警倒计时看板

这个提交包含在:
chen
2026-09-17 20:06:38 +08:00
父节点 29aeb63026
当前提交 7fc169fb6e
共有 3 个文件被更改,包括 673 次插入 和 32 次删除

查看文件

@@ -3,13 +3,60 @@
<s-layout navbar="inner" class="index-wrap" title="收益中心" :bgStyle="bgStyle" onShareAppMessage>
<!-- 分销商信息 -->
<commission-info />
<!-- 30 日活跃警示 -->
<view v-if="userInfo?.active_30d === false || agentInfo?.active_30d === false" class="active-warn">
<view class="warn-text">近 30 日暂无本人订单,业务奖励已暂停。</view>
<button class="ss-reset-button warn-btn" @tap="sheep.$router.go('/pages/index/index')">
去首页
</button>
<!-- 30 天收益保活与预警倒计时看板 -->
<view class="active-countdown-card" :class="'status-' + activeState.status">
<!-- 头部状态栏 -->
<view class="card-header ss-flex ss-row-between ss-col-center">
<view class="header-left ss-flex ss-col-center">
<view class="status-icon-box ss-flex ss-row-center ss-col-center">
<text v-if="activeState.status === 'active'" class="cicon-check icon-symbol text-green"></text>
<text v-else-if="activeState.status === 'expiring'" class="cicon-notice icon-symbol text-amber"></text>
<text v-else class="cicon-close icon-symbol text-red"></text>
</view>
<view class="header-title-box ss-m-l-12 ss-flex ss-col-center">
<text class="status-title-text">{{ activeState.title }}</text>
<text class="status-pill ss-m-l-10" :class="'pill-' + activeState.status">{{ activeState.badge }}</text>
</view>
</view>
<button class="ss-reset-button action-btn" :class="'btn-' + activeState.status" @tap="onActionTap">
{{ activeState.btnText }}
</button>
</view>
<!-- 倒计时与状态内容 -->
<view class="card-body ss-m-t-14">
<!-- 正常活跃 / 临期预警:展示秒级动态跳动倒计时 -->
<view v-if="activeState.status !== 'inactive'" class="countdown-row ss-flex ss-col-center ss-row-between">
<view class="countdown-label ss-flex ss-col-center">
<text class="label-prefix">保活剩余:</text>
<text class="label-days-highlight">{{ countdown.days }}</text>
<text class="label-days-text">天</text>
</view>
<view class="timer-digits ss-flex ss-col-center">
<view class="digit-box"><text class="num">{{ countdown.hours }}</text><text class="unit">时</text></view>
<text class="colon">:</text>
<view class="digit-box"><text class="num">{{ countdown.minutes }}</text><text class="unit">分</text></view>
<text class="colon">:</text>
<view class="digit-box sec-box"><text class="num">{{ countdown.seconds }}</text><text class="unit">秒</text></view>
</view>
</view>
<!-- 已暂停状态说明 -->
<view v-else class="inactive-row ss-flex ss-col-center">
<text class="inactive-desc">近 30 日内暂无本人自购订单,所有收益分成已暂缓沉淀。</text>
</view>
<!-- 规则小贴士与上次自购时间 -->
<view class="rule-footer ss-flex ss-row-between ss-col-center ss-m-t-10">
<view class="footer-left ss-flex ss-col-center ss-line-1 ss-flex-1">
<text class="footer-dot">●</text>
<text class="footer-tip-text ss-line-1">{{ activeState.tip }}</text>
</view>
<text v-if="activeState.lastOrderText" class="footer-right-time ss-m-l-10">{{ activeState.lastOrderText }}</text>
</view>
</view>
</view>
<!-- 账户信息 -->
<account-info />
<!-- 团队作战快捷看板 -->
@@ -21,7 +68,8 @@
<view class="team-quick-info ss-m-l-16">
<view class="team-title ss-flex ss-col-center">
<text class="title-bold">团队作战中心</text>
<text class="active-badge ss-m-l-10" v-if="userInfo?.active_30d !== false && agentInfo?.active_30d !== false">正常活跃</text>
<text class="active-badge ss-m-l-10" v-if="activeState.status === 'active'">正常活跃 (剩{{ countdown.days }}天)</text>
<text class="amber-badge ss-m-l-10" v-else-if="activeState.status === 'expiring'">临期预警 (仅剩{{ countdown.days }}天)</text>
<text class="warn-badge ss-m-l-10" v-else>奖励暂停</text>
</view>
<view class="team-sub ss-m-t-6">
@@ -48,8 +96,9 @@
<script setup>
import sheep from '@/sheep';
import { onShow } from '@dcloudio/uni-app';
import { onShow, onHide, onUnload } from '@dcloudio/uni-app';
import { computed, reactive } from 'vue';
import dayjs from 'dayjs';
import commissionInfo from './components/commission-info.vue';
import accountInfo from './components/account-info.vue';
import commissionLog from './components/commission-log.vue';
@@ -68,6 +117,130 @@
const agentInfo = computed(() => sheep.$store('user').agentInfo);
const userInfo = computed(() => sheep.$store('user').userInfo);
// 实时倒计时数据
const countdown = reactive({
days: '00',
hours: '00',
minutes: '00',
seconds: '00',
totalSeconds: 0,
});
let timer = null;
// 活跃状态与文案综合计算
const activeState = computed(() => {
const user = userInfo.value || {};
const agent = agentInfo.value || {};
const lastTime = Number(agent.last_self_order_time || user.last_self_order_time || 0);
const isExplicitInactive = user.active_30d === false || agent.active_30d === false;
// 1. 明确暂停或无自购
if (isExplicitInactive || (lastTime <= 0 && agent.active_30d === false)) {
return {
status: 'inactive',
title: '收益奖励已暂停',
badge: '暂缓计奖',
btnText: '去选购恢复',
tip: '次日完成任意一笔本人自购订单即可自动恢复保活',
lastOrderText: lastTime > 0 ? `上次自购: ${dayjs(lastTime * 1000).format('MM-DD HH:mm')}` : '暂无自购记录',
};
}
// 2. 计算到期时间戳
const nowSec = Math.floor(Date.now() / 1000);
let expireTime = Number(agent.expire_time || 0);
if (!expireTime) {
expireTime = lastTime > 0 ? (lastTime + 30 * 86400) : (nowSec + 30 * 86400);
}
const remainingSeconds = Math.max(0, expireTime - nowSec);
const remainingDays = Math.ceil(remainingSeconds / 86400);
if (remainingSeconds <= 0) {
return {
status: 'inactive',
title: '收益奖励已暂停',
badge: '暂缓计奖',
btnText: '去选购恢复',
tip: '近30日暂无本人自购,次日下单后即可自动激活',
lastOrderText: lastTime > 0 ? `上次自购: ${dayjs(lastTime * 1000).format('MM-DD HH:mm')}` : '',
};
}
if (remainingDays <= 5) {
return {
status: 'expiring',
title: '30天保活临期预警',
badge: `仅剩 ${remainingDays} 天`,
btnText: '立即自购保活',
tip: '即将暂停分润!请在倒计时结束前完成自购续期',
lastOrderText: lastTime > 0 ? `上次自购: ${dayjs(lastTime * 1000).format('MM-DD HH:mm')}` : '',
};
}
return {
status: 'active',
title: '30天收益资格生效中',
badge: '正常活跃',
btnText: '去自购续期',
tip: '已满足30日保活,享有全部团队分红与奖项',
lastOrderText: lastTime > 0 ? `上次自购: ${dayjs(lastTime * 1000).format('MM-DD HH:mm')}` : '',
};
});
function updateCountdown() {
const user = userInfo.value || {};
const agent = agentInfo.value || {};
const lastTime = Number(agent.last_self_order_time || user.last_self_order_time || 0);
if (user.active_30d === false || agent.active_30d === false) {
countdown.days = '00';
countdown.hours = '00';
countdown.minutes = '00';
countdown.seconds = '00';
countdown.totalSeconds = 0;
return;
}
const nowSec = Math.floor(Date.now() / 1000);
let expireTime = Number(agent.expire_time || 0);
if (!expireTime) {
expireTime = lastTime > 0 ? (lastTime + 30 * 86400) : (nowSec + 30 * 86400);
}
const remaining = Math.max(0, expireTime - nowSec);
countdown.totalSeconds = remaining;
const d = Math.floor(remaining / 86400);
const h = Math.floor((remaining % 86400) / 3600);
const m = Math.floor((remaining % 3600) / 60);
const s = remaining % 60;
countdown.days = d < 10 ? '0' + d : String(d);
countdown.hours = h < 10 ? '0' + h : String(h);
countdown.minutes = m < 10 ? '0' + m : String(m);
countdown.seconds = s < 10 ? '0' + s : String(s);
}
function startTimer() {
if (timer) clearInterval(timer);
updateCountdown();
timer = setInterval(updateCountdown, 1000);
}
function stopTimer() {
if (timer) {
clearInterval(timer);
timer = null;
}
}
function onActionTap() {
sheep.$router.go('/pages/index/index');
}
async function getUserInfo() {
await sheep.$store('user').getInfo();
}
@@ -82,10 +255,21 @@
state.error = res?.code || 0;
state.errorData = res?.data || {};
}
updateCountdown();
}
onShow(() => {
getUserInfo();
getAgentInfo();
startTimer();
});
onHide(() => {
stopTimer();
});
onUnload(() => {
stopTimer();
});
</script>
@@ -94,35 +278,231 @@
background-size: 100% 100% !important;
}
.active-warn {
.active-countdown-card {
width: 694rpx;
margin: 0 auto 16rpx;
padding: 20rpx 24rpx;
margin: 16rpx auto 0;
padding: 24rpx 28rpx;
box-sizing: border-box;
background: #fff4e5;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 16rpx;
position: relative;
z-index: 3;
transition: all 0.3s ease;
.warn-text {
flex: 1;
font-size: 24rpx;
line-height: 34rpx;
color: #a17545;
padding-right: 16rpx;
// 正常活跃状态
&.status-active {
background: linear-gradient(135deg, #f0fdf4 0%, #ffffff 100%);
border: 1rpx solid #bbf7d0;
box-shadow: 0 4rpx 16rpx rgba(187, 247, 208, 0.35);
.status-icon-box {
background: #dcfce7;
}
.status-title-text {
color: #166534;
}
.pill-active {
background: #dcfce7;
color: #15803d;
}
.btn-active {
background: #059669;
color: #ffffff;
}
.digit-box {
background: #ecfdf5;
border: 1rpx solid #a7f3d0;
.num {
color: #047857;
}
.unit {
color: #065f46;
}
}
.colon {
color: #059669;
}
}
.warn-btn {
flex-shrink: 0;
height: 52rpx;
padding: 0 22rpx;
background: #ff6000;
border-radius: 26rpx;
font-size: 22rpx;
color: #ffffff;
// 临期预警状态
&.status-expiring {
background: linear-gradient(135deg, #fff7ed 0%, #fffbeb 100%);
border: 1rpx solid #fed7aa;
box-shadow: 0 4rpx 18rpx rgba(253, 186, 116, 0.45);
.status-icon-box {
background: #ffedd5;
}
.status-title-text {
color: #9a3412;
}
.pill-expiring {
background: #ffedd5;
color: #c2410c;
font-weight: bold;
}
.btn-expiring {
background: linear-gradient(90deg, #ea580c 0%, #f97316 100%);
color: #ffffff;
box-shadow: 0 4rpx 12rpx rgba(234, 88, 12, 0.3);
}
.digit-box {
background: #ffedd5;
border: 1rpx solid #fdba74;
.num {
color: #c2410c;
}
.unit {
color: #9a3412;
}
}
.colon {
color: #ea580c;
}
}
// 奖励暂停状态
&.status-inactive {
background: linear-gradient(135deg, #fef2f2 0%, #fff1f2 100%);
border: 1rpx solid #fecaca;
box-shadow: 0 4rpx 16rpx rgba(254, 202, 202, 0.35);
.status-icon-box {
background: #fee2e2;
}
.status-title-text {
color: #991b1b;
}
.pill-inactive {
background: #fee2e2;
color: #b91c1c;
}
.btn-inactive {
background: #dc2626;
color: #ffffff;
}
}
.card-header {
.status-icon-box {
width: 48rpx;
height: 48rpx;
border-radius: 24rpx;
.icon-symbol {
font-size: 26rpx;
font-weight: bold;
}
.text-green { color: #059669; }
.text-amber { color: #d97706; }
.text-red { color: #dc2626; }
}
.status-title-text {
font-size: 28rpx;
font-weight: bold;
}
.status-pill {
font-size: 20rpx;
padding: 4rpx 14rpx;
border-radius: 20rpx;
font-weight: 500;
}
.action-btn {
height: 52rpx;
padding: 0 24rpx;
border-radius: 26rpx;
font-size: 22rpx;
font-weight: 500;
}
}
.card-body {
.countdown-row {
background: rgba(255, 255, 255, 0.7);
padding: 12rpx 16rpx;
border-radius: 10rpx;
.countdown-label {
.label-prefix {
font-size: 24rpx;
color: #64748b;
}
.label-days-highlight {
font-size: 30rpx;
font-weight: bold;
color: #0f172a;
margin: 0 2rpx;
font-family: OPPOSANS;
}
.label-days-text {
font-size: 22rpx;
color: #475569;
}
}
.timer-digits {
.digit-box {
padding: 4rpx 8rpx;
border-radius: 6rpx;
display: flex;
align-items: baseline;
min-width: 52rpx;
justify-content: center;
.num {
font-family: OPPOSANS, DIN, monospace;
font-size: 24rpx;
font-weight: bold;
}
.unit {
font-size: 18rpx;
margin-left: 2rpx;
}
}
.colon {
font-weight: bold;
font-size: 24rpx;
margin: 0 4rpx;
}
}
}
.inactive-row {
background: rgba(254, 226, 226, 0.4);
padding: 14rpx 18rpx;
border-radius: 10rpx;
.inactive-desc {
font-size: 24rpx;
color: #991b1b;
line-height: 34rpx;
}
}
.rule-footer {
font-size: 20rpx;
color: #94a3b8;
.footer-dot {
font-size: 14rpx;
color: #cbd5e1;
margin-right: 6rpx;
}
.footer-tip-text {
font-size: 22rpx;
color: #64748b;
}
.footer-right-time {
font-size: 20rpx;
color: #94a3b8;
font-family: OPPOSANS;
flex-shrink: 0;
}
}
}
}
@@ -159,6 +539,14 @@
color: #059669;
font-weight: 500;
}
.amber-badge {
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 12rpx;
background: #ffedd5;
color: #c2410c;
font-weight: 500;
}
.warn-badge {
font-size: 20rpx;
padding: 2rpx 10rpx;