2184 lines
50 KiB
Vue
2184 lines
50 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<!-- 顶部蓝色头部 -->
|
||
<view class="header-section">
|
||
<view class="header-content">
|
||
<view class="header-title-row">
|
||
<view class="header-title-group">
|
||
<text class="header-title">分销获客统计</text>
|
||
<text class="header-subtitle">实时数据 · 精准统计</text>
|
||
</view>
|
||
<view class="header-actions">
|
||
<view class="settings-btn" @click="openChangePasswordPopup">
|
||
<view class="settings-circle">
|
||
<uni-icons type="gear" size="20" color="#2563eb"></uni-icons>
|
||
</view>
|
||
<text class="settings-text">设置</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="channel-row">
|
||
<view class="channel-left">
|
||
<text class="channel-label">渠道名称</text>
|
||
<text class="channel-name">{{ channelInfo.name || '--' }}</text>
|
||
</view>
|
||
<view class="channel-code-pill">
|
||
<text class="channel-code-label">分销编码</text>
|
||
<text class="channel-code-text">{{ channelInfo.code || '--' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 统计卡片区域(严格贴近视觉稿布局) -->
|
||
<view class="stats-section">
|
||
<!-- 顶部大卡片:可提现金额 + 收益概览 -->
|
||
<view class="stats-main-card">
|
||
<!-- 当前可提现金额 -->
|
||
<view class="withdraw-header-row">
|
||
<view class="withdraw-title-wrap">
|
||
<view class="withdraw-icon-wrap">
|
||
<uni-icons type="wallet" size="18" color="#4f46e5"></uni-icons>
|
||
</view>
|
||
<text class="withdraw-label">当前可提现金额</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="withdraw-amount-row">
|
||
<text class="withdraw-currency">¥</text>
|
||
<text class="withdraw-amount-text">
|
||
{{ (statistics.withdrawableAmount || 0).toFixed(2) }}
|
||
</text>
|
||
</view>
|
||
|
||
<view class="withdraw-button-row">
|
||
<button class="withdraw-button" :disabled="statisticsLoading" @click="handleWithdraw">
|
||
<uni-icons v-if="!statisticsLoading" type="arrow-up" size="20" color="#fff"></uni-icons>
|
||
<uni-icons v-else type="spinner-cycle" size="20" color="#fff"></uni-icons>
|
||
<text class="withdraw-btn-text">{{ statisticsLoading ? '处理中...' : '一键提现' }}</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 底部三栏收益概览 -->
|
||
<view class="revenue-divider"></view>
|
||
<view class="revenue-row">
|
||
<view class="revenue-item">
|
||
<text class="revenue-label">总收益</text>
|
||
<text class="revenue-value">¥{{ (statistics.totalRevenue || 0).toFixed(2) }}</text>
|
||
</view>
|
||
<view class="revenue-item">
|
||
<text class="revenue-label">待审核</text>
|
||
<text class="revenue-value pending">¥{{ (statistics.pendingReview || 0).toFixed(2) }}</text>
|
||
</view>
|
||
<view class="revenue-item">
|
||
<text class="revenue-label">已提现</text>
|
||
<text class="revenue-value">¥{{ (statistics.withdrawn || 0).toFixed(2) }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 下方两张小卡片:总加好友数 / 总获客数 -->
|
||
<view class="acquisition-row">
|
||
<view class="acquisition-card">
|
||
<view class="acquisition-header-row">
|
||
<view class="acquisition-icon friends-icon">
|
||
<uni-icons type="plusempty" size="30" color="#4f46e5"></uni-icons>
|
||
</view>
|
||
<text class="acquisition-title-text">总加好友数</text>
|
||
</view>
|
||
<view class="acquisition-bottom-row">
|
||
<text class="acquisition-main-number">{{ statistics.totalFriends || 0 }}</text>
|
||
<text class="acquisition-main-sub">今日 {{ statistics.todayFriends || 0 }}</text>
|
||
</view>
|
||
<view class="acquisition-bg-circle friends-circle"></view>
|
||
</view>
|
||
|
||
<view class="acquisition-card">
|
||
<view class="acquisition-header-row">
|
||
<view class="acquisition-icon customer-icon">
|
||
<uni-icons type="person-filled" size="30" color="#a855f7"></uni-icons>
|
||
</view>
|
||
<text class="acquisition-title-text">总获客数</text>
|
||
</view>
|
||
<view class="acquisition-bottom-row">
|
||
<text class="acquisition-main-number">{{ statistics.totalCustomers || 0 }}</text>
|
||
<text class="acquisition-main-sub">今日 {{ statistics.todayCustomers || 0 }}</text>
|
||
</view>
|
||
<view class="acquisition-bg-circle customer-circle"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 明细记录(整体卡片) -->
|
||
<view class="records-section">
|
||
<view class="records-header">
|
||
<uni-icons type="list" size="20" color="#333"></uni-icons>
|
||
<text class="records-title">明细记录</text>
|
||
</view>
|
||
|
||
<!-- 标签页(胶囊样式) -->
|
||
<view class="tabs-container">
|
||
<view class="tabs-pill">
|
||
<view
|
||
class="tab-item"
|
||
:class="{'active': currentTab === 'revenue'}"
|
||
@click="switchTab('revenue')"
|
||
>
|
||
收益明细
|
||
</view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{'active': currentTab === 'withdraw'}"
|
||
@click="switchTab('withdraw')"
|
||
>
|
||
提现明细
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 筛选条件卡片(始终展开) -->
|
||
<view class="filter-section">
|
||
<view class="filter-card">
|
||
<view class="filter-header-row">
|
||
<view class="filter-header-left">
|
||
<uni-icons type="spinner" size="18" color="#6b7280"></uni-icons>
|
||
<text class="filter-title">筛选条件</text>
|
||
</view>
|
||
<!-- 预留图标位,可去掉展开/收起逻辑 -->
|
||
<uni-icons type="arrowdown" size="16" color="#d1d5db"></uni-icons>
|
||
</view>
|
||
<view class="filter-content">
|
||
<!-- 收益明细:类型 + 时间 -->
|
||
<template v-if="currentTab === 'revenue'">
|
||
<view class="filter-item">
|
||
<text class="filter-label">类型</text>
|
||
<picker mode="selector" :range="typeOptions" :value="selectedTypeIndex" @change="onTypeChange">
|
||
<view class="filter-picker">
|
||
<text>{{ typeOptions[selectedTypeIndex] }}</text>
|
||
<uni-icons type="arrowdown" size="14" color="#9ca3af"></uni-icons>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
<view class="filter-item">
|
||
<text class="filter-label">时间</text>
|
||
<picker mode="date" :value="selectedDate" @change="onDateChange">
|
||
<view class="filter-picker">
|
||
<text>{{ selectedDate || '选择日期' }}</text>
|
||
<uni-icons type="calendar" size="14" color="#9ca3af"></uni-icons>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
</template>
|
||
|
||
<!-- 提现明细:状态 + 到账方式 -->
|
||
<template v-else>
|
||
<view class="filter-item">
|
||
<text class="filter-label">状态</text>
|
||
<picker mode="selector" :range="statusOptions" :value="selectedStatusIndex" @change="onStatusChange">
|
||
<view class="filter-picker">
|
||
<text>{{ statusOptions[selectedStatusIndex] }}</text>
|
||
<uni-icons type="arrowdown" size="14" color="#9ca3af"></uni-icons>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
<view class="filter-item">
|
||
<text class="filter-label">到账方式</text>
|
||
<picker mode="selector" :range="payTypeOptions" :value="selectedPayTypeIndex" @change="onPayTypeChange">
|
||
<view class="filter-picker">
|
||
<text>{{ payTypeOptions[selectedPayTypeIndex] }}</text>
|
||
<uni-icons type="arrowdown" size="14" color="#9ca3af"></uni-icons>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 记录列表 -->
|
||
<view class="records-list">
|
||
<view class="records-count">
|
||
<text>共{{ totalRecords }}条记录</text>
|
||
<view class="records-count-pill">
|
||
<text class="current-count">
|
||
{{ Math.min(currentPage * pageSize, totalRecords) }} / {{ totalRecords }}条
|
||
</text>
|
||
</view>
|
||
</view>
|
||
<!-- 加载中状态 -->
|
||
<view v-if="loading && recordsList.length === 0" class="loading-state">
|
||
<uni-icons type="spinner-cycle" size="40" color="#6366f1"></uni-icons>
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
<!-- 空状态 -->
|
||
<view v-else-if="recordsList.length === 0 && !loading" class="empty-state">
|
||
<uni-icons type="info" size="40" color="#ccc"></uni-icons>
|
||
<text class="empty-text">暂无记录</text>
|
||
</view>
|
||
<!-- 记录列表 -->
|
||
<view v-else>
|
||
<!-- 收益明细样式 -->
|
||
<view v-if="currentTab === 'revenue'">
|
||
<view v-for="(item, index) in recordsList" :key="index" class="record-item">
|
||
<view class="record-main-row">
|
||
<view class="record-title-wrap">
|
||
<text class="record-name">{{ item.sourceType || '-' }}</text>
|
||
<view
|
||
class="record-badge"
|
||
:class="{'badge-acquisition': item.type === 'acquisition', 'badge-friend': item.type === 'friend'}"
|
||
>
|
||
{{ item.typeLabel || '' }}
|
||
</view>
|
||
</view>
|
||
<text class="record-amount" :class="{'amount-positive': item.amount > 0}">
|
||
{{ item.amount > 0 ? '+' : '' }}¥{{ item.amount.toFixed(2) }}
|
||
</text>
|
||
</view>
|
||
<view class="record-meta-row">
|
||
<view class="record-time-wrap">
|
||
<uni-icons type="time" size="16" color="#9ca3af"></uni-icons>
|
||
<text class="record-time">{{ item.createTime }}</text>
|
||
</view>
|
||
<text class="record-meta-type">收益</text>
|
||
</view>
|
||
<view v-if="item.remark" class="record-remark">
|
||
<text class="remark-label">备注:</text>
|
||
<text class="remark-value">{{ item.remark }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提现明细样式 -->
|
||
<view v-else>
|
||
<view v-for="(item, index) in recordsList" :key="index" class="record-item withdraw-record-item">
|
||
<view class="withdraw-main-row">
|
||
<view class="withdraw-main-left">
|
||
<text class="withdraw-title">
|
||
<text v-if="item.status === 'approved' && item.payTypeLabel">提现到 {{ item.payTypeLabel }}</text>
|
||
<text v-else>提现申请</text>
|
||
</text>
|
||
</view>
|
||
<text class="withdraw-amount">
|
||
¥{{ Number(item.amount || 0).toFixed(2) }}
|
||
</text>
|
||
</view>
|
||
|
||
<view class="withdraw-tags-row">
|
||
<view
|
||
class="withdraw-tag status-tag"
|
||
:class="'status-' + (item.status || '')"
|
||
>
|
||
{{ item.statusLabel || '状态未知' }}
|
||
</view>
|
||
<view v-if="item.payTypeLabel && item.status === 'approved'" class="withdraw-tag">
|
||
{{ item.payTypeLabel }}
|
||
</view>
|
||
<view v-if="item.reviewer" class="withdraw-tag">
|
||
审核:{{ item.reviewer }}
|
||
</view>
|
||
</view>
|
||
|
||
<view class="withdraw-meta-row">
|
||
<view class="withdraw-meta-item">
|
||
<text class="meta-label">申请时间</text>
|
||
<text class="meta-value">{{ item.applyTime || '-' }}</text>
|
||
</view>
|
||
<view class="withdraw-meta-item">
|
||
<text class="meta-label">审核时间</text>
|
||
<text class="meta-value">{{ item.reviewTime || '-' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="item.remark" class="withdraw-remark">
|
||
<text class="meta-label">备注</text>
|
||
<text class="meta-value">{{ item.remark }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<view v-if="hasMore && recordsList.length > 0" class="load-more" @click="loadMore">
|
||
<text v-if="loading" class="load-more-text">加载中...</text>
|
||
<text v-else class="load-more-text">加载更多记录...</text>
|
||
</view>
|
||
<view v-else-if="!hasMore && recordsList.length > 0" class="load-more">
|
||
<text class="load-more-text no-more">没有更多了</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 修改密码弹框 -->
|
||
<uni-popup ref="changePasswordPopup" type="center" :mask-click="false">
|
||
<view class="password-popup">
|
||
<view class="password-popup-header">
|
||
<text class="password-popup-title">修改密码</text>
|
||
<text class="password-popup-subtitle">
|
||
请设置新密码,建议使用字母、数字组合,长度不少于6位
|
||
</text>
|
||
</view>
|
||
|
||
<!-- 表单区域 -->
|
||
<view class="password-popup-form">
|
||
<view class="password-form-item">
|
||
<view class="password-form-label">
|
||
<uni-icons type="locked" size="16" color="#6366f1"></uni-icons>
|
||
<text class="label-text">原密码</text>
|
||
</view>
|
||
<view class="password-input-wrapper">
|
||
<input
|
||
class="password-input"
|
||
type="password"
|
||
v-model="passwordForm.oldPassword"
|
||
placeholder="请输入原密码"
|
||
placeholder-class="password-placeholder"
|
||
maxlength="50"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="password-form-item">
|
||
<view class="password-form-label">
|
||
<uni-icons type="locked" size="16" color="#6366f1"></uni-icons>
|
||
<text class="label-text">新密码</text>
|
||
</view>
|
||
<view class="password-input-wrapper">
|
||
<input
|
||
class="password-input"
|
||
type="password"
|
||
v-model="passwordForm.newPassword"
|
||
placeholder="请输入新密码(至少6位)"
|
||
placeholder-class="password-placeholder"
|
||
maxlength="50"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="password-form-item">
|
||
<view class="password-form-label">
|
||
<uni-icons type="locked" size="16" color="#6366f1"></uni-icons>
|
||
<text class="label-text">确认新密码</text>
|
||
</view>
|
||
<view class="password-input-wrapper">
|
||
<input
|
||
class="password-input"
|
||
type="password"
|
||
v-model="passwordForm.confirmPassword"
|
||
placeholder="请再次输入新密码"
|
||
placeholder-class="password-placeholder"
|
||
maxlength="50"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提示信息 -->
|
||
<view class="password-popup-info">
|
||
<view class="password-info-row">
|
||
<view class="password-info-dot"></view>
|
||
<text class="password-info-text">
|
||
密码长度至少6位,建议使用字母、数字组合
|
||
</text>
|
||
</view>
|
||
<view class="password-info-row">
|
||
<view class="password-info-dot"></view>
|
||
<text class="password-info-text">
|
||
修改密码后需要重新登录
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="password-popup-footer">
|
||
<button class="password-popup-btn cancel" @click="closeChangePasswordPopup">取消</button>
|
||
<button class="password-popup-btn confirm" @click="confirmChangePassword" :disabled="passwordLoading">
|
||
{{ passwordLoading ? '修改中...' : '确认修改' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</uni-popup>
|
||
|
||
<!-- 提现申请弹框 -->
|
||
<uni-popup ref="withdrawPopup" type="center" :mask-click="false">
|
||
<view class="withdraw-popup">
|
||
<view class="withdraw-popup-header">
|
||
<text class="withdraw-popup-title">提交提现申请</text>
|
||
<text class="withdraw-popup-subtitle">
|
||
将提交提现申请,金额为可提现的全部余额,可根据需要调整金额
|
||
</text>
|
||
</view>
|
||
|
||
<!-- 金额卡片 -->
|
||
<view class="withdraw-popup-card">
|
||
<view class="withdraw-popup-card-top">
|
||
<view class="withdraw-popup-card-left">
|
||
<text class="card-main-label">可提现余额</text>
|
||
<text class="card-sub-label" @click="setFullWithdraw">全部提现</text>
|
||
</view>
|
||
<view class="withdraw-popup-amount-wrap">
|
||
<text class="withdraw-popup-amount-currency"></text>
|
||
<input
|
||
class="withdraw-popup-amount-input"
|
||
type="digit"
|
||
v-model="withdrawFormAmount"
|
||
placeholder="0.00"
|
||
placeholder-class="withdraw-popup-placeholder"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提示信息 -->
|
||
<view class="withdraw-popup-info">
|
||
<view class="withdraw-info-row">
|
||
<view class="withdraw-info-dot"></view>
|
||
<text class="withdraw-info-text">
|
||
申请提现金额:¥{{ Number(withdrawFormAmount || statistics.withdrawableAmount || 0).toFixed(2) }}
|
||
</text>
|
||
</view>
|
||
<view class="withdraw-info-row">
|
||
<view class="withdraw-info-dot"></view>
|
||
<text class="withdraw-info-text">
|
||
提现申请提交后,等待管理员审核
|
||
</text>
|
||
</view>
|
||
<view class="withdraw-info-row">
|
||
<view class="withdraw-info-dot"></view>
|
||
<text class="withdraw-info-text">
|
||
审核通过后将尽快打款
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="withdraw-popup-footer">
|
||
<button class="withdraw-popup-btn cancel" @click="closeWithdrawPopup">取消</button>
|
||
<button class="withdraw-popup-btn confirm" @click="confirmWithdraw" :disabled="statisticsLoading">
|
||
{{ statisticsLoading ? '处理中...' : '提交申请' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</uni-popup>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import config from '@/config';
|
||
import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue';
|
||
|
||
export default {
|
||
components: {
|
||
uniPopup
|
||
},
|
||
data() {
|
||
return {
|
||
token: '',
|
||
channelInfo: {
|
||
name: '',
|
||
code: ''
|
||
},
|
||
statistics: {
|
||
withdrawableAmount: 0,
|
||
totalRevenue: 0,
|
||
pendingReview: 0,
|
||
withdrawn: 0,
|
||
totalFriends: 0,
|
||
todayFriends: 0,
|
||
totalCustomers: 0,
|
||
todayCustomers: 0
|
||
},
|
||
currentTab: 'revenue', // revenue | withdraw
|
||
showFilter: false,
|
||
// 收益明细筛选
|
||
typeOptions: ['全部', '获客', '加好友'],
|
||
selectedTypeIndex: 0,
|
||
selectedDate: '',
|
||
// 提现明细筛选
|
||
statusOptions: ['全部', '审核中', '已通过', '已拒绝'],
|
||
selectedStatusIndex: 0,
|
||
payTypeOptions: ['全部', '微信', '支付宝', '银行卡'],
|
||
selectedPayTypeIndex: 0,
|
||
// 提现弹框
|
||
withdrawFormAmount: '',
|
||
// 修改密码弹框
|
||
passwordForm: {
|
||
oldPassword: '',
|
||
newPassword: '',
|
||
confirmPassword: ''
|
||
},
|
||
passwordLoading: false,
|
||
recordsList: [],
|
||
totalRecords: 0,
|
||
currentPage: 1,
|
||
pageSize: 10,
|
||
hasMore: true,
|
||
loading: false,
|
||
statisticsLoading: false
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
// 令牌校验
|
||
this.token = uni.getStorageSync('channelToken');
|
||
if (!this.token) {
|
||
uni.showToast({ title: '请先登录', icon: 'none', duration: 2000 });
|
||
setTimeout(() => {
|
||
uni.redirectTo({ url: '/pages/channel/login' });
|
||
}, 1500);
|
||
return;
|
||
}
|
||
|
||
// 从本地存储或URL参数获取渠道信息
|
||
let storedCode = uni.getStorageSync('channelCode');
|
||
let storedName = uni.getStorageSync('channelName');
|
||
|
||
console.log('页面加载 - 从本地存储获取:', { storedCode, storedName });
|
||
|
||
// 如果单独保存的channelCode为空,尝试从完整登录数据中恢复
|
||
if (!storedCode) {
|
||
try {
|
||
const loginDataStr = uni.getStorageSync('channelLoginData');
|
||
if (loginDataStr) {
|
||
const loginData = JSON.parse(loginDataStr);
|
||
console.log('从完整登录数据恢复:', loginData);
|
||
|
||
const channelInfo = loginData.channelInfo || {};
|
||
if (channelInfo.channelCode) {
|
||
storedCode = channelInfo.channelCode;
|
||
uni.setStorageSync('channelCode', storedCode);
|
||
console.log('从完整登录数据恢复channelCode:', storedCode);
|
||
}
|
||
if (channelInfo.channelName) {
|
||
storedName = channelInfo.channelName;
|
||
uni.setStorageSync('channelName', storedName);
|
||
console.log('从完整登录数据恢复channelName:', storedName);
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('解析完整登录数据失败:', e);
|
||
}
|
||
}
|
||
|
||
if (storedCode) {
|
||
this.channelInfo.code = storedCode;
|
||
} else if (options.code) {
|
||
this.channelInfo.code = options.code;
|
||
}
|
||
|
||
if (storedName) {
|
||
this.channelInfo.name = storedName;
|
||
} else if (options.name) {
|
||
this.channelInfo.name = decodeURIComponent(options.name);
|
||
}
|
||
|
||
console.log('页面加载 - 当前channelInfo:', this.channelInfo);
|
||
|
||
uni.setNavigationBarTitle({
|
||
title: '分销获客统计'
|
||
});
|
||
|
||
this.loadData();
|
||
},
|
||
onPullDownRefresh() {
|
||
this.refreshData();
|
||
},
|
||
methods: {
|
||
/**
|
||
* 统一的渠道API请求方法
|
||
* @param {Object} options - 请求配置
|
||
* @returns {Promise} 请求Promise
|
||
*/
|
||
channelRequest(options) {
|
||
return new Promise((resolve, reject) => {
|
||
// 检查token
|
||
if (!this.token) {
|
||
reject(new Error('未登录'));
|
||
return;
|
||
}
|
||
|
||
// 检查channelCode
|
||
if (options.needChannelCode && !this.channelInfo.code) {
|
||
const storedCode = uni.getStorageSync('channelCode');
|
||
if (storedCode) {
|
||
this.channelInfo.code = storedCode;
|
||
} else {
|
||
reject(new Error('缺少渠道编码'));
|
||
return;
|
||
}
|
||
}
|
||
|
||
uni.request({
|
||
method: options.method || 'GET',
|
||
url: config.apiUrl2 + options.url,
|
||
data: options.data || {},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded',
|
||
Authorization: `Bearer ${this.token}`
|
||
},
|
||
success: (response) => {
|
||
// 统一处理响应
|
||
if (response.data.code === 10000 || response.data.code === 200) {
|
||
resolve(response.data);
|
||
} else {
|
||
// 处理业务错误
|
||
const errorMsg = response.data.message || response.data.msg || '请求失败';
|
||
// 如果是token失效,跳转登录
|
||
if (response.data.code === 401 || response.data.code === 10030) {
|
||
uni.showToast({
|
||
title: '登录已过期,请重新登录',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
setTimeout(() => {
|
||
uni.redirectTo({ url: '/pages/channel/login' });
|
||
}, 1500);
|
||
reject(new Error('登录已过期'));
|
||
} else {
|
||
reject(new Error(errorMsg));
|
||
}
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
console.error('请求失败:', error);
|
||
reject(error);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 统一错误处理
|
||
* @param {Error} error - 错误对象
|
||
* @param {String} defaultMsg - 默认错误消息
|
||
*/
|
||
handleError(error, defaultMsg = '操作失败,请重试') {
|
||
const errorMsg = error.message || error.errMsg || defaultMsg;
|
||
uni.showToast({
|
||
title: errorMsg,
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 获取渠道编码(带容错处理)
|
||
*/
|
||
getChannelCode() {
|
||
if (this.channelInfo.code) {
|
||
return this.channelInfo.code;
|
||
}
|
||
const storedCode = uni.getStorageSync('channelCode');
|
||
if (storedCode) {
|
||
this.channelInfo.code = storedCode;
|
||
return storedCode;
|
||
}
|
||
return null;
|
||
},
|
||
|
||
// 加载数据
|
||
async loadData() {
|
||
// 并行加载统计数据和记录列表
|
||
await Promise.all([
|
||
this.loadStatistics(),
|
||
this.loadRecords()
|
||
]);
|
||
},
|
||
|
||
// 加载统计数据
|
||
async loadStatistics() {
|
||
const channelCode = this.getChannelCode();
|
||
console.log('loadStatistics - 获取到的channelCode:', channelCode);
|
||
|
||
if (!channelCode) {
|
||
console.error('缺少channelCode,无法加载统计数据');
|
||
// 尝试从本地存储重新获取
|
||
const storedCode = uni.getStorageSync('channelCode');
|
||
console.log('loadStatistics - 从本地存储重新获取:', storedCode);
|
||
if (storedCode) {
|
||
this.channelInfo.code = storedCode;
|
||
// 递归调用一次
|
||
return this.loadStatistics();
|
||
}
|
||
this.handleError(new Error('缺少渠道编码,请重新登录'), '无法加载统计数据');
|
||
uni.stopPullDownRefresh();
|
||
return;
|
||
}
|
||
|
||
this.statisticsLoading = true;
|
||
console.log('loadStatistics - 开始请求接口,channelCode:', channelCode);
|
||
|
||
try {
|
||
const response = await this.channelRequest({
|
||
url: '/v1/frontend/distribution/user/home',
|
||
method: 'GET',
|
||
data: {
|
||
channelCode: channelCode
|
||
},
|
||
needChannelCode: false // 已经检查过了,不需要再次检查
|
||
});
|
||
|
||
const data = response.data || {};
|
||
const channelInfo = data.channelInfo || {};
|
||
const financialStats = data.financialStats || {};
|
||
const customerStats = data.customerStats || {};
|
||
|
||
console.log('loadStatistics - 接口返回数据:', {
|
||
data,
|
||
channelInfo,
|
||
financialStats,
|
||
customerStats
|
||
});
|
||
|
||
// 更新渠道信息 - 优先从channelInfo获取,兼容直接返回在data中的情况
|
||
const updatedChannelCode = channelInfo.channelCode || data.code || this.channelInfo.code;
|
||
const updatedChannelName = channelInfo.channelName || data.name || this.channelInfo.name;
|
||
|
||
console.log('loadStatistics - 更新后的渠道信息:', { updatedChannelCode, updatedChannelName });
|
||
|
||
this.channelInfo = {
|
||
name: updatedChannelName,
|
||
code: updatedChannelCode
|
||
};
|
||
|
||
// 更新本地存储
|
||
if (updatedChannelCode) {
|
||
uni.setStorageSync('channelCode', updatedChannelCode);
|
||
console.log('loadStatistics - 已保存channelCode到本地存储:', updatedChannelCode);
|
||
}
|
||
if (updatedChannelName) {
|
||
uni.setStorageSync('channelName', updatedChannelName);
|
||
console.log('loadStatistics - 已保存channelName到本地存储:', updatedChannelName);
|
||
}
|
||
|
||
// 更新统计数据 - 从financialStats和customerStats中获取
|
||
// 兼容旧的数据结构(直接返回在data中)
|
||
this.statistics = {
|
||
withdrawableAmount: parseFloat(financialStats.withdrawableAmount || data.withdrawableAmount || 0),
|
||
totalRevenue: parseFloat(financialStats.totalRevenue || data.totalRevenue || 0),
|
||
pendingReview: parseFloat(financialStats.pendingReview || data.pendingReview || 0),
|
||
withdrawn: parseFloat(financialStats.withdrawn || data.withdrawn || 0),
|
||
totalFriends: parseInt(customerStats.totalFriends || data.totalFriends || 0),
|
||
todayFriends: parseInt(customerStats.todayFriends || data.todayFriends || 0),
|
||
totalCustomers: parseInt(customerStats.totalCustomers || data.totalCustomers || 0),
|
||
todayCustomers: parseInt(customerStats.todayCustomers || data.todayCustomers || 0)
|
||
};
|
||
|
||
console.log('loadStatistics - 更新后的统计数据:', this.statistics);
|
||
} catch (error) {
|
||
console.error('加载统计数据失败:', error);
|
||
this.handleError(error, '加载统计数据失败');
|
||
} finally {
|
||
this.statisticsLoading = false;
|
||
// 如果是在下拉刷新时,且loadRecords还没完成,这里不停止刷新
|
||
// 让loadRecords完成后统一停止
|
||
}
|
||
},
|
||
|
||
// 加载记录列表
|
||
async loadRecords() {
|
||
if (this.loading) return;
|
||
|
||
const channelCode = this.getChannelCode();
|
||
if (!channelCode) {
|
||
console.error('缺少channelCode');
|
||
return;
|
||
}
|
||
|
||
this.loading = true;
|
||
|
||
try {
|
||
// 根据当前标签页选择不同的接口与筛选参数
|
||
const url = this.currentTab === 'revenue'
|
||
? '/v1/frontend/distribution/user/revenue-records'
|
||
: '/v1/frontend/distribution/user/withdrawal-records';
|
||
|
||
const requestData = {
|
||
channelCode: channelCode,
|
||
page: this.currentPage,
|
||
limit: this.pageSize
|
||
};
|
||
|
||
if (this.currentTab === 'revenue') {
|
||
requestData.filterType = this.selectedTypeIndex === 0 ? '' : this.typeOptions[this.selectedTypeIndex];
|
||
requestData.date = this.selectedDate;
|
||
} else {
|
||
// 状态映射:全部 / 审核中 / 已通过 / 已拒绝
|
||
const statusMap = ['all', 'pending', 'approved', 'rejected'];
|
||
const payTypeMap = ['all', 'wechat', 'alipay','bankcard'];
|
||
requestData.status = statusMap[this.selectedStatusIndex] || '';
|
||
requestData.payType = payTypeMap[this.selectedPayTypeIndex] || '';
|
||
}
|
||
|
||
const response = await this.channelRequest({
|
||
url,
|
||
method: 'GET',
|
||
data: requestData,
|
||
needChannelCode: true
|
||
});
|
||
|
||
const data = response.data || {};
|
||
const list = data.list || data.records || [];
|
||
|
||
if (this.currentPage === 1) {
|
||
this.recordsList = list;
|
||
} else {
|
||
this.recordsList = [...this.recordsList, ...list];
|
||
}
|
||
|
||
this.totalRecords = data.total || 0;
|
||
this.hasMore = this.recordsList.length < this.totalRecords;
|
||
} catch (error) {
|
||
this.handleError(error, '加载记录失败');
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
|
||
// 刷新数据
|
||
async refreshData() {
|
||
try {
|
||
// 重置状态
|
||
this.currentPage = 1;
|
||
this.hasMore = true;
|
||
this.recordsList = [];
|
||
this.loading = false;
|
||
this.statisticsLoading = false;
|
||
|
||
// 确保有channelCode
|
||
if (!this.channelInfo.code) {
|
||
const storedCode = uni.getStorageSync('channelCode');
|
||
if (storedCode) {
|
||
this.channelInfo.code = storedCode;
|
||
}
|
||
}
|
||
|
||
// 重新加载数据
|
||
await this.loadData();
|
||
} catch (error) {
|
||
console.error('刷新数据失败:', error);
|
||
} finally {
|
||
// 确保下拉刷新状态被停止
|
||
uni.stopPullDownRefresh();
|
||
}
|
||
},
|
||
|
||
// 切换标签页
|
||
switchTab(tab) {
|
||
if (this.currentTab === tab) return;
|
||
this.currentTab = tab;
|
||
this.currentPage = 1;
|
||
this.recordsList = [];
|
||
this.loadRecords();
|
||
},
|
||
|
||
// 切换筛选条件显示
|
||
toggleFilter() {
|
||
this.showFilter = !this.showFilter;
|
||
},
|
||
|
||
// 类型选择变化
|
||
onTypeChange(e) {
|
||
this.selectedTypeIndex = e.detail.value;
|
||
this.currentPage = 1;
|
||
this.recordsList = [];
|
||
this.loadRecords();
|
||
},
|
||
|
||
// 日期选择变化(收益明细)
|
||
onDateChange(e) {
|
||
this.selectedDate = e.detail.value;
|
||
this.currentPage = 1;
|
||
this.recordsList = [];
|
||
this.loadRecords();
|
||
},
|
||
|
||
// 状态选择变化(提现明细)
|
||
onStatusChange(e) {
|
||
this.selectedStatusIndex = e.detail.value;
|
||
this.currentPage = 1;
|
||
this.recordsList = [];
|
||
this.loadRecords();
|
||
},
|
||
|
||
// 到账方式选择变化(提现明细)
|
||
onPayTypeChange(e) {
|
||
this.selectedPayTypeIndex = e.detail.value;
|
||
this.currentPage = 1;
|
||
this.recordsList = [];
|
||
this.loadRecords();
|
||
},
|
||
|
||
// 加载更多
|
||
loadMore() {
|
||
if (this.hasMore && !this.loading) {
|
||
this.currentPage++;
|
||
this.loadRecords();
|
||
}
|
||
},
|
||
|
||
// 一键提现 - 打开弹框
|
||
handleWithdraw() {
|
||
if (this.statistics.withdrawableAmount <= 0) {
|
||
uni.showToast({
|
||
title: '暂无可提现金额',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
this.withdrawFormAmount = (this.statistics.withdrawableAmount || 0).toFixed(2);
|
||
this.$refs.withdrawPopup && this.$refs.withdrawPopup.open();
|
||
},
|
||
|
||
// 设置为全部提现
|
||
setFullWithdraw() {
|
||
this.withdrawFormAmount = (this.statistics.withdrawableAmount || 0).toFixed(2);
|
||
},
|
||
|
||
// 关闭提现弹框
|
||
closeWithdrawPopup() {
|
||
this.$refs.withdrawPopup && this.$refs.withdrawPopup.close();
|
||
},
|
||
|
||
// 确认提现
|
||
async confirmWithdraw() {
|
||
const maxAmount = Number(this.statistics.withdrawableAmount || 0);
|
||
let amount = Number(this.withdrawFormAmount);
|
||
|
||
if (isNaN(amount) || amount <= 0) {
|
||
uni.showToast({
|
||
title: '请输入正确的提现金额',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (amount > maxAmount) {
|
||
uni.showToast({
|
||
title: `最多可提现 ¥${maxAmount.toFixed(2)}`,
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
const channelCode = this.getChannelCode();
|
||
if (!channelCode) {
|
||
uni.showToast({
|
||
title: '缺少渠道编码',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
try {
|
||
this.statisticsLoading = true;
|
||
await this.channelRequest({
|
||
url: '/v1/distribution/withdrawals',
|
||
method: 'POST',
|
||
data: {
|
||
channelCode,
|
||
amount
|
||
},
|
||
needChannelCode: true
|
||
});
|
||
|
||
uni.showToast({
|
||
title: '提现申请已提交',
|
||
icon: 'success',
|
||
duration: 2000
|
||
});
|
||
|
||
this.closeWithdrawPopup();
|
||
|
||
// 刷新统计与记录
|
||
setTimeout(() => {
|
||
this.refreshData();
|
||
}, 1200);
|
||
} catch (error) {
|
||
this.handleError(error, '提现失败,请重试');
|
||
} finally {
|
||
this.statisticsLoading = false;
|
||
}
|
||
},
|
||
|
||
// 打开修改密码弹框
|
||
openChangePasswordPopup() {
|
||
// 重置表单
|
||
this.passwordForm = {
|
||
oldPassword: '',
|
||
newPassword: '',
|
||
confirmPassword: ''
|
||
};
|
||
this.$refs.changePasswordPopup && this.$refs.changePasswordPopup.open();
|
||
},
|
||
|
||
// 关闭修改密码弹框
|
||
closeChangePasswordPopup() {
|
||
this.$refs.changePasswordPopup && this.$refs.changePasswordPopup.close();
|
||
// 清空表单
|
||
this.passwordForm = {
|
||
oldPassword: '',
|
||
newPassword: '',
|
||
confirmPassword: ''
|
||
};
|
||
},
|
||
|
||
// 验证密码表单
|
||
validatePasswordForm() {
|
||
if (!this.passwordForm.oldPassword || !this.passwordForm.oldPassword.trim()) {
|
||
uni.showToast({
|
||
title: '请输入原密码',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return false;
|
||
}
|
||
|
||
if (!this.passwordForm.newPassword || !this.passwordForm.newPassword.trim()) {
|
||
uni.showToast({
|
||
title: '请输入新密码',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return false;
|
||
}
|
||
|
||
if (this.passwordForm.newPassword.length < 6) {
|
||
uni.showToast({
|
||
title: '新密码长度至少6位',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return false;
|
||
}
|
||
|
||
if (this.passwordForm.newPassword !== this.passwordForm.confirmPassword) {
|
||
uni.showToast({
|
||
title: '两次输入的新密码不一致',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return false;
|
||
}
|
||
|
||
if (this.passwordForm.oldPassword === this.passwordForm.newPassword) {
|
||
uni.showToast({
|
||
title: '新密码不能与原密码相同',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
},
|
||
|
||
// 确认修改密码
|
||
async confirmChangePassword() {
|
||
if (this.passwordLoading) return;
|
||
|
||
if (!this.validatePasswordForm()) {
|
||
return;
|
||
}
|
||
|
||
const channelCode = this.getChannelCode();
|
||
if (!channelCode) {
|
||
uni.showToast({
|
||
title: '缺少渠道编码',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
try {
|
||
this.passwordLoading = true;
|
||
|
||
// 准备请求参数
|
||
const requestData = {
|
||
channelCode: channelCode,
|
||
oldPassword: this.passwordForm.oldPassword.trim(),
|
||
newPassword: this.passwordForm.newPassword.trim()
|
||
};
|
||
|
||
console.log('修改密码请求参数:', {
|
||
url: '/v1/frontend/distribution/user/change-password',
|
||
data: {
|
||
...requestData,
|
||
oldPassword: '***', // 隐藏密码
|
||
newPassword: '***' // 隐藏密码
|
||
}
|
||
});
|
||
|
||
const response = await this.channelRequest({
|
||
url: '/v1/frontend/distribution/user/change-password',
|
||
method: 'POST',
|
||
data: requestData,
|
||
needChannelCode: true
|
||
});
|
||
|
||
console.log('修改密码接口响应:', response);
|
||
|
||
uni.showToast({
|
||
title: '密码修改成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
});
|
||
|
||
this.closeChangePasswordPopup();
|
||
|
||
// 提示重新登录
|
||
setTimeout(() => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '密码已修改,请重新登录',
|
||
showCancel: false,
|
||
confirmText: '确定',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 获取companyId(在清除之前获取)
|
||
const companyId = uni.getStorageSync('channelCompanyId') || '';
|
||
|
||
// 清除登录信息
|
||
uni.removeStorageSync('channelToken');
|
||
uni.removeStorageSync('channelCode');
|
||
uni.removeStorageSync('channelName');
|
||
uni.removeStorageSync('channelLoginData');
|
||
// 注意:不清除channelCompanyId,因为退出登录时需要用到
|
||
|
||
// 跳转到登录页,带上companyId
|
||
const loginUrl = companyId
|
||
? `/pages/channel/login?companyId=${companyId}`
|
||
: '/pages/channel/login';
|
||
|
||
uni.redirectTo({
|
||
url: loginUrl
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}, 1500);
|
||
} catch (error) {
|
||
console.error('修改密码失败:', error);
|
||
this.handleError(error, '修改密码失败,请重试');
|
||
} finally {
|
||
this.passwordLoading = false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page-container {
|
||
min-height: 100vh;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
/* 顶部蓝色头部 */
|
||
.header-section {
|
||
background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 60%, #1e40af 100%);
|
||
padding: 56rpx 30rpx 120rpx;
|
||
color: #fff;
|
||
border-bottom-left-radius: 40rpx;
|
||
border-bottom-right-radius: 40rpx;
|
||
}
|
||
|
||
.header-content {
|
||
position: relative;
|
||
}
|
||
|
||
.header-title-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.header-title-group {
|
||
flex: 1;
|
||
}
|
||
|
||
.header-title {
|
||
display: block;
|
||
font-size: 44rpx;
|
||
font-weight: 600;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.header-subtitle {
|
||
display: block;
|
||
font-size: 24rpx;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.settings-btn {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
padding: 0;
|
||
}
|
||
|
||
.settings-text {
|
||
font-size: 22rpx;
|
||
color: #fff;
|
||
}
|
||
|
||
.settings-circle {
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
border-radius: 50%;
|
||
background-color: #ffffff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.25);
|
||
}
|
||
|
||
.channel-info {
|
||
display: none;
|
||
}
|
||
|
||
.channel-row {
|
||
margin-top: 40rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.channel-left {
|
||
flex: 1;
|
||
}
|
||
|
||
.channel-label {
|
||
font-size: 24rpx;
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.channel-name {
|
||
display: block;
|
||
margin-top: 8rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.channel-code-pill {
|
||
margin-left: 16rpx;
|
||
padding: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
max-width: 360rpx;
|
||
}
|
||
|
||
.channel-code-label {
|
||
font-size: 22rpx;
|
||
opacity: 0.8;
|
||
margin-bottom: 4rpx;
|
||
}
|
||
|
||
.channel-code-text {
|
||
font-size: 22rpx;
|
||
font-weight: 500;
|
||
letter-spacing: 2rpx;
|
||
}
|
||
|
||
/* 统计卡片区域(贴近效果图) */
|
||
.stats-section {
|
||
padding: 0 30rpx 30rpx;
|
||
margin-top: -60rpx; /* 白卡片稍微压在蓝色圆角上 */
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
/* 顶部大统计卡片 */
|
||
.stats-main-card {
|
||
background: #ffffff;
|
||
border-radius: 32rpx;
|
||
padding: 32rpx 30rpx 28rpx;
|
||
box-shadow: 0 20rpx 40rpx rgba(15, 23, 42, 0.16);
|
||
}
|
||
|
||
.withdraw-header-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.withdraw-title-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.withdraw-icon-wrap {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 50%;
|
||
background: rgba(79, 70, 229, 0.1);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.withdraw-label {
|
||
font-size: 24rpx;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.withdraw-amount-row {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: flex-start;
|
||
margin: 24rpx 0 32rpx;
|
||
}
|
||
|
||
.withdraw-currency {
|
||
font-size: 60rpx;
|
||
color: #111827;
|
||
font-weight: 700;
|
||
margin-right: 4rpx;
|
||
}
|
||
|
||
.withdraw-amount-text {
|
||
font-size: 60rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
}
|
||
|
||
.withdraw-button-row {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.withdraw-button {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%);
|
||
border: none;
|
||
border-radius: 30rpx;
|
||
color: #fff;
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 12rpx;
|
||
&:disabled {
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
|
||
.withdraw-btn-text {
|
||
color: #fff;
|
||
}
|
||
|
||
.revenue-divider {
|
||
height: 1rpx;
|
||
background: #e5e7eb;
|
||
margin: 16rpx 0 22rpx;
|
||
}
|
||
|
||
.revenue-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.revenue-item {
|
||
flex: 1;
|
||
align-items: center;
|
||
text-align: center;
|
||
border-right: 1rpx solid #e5e7eb;
|
||
}
|
||
|
||
.revenue-label {
|
||
font-size: 22rpx;
|
||
color: #9ca3af;
|
||
margin-bottom: 6rpx;
|
||
display: block;
|
||
}
|
||
|
||
.revenue-value {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
.revenue-item:last-child {
|
||
border-right: none;
|
||
}
|
||
|
||
/* 底部两张统计卡片(总加好友数 / 总获客数) */
|
||
.acquisition-row {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.acquisition-card {
|
||
flex: 1;
|
||
background: #ffffff;
|
||
border-radius: 24rpx;
|
||
padding: 24rpx 22rpx 20rpx;
|
||
box-shadow: 0 12rpx 30rpx rgba(15, 23, 42, 0.08);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.acquisition-main-number {
|
||
font-size: 40rpx;
|
||
font-weight: 700;
|
||
color: #111827;
|
||
}
|
||
|
||
.acquisition-main-sub {
|
||
font-size: 22rpx;
|
||
color: #9ca3af;
|
||
margin-left: 12rpx;
|
||
}
|
||
|
||
.acquisition-header-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
margin-bottom: 26rpx;
|
||
}
|
||
|
||
.acquisition-icon {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
border-radius: 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.friends-icon {
|
||
background: #e5edff;
|
||
}
|
||
|
||
.customer-icon {
|
||
background: #f5e5ff;
|
||
}
|
||
|
||
.acquisition-title-text {
|
||
margin-left: 24rpx;
|
||
font-size: 24rpx;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.acquisition-bottom-row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.acquisition-bg-circle {
|
||
position: absolute;
|
||
right: -40rpx;
|
||
bottom: -40rpx;
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.friends-circle {
|
||
background: #eef3ff;
|
||
}
|
||
|
||
.customer-circle {
|
||
background: #f8edff;
|
||
}
|
||
|
||
/* 明细记录区域 */
|
||
.records-section {
|
||
background: transparent;
|
||
margin-top: 20rpx;
|
||
min-height: 60vh;
|
||
}
|
||
|
||
.records-section > view {
|
||
background-color: #ffffff;
|
||
border-radius: 24rpx;
|
||
padding: 26rpx 24rpx 30rpx;
|
||
box-shadow: 0 18rpx 36rpx rgba(15, 23, 42, 0.06);
|
||
}
|
||
|
||
.records-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.records-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
/* 标签页 - 胶囊 */
|
||
.tabs-container {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.tabs-pill {
|
||
display: flex;
|
||
background-color: #eef2ff;
|
||
border-radius: 999rpx;
|
||
padding: 6rpx;
|
||
}
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 18rpx 0;
|
||
font-size: 26rpx;
|
||
color: #6b7280;
|
||
border-radius: 999rpx;
|
||
|
||
&.active {
|
||
background-color: #ffffff;
|
||
color: #2563eb;
|
||
font-weight: 600;
|
||
box-shadow: 0 4rpx 12rpx rgba(37, 99, 235, 0.12);
|
||
}
|
||
}
|
||
|
||
/* 筛选条件卡片 */
|
||
.filter-section {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.filter-card {
|
||
background-color: #f7f9fc;
|
||
border-radius: 20rpx;
|
||
padding: 22rpx 24rpx 18rpx;
|
||
border: 1rpx solid #eef2ff;
|
||
}
|
||
|
||
.filter-header-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.filter-header-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
}
|
||
|
||
.filter-title {
|
||
font-size: 26rpx;
|
||
color: #4b5563;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.filter-content {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
gap: 24rpx;
|
||
margin-top: 22rpx;
|
||
}
|
||
|
||
.filter-item {
|
||
flex: 1;
|
||
}
|
||
|
||
.filter-label {
|
||
font-size: 26rpx;
|
||
color: #9ca3af;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.filter-picker {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
font-size: 26rpx;
|
||
color: #111827;
|
||
padding: 18rpx 22rpx;
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
border: 1rpx solid #eef2ff;
|
||
}
|
||
|
||
/* 记录列表 */
|
||
.records-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.records-count {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.records-count-pill {
|
||
padding: 6rpx 16rpx;
|
||
border-radius: 999rpx;
|
||
background-color: #e5e7eb;
|
||
}
|
||
|
||
.current-count {
|
||
color: #4b5563;
|
||
}
|
||
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 100rpx 0;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.record-item {
|
||
padding: 24rpx 0;
|
||
border-bottom: 1rpx solid #edf0f7;
|
||
}
|
||
|
||
.record-name {
|
||
font-size: 28rpx;
|
||
color: #111827;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.record-main-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.record-title-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
flex: 1;
|
||
}
|
||
|
||
.record-badge {
|
||
font-size: 20rpx;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 999rpx;
|
||
color: #4b5563;
|
||
background-color: #e5ecff;
|
||
}
|
||
|
||
.badge-friend {
|
||
background-color: #e5f7ff;
|
||
}
|
||
|
||
.record-amount {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
|
||
&.amount-positive {
|
||
color: #16a34a;
|
||
}
|
||
}
|
||
|
||
.record-meta-row {
|
||
margin-top: 12rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.record-time-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6rpx;
|
||
}
|
||
|
||
.record-time {
|
||
font-size: 24rpx;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.record-meta-type {
|
||
font-size: 24rpx;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.record-remark {
|
||
margin-top: 12rpx;
|
||
font-size: 22rpx;
|
||
color: #9ca3af;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.remark-label {
|
||
color: #9ca3af;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.remark-value {
|
||
color: #6b7280;
|
||
flex: 1;
|
||
}
|
||
|
||
/* 提现明细专用样式 */
|
||
.withdraw-record-item {
|
||
padding: 26rpx 0;
|
||
}
|
||
|
||
.withdraw-main-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.withdraw-main-left {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6rpx;
|
||
}
|
||
|
||
.withdraw-title {
|
||
font-size: 28rpx;
|
||
color: #111827;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.withdraw-id {
|
||
font-size: 22rpx;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.withdraw-amount {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #16a34a;
|
||
}
|
||
|
||
.withdraw-tags-row {
|
||
margin-top: 12rpx;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10rpx;
|
||
}
|
||
|
||
.withdraw-tag {
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 20rpx;
|
||
color: #4b5563;
|
||
background-color: #e5ecff;
|
||
}
|
||
|
||
.status-tag {
|
||
background-color: #dcfce7;
|
||
color: #15803d;
|
||
}
|
||
|
||
.status-pending {
|
||
background-color: #fef9c3;
|
||
color: #ca8a04;
|
||
}
|
||
|
||
.status-rejected {
|
||
background-color: #fee2e2;
|
||
color: #b91c1c;
|
||
}
|
||
|
||
.withdraw-meta-row {
|
||
margin-top: 12rpx;
|
||
display: flex;
|
||
gap: 32rpx;
|
||
}
|
||
|
||
.withdraw-meta-item {
|
||
font-size: 22rpx;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.withdraw-meta-item .meta-label {
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.withdraw-meta-item .meta-value {
|
||
margin-left: 4rpx;
|
||
color: #6b7280;
|
||
}
|
||
|
||
.withdraw-remark {
|
||
margin-top: 10rpx;
|
||
font-size: 22rpx;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
/* 提现弹框 */
|
||
.withdraw-popup {
|
||
width: 600rpx;
|
||
background-color: #ffffff;
|
||
border-radius: 24rpx;
|
||
padding: 32rpx 28rpx 26rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.withdraw-popup-header {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.withdraw-popup-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
.withdraw-popup-subtitle {
|
||
margin-top: 8rpx;
|
||
font-size: 24rpx;
|
||
color: #9ca3af;
|
||
display: block;
|
||
}
|
||
|
||
.withdraw-popup-placeholder {
|
||
color: #d1d5db;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
/* 金额卡片 */
|
||
.withdraw-popup-card {
|
||
margin-top: 20rpx;
|
||
padding: 24rpx 24rpx 22rpx;
|
||
border-radius: 20rpx;
|
||
background-color: #fff7ed;
|
||
border: 1rpx solid #fed7aa;
|
||
}
|
||
|
||
.withdraw-popup-card-top {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.withdraw-popup-card-left {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.card-main-label {
|
||
font-size: 26rpx;
|
||
color: #4b5563;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.card-sub-label {
|
||
font-size: 22rpx;
|
||
color: #f97316;
|
||
}
|
||
|
||
.withdraw-popup-amount-wrap {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: flex-end;
|
||
flex: 1;
|
||
}
|
||
|
||
.withdraw-popup-amount-currency {
|
||
font-size: 40rpx;
|
||
color: #f97316;
|
||
font-weight: 700;
|
||
margin-right: 0;
|
||
}
|
||
|
||
.withdraw-popup-amount-input {
|
||
min-width: 160rpx;
|
||
font-size: 40rpx;
|
||
font-weight: 700;
|
||
color: #f97316;
|
||
text-align: right;
|
||
margin-left: 4rpx;
|
||
}
|
||
|
||
/* 信息块 */
|
||
.withdraw-popup-info {
|
||
margin-top: 20rpx;
|
||
padding: 18rpx 20rpx;
|
||
border-radius: 16rpx;
|
||
background-color: #eef2ff;
|
||
}
|
||
|
||
.withdraw-info-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.withdraw-info-row:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.withdraw-info-dot {
|
||
width: 8rpx;
|
||
height: 8rpx;
|
||
border-radius: 50%;
|
||
background-color: #3b82f6;
|
||
}
|
||
|
||
.withdraw-info-text {
|
||
font-size: 24rpx;
|
||
color: #4b5563;
|
||
}
|
||
|
||
.withdraw-popup-footer {
|
||
margin-top: 26rpx;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.withdraw-popup-btn {
|
||
min-width: 180rpx;
|
||
height: 72rpx;
|
||
line-height: 72rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.withdraw-popup-btn.cancel {
|
||
background-color: #e5e7eb;
|
||
color: #374151;
|
||
}
|
||
|
||
.withdraw-popup-btn.confirm {
|
||
background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%);
|
||
color: #ffffff;
|
||
}
|
||
|
||
.load-more {
|
||
text-align: center;
|
||
padding: 30rpx 0;
|
||
color: #6366f1;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.load-more-text {
|
||
color: #6366f1;
|
||
|
||
&.no-more {
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.loading-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 100rpx 0;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* 修改密码弹框 */
|
||
.password-popup {
|
||
width: 600rpx;
|
||
background-color: #ffffff;
|
||
border-radius: 24rpx;
|
||
padding: 32rpx 28rpx 26rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.password-popup-header {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.password-popup-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
.password-popup-subtitle {
|
||
margin-top: 8rpx;
|
||
font-size: 24rpx;
|
||
color: #9ca3af;
|
||
display: block;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* 表单区域 */
|
||
.password-popup-form {
|
||
margin-top: 20rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.password-form-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.password-form-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.password-form-label .label-text {
|
||
font-size: 26rpx;
|
||
color: #4b5563;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.password-input-wrapper {
|
||
position: relative;
|
||
}
|
||
|
||
.password-input {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
padding: 0 24rpx;
|
||
font-size: 28rpx;
|
||
color: #111827;
|
||
background-color: #f9fafb;
|
||
border: 2rpx solid #e5e7eb;
|
||
border-radius: 12rpx;
|
||
box-sizing: border-box;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.password-input:focus {
|
||
border-color: #6366f1;
|
||
background-color: #fff;
|
||
box-shadow: 0 0 0 4rpx rgba(99, 102, 241, 0.1);
|
||
}
|
||
|
||
.password-placeholder {
|
||
color: #d1d5db;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
/* 提示信息 */
|
||
.password-popup-info {
|
||
margin-top: 20rpx;
|
||
padding: 18rpx 20rpx;
|
||
border-radius: 16rpx;
|
||
background-color: #eef2ff;
|
||
}
|
||
|
||
.password-info-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.password-info-row:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.password-info-dot {
|
||
width: 8rpx;
|
||
height: 8rpx;
|
||
border-radius: 50%;
|
||
background-color: #3b82f6;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.password-info-text {
|
||
font-size: 24rpx;
|
||
color: #4b5563;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.password-popup-footer {
|
||
margin-top: 26rpx;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.password-popup-btn {
|
||
min-width: 180rpx;
|
||
height: 72rpx;
|
||
line-height: 72rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
}
|
||
|
||
.password-popup-btn.cancel {
|
||
background-color: #e5e7eb;
|
||
color: #374151;
|
||
}
|
||
|
||
.password-popup-btn.confirm {
|
||
background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%);
|
||
color: #ffffff;
|
||
}
|
||
|
||
.password-popup-btn:disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
/* H5适配 */
|
||
/* #ifdef H5 */
|
||
.withdraw-button,
|
||
.settings-btn {
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover {
|
||
opacity: 0.9;
|
||
transform: translateY(-2rpx);
|
||
}
|
||
|
||
&:active {
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.password-popup-btn {
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover:not(:disabled) {
|
||
opacity: 0.9;
|
||
transform: translateY(-2rpx);
|
||
}
|
||
|
||
&:active:not(:disabled) {
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
/* #endif */
|
||
</style>
|
||
|