feat(commission): 收益中心新增30天保活状态与秒级动态预警倒计时看板
这个提交包含在:
@@ -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;
|
||||
|
||||
在新工单中引用
屏蔽一个用户