1300 行
33 KiB
Vue
1300 行
33 KiB
Vue
<template>
|
||
<view class="bill-records-page" :class="['theme-' + currentThemeKey]" :style="navStyle">
|
||
<!-- 顶部沉浸式自定义导航栏 -->
|
||
<view class="custom-nav-bar">
|
||
<view class="nav-content">
|
||
<view class="btn-nav-back btn-active" @click="handleBack">
|
||
<image class="nav-back-icon" :src="ICONS.chevronLeft" mode="aspectFit" />
|
||
</view>
|
||
<view class="nav-title-wrap">
|
||
<text class="nav-title">账单明细</text>
|
||
</view>
|
||
<!-- 胶囊右侧对称避让占位 -->
|
||
<view class="nav-capsule-placeholder"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 主滚动区域 -->
|
||
<scroll-view
|
||
scroll-y
|
||
class="records-scroll-view"
|
||
refresher-enabled
|
||
:refresher-triggered="isRefreshing"
|
||
@refresherrefresh="onRefresh"
|
||
>
|
||
<view class="records-content-body">
|
||
<!-- 1. 资产与收支统计总览卡片 -->
|
||
<view class="summary-stat-card">
|
||
<view class="card-bg-glow"></view>
|
||
<view class="summary-inner">
|
||
<view class="summary-title-row">
|
||
<view class="title-left">
|
||
<view class="wallet-icon-box">
|
||
<image class="wallet-icon-svg" :src="ICONS.wallet" mode="aspectFit" />
|
||
</view>
|
||
<view class="title-text-wrap">
|
||
<text class="summary-card-title">账户收支总览</text>
|
||
<text class="summary-card-sub">资金安全保障 · 实时明细核算</text>
|
||
</view>
|
||
</view>
|
||
<view class="header-actions">
|
||
<view class="btn-action-pill btn-active" @click="goToRecharge">
|
||
<text class="btn-action-txt">去充值</text>
|
||
</view>
|
||
<view class="btn-action-pill outline btn-active" @click="goToWithdrawRecords">
|
||
<text class="btn-action-txt">提现记录</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 核心数据栏 -->
|
||
<view class="balance-display-box">
|
||
<text class="balance-label">钱包当前可用余额</text>
|
||
<view class="balance-val-row">
|
||
<text class="currency-symbol">¥</text>
|
||
<text class="balance-val">{{ userBalance }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="summary-numbers-grid">
|
||
<view class="stat-col">
|
||
<text class="stat-num-val green">+¥{{ stats.totalIn }}</text>
|
||
<text class="stat-num-label">累计入账 (充值/退款)</text>
|
||
</view>
|
||
<view class="col-divider"></view>
|
||
<view class="stat-col">
|
||
<text class="stat-num-val red">-¥{{ stats.totalOut }}</text>
|
||
<text class="stat-num-label">累计支出 (订单/提现)</text>
|
||
</view>
|
||
<view class="col-divider"></view>
|
||
<view class="stat-col">
|
||
<text class="stat-num-val count">{{ allRecords.length }} 笔</text>
|
||
<text class="stat-num-label">流水总记录</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 2. 分类筛选选项卡 (Tabs) -->
|
||
<view class="filter-tabs-card">
|
||
<scroll-view scroll-x class="tabs-scroll" :show-scrollbar="false">
|
||
<view class="filter-tabs-row">
|
||
<view
|
||
v-for="tab in tabList"
|
||
:key="tab.key"
|
||
class="tab-item btn-active"
|
||
:class="{ active: currentTab === tab.key }"
|
||
@click="selectTab(tab.key)"
|
||
>
|
||
<text class="tab-label">{{ tab.label }}</text>
|
||
<text class="tab-badge" v-if="tab.count > 0">{{ tab.count }}</text>
|
||
<view class="tab-indicator" v-if="currentTab === tab.key"></view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 3. 加载骨架屏 -->
|
||
<view class="skeleton-wrap" v-if="isLoading">
|
||
<view class="skeleton-card" v-for="n in 3" :key="n"></view>
|
||
</view>
|
||
|
||
<!-- 4. 账单明细列表 -->
|
||
<view class="records-list-wrap" v-else-if="filteredRecords.length > 0">
|
||
<view
|
||
v-for="item in filteredRecords"
|
||
:key="item.id || item.recordNo"
|
||
class="record-card card-active"
|
||
>
|
||
<!-- 卡片头部: 流水单号与复制 -->
|
||
<view class="card-head-row">
|
||
<view class="order-no-wrap" @click="handleCopyOrderNo(item.recordNo)">
|
||
<text class="order-no-label">流水号:</text>
|
||
<text class="order-no-val">{{ item.recordNo || item.id }}</text>
|
||
<text class="copy-tag">复制</text>
|
||
</view>
|
||
<view class="type-badge" :class="item.direction === 'IN' ? 'in-badge' : 'out-badge'">
|
||
<text class="badge-txt">{{ item.direction === 'IN' ? '资金入账' : '资金出账' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 卡片主体: 图标、标题与金额 -->
|
||
<view class="card-main-row">
|
||
<view class="main-left">
|
||
<view class="type-icon-box" :class="getTypeIconClass(item.type)">
|
||
<image class="type-icon-svg" :src="getTypeIcon(item.type)" mode="aspectFit" />
|
||
</view>
|
||
<view class="title-col">
|
||
<text class="record-title">{{ item.type || '账户资金变动' }}</text>
|
||
<text class="record-party">{{ item.counterparty || '系统结算' }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="amount-col">
|
||
<text
|
||
class="amount-val"
|
||
:class="item.direction === 'IN' ? 'amount-in' : 'amount-out'"
|
||
>
|
||
{{ item.direction === 'IN' ? '+' : '-' }}¥{{ formatAmount(item.amount) }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 卡片底部: 时间与关联业务信息 -->
|
||
<view class="card-foot-row">
|
||
<view class="foot-time">
|
||
<image class="time-icon-svg" :src="ICONS.clock" mode="aspectFit" />
|
||
<text class="time-txt">{{ formatTime(item.createdAt) }}</text>
|
||
</view>
|
||
<view class="foot-related" v-if="item.relatedId">
|
||
<text class="related-txt">业务ID: {{ item.relatedId }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 列表触底提示 -->
|
||
<view class="list-end-tip">
|
||
<text class="tip-line"></text>
|
||
<text class="tip-txt">已展示全部收支明细</text>
|
||
<text class="tip-line"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 5. 暂无数据空状态 -->
|
||
<view class="empty-state-card" v-else>
|
||
<view class="empty-icon-wrap">
|
||
<image class="empty-icon-svg" :src="ICONS.fileText" mode="aspectFit" />
|
||
</view>
|
||
<text class="empty-title">暂无相关收支账单记录</text>
|
||
<text class="empty-sub">预约生活陪伴大神、参与同城组局或充值钱包后将在此自动生成明细</text>
|
||
<button class="btn-empty-action btn-active" @click="handleBack">返回个人中心</button>
|
||
</view>
|
||
|
||
<!-- 底部安全垫高 -->
|
||
<view class="bottom-spacer"></view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import { ICONS } from '@/common/icons.js';
|
||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||
import { getNavBarInfo } from '@/common/nav.js';
|
||
import { getUserFinanceRecords, getUserProfile } from '@/common/api.js';
|
||
|
||
const currentThemeKey = ref(getCurrentThemeKey());
|
||
const navInfo = ref(getNavBarInfo());
|
||
const isLoading = ref(true);
|
||
const isRefreshing = ref(false);
|
||
const allRecords = ref([]);
|
||
const currentTab = ref('ALL');
|
||
const userBalance = ref('0.00');
|
||
|
||
const stats = ref({
|
||
totalIn: '0.00',
|
||
totalOut: '0.00'
|
||
});
|
||
|
||
// 顶部导航栏安全距离避让与胶囊对称计算
|
||
const navStyle = computed(() => {
|
||
return {
|
||
'--safe-header-gap': `${navInfo.value.totalHeaderHeight}px`,
|
||
'--capsule-right-space': `${navInfo.value.capsuleRightSpace}px`,
|
||
'--capsule-height': `${navInfo.value.capsuleHeight}px`
|
||
};
|
||
});
|
||
|
||
// 计算分类筛选选项及计数
|
||
const tabList = computed(() => {
|
||
const records = allRecords.value;
|
||
const inCount = records.filter(r => r.direction === 'IN').length;
|
||
const outCount = records.filter(r => r.direction === 'OUT').length;
|
||
const orderCount = records.filter(r => (r.type && r.type.includes('订单')) || (r.relatedId && r.relatedId.startsWith('ord_'))).length;
|
||
const rechargeCount = records.filter(r => r.type && (r.type.includes('充值') || r.type.includes('报名'))).length;
|
||
|
||
return [
|
||
{ key: 'ALL', label: '全部', count: records.length },
|
||
{ key: 'IN', label: '收入/充值', count: inCount },
|
||
{ key: 'OUT', label: '支出/付款', count: outCount },
|
||
{ key: 'ORDER', label: '订单消费', count: orderCount },
|
||
{ key: 'RECHARGE', label: '充值与报名', count: rechargeCount }
|
||
];
|
||
});
|
||
|
||
// 根据当前选中的 Tab 过滤列表
|
||
const filteredRecords = computed(() => {
|
||
const tab = currentTab.value;
|
||
if (tab === 'IN') {
|
||
return allRecords.value.filter(r => r.direction === 'IN');
|
||
}
|
||
if (tab === 'OUT') {
|
||
return allRecords.value.filter(r => r.direction === 'OUT');
|
||
}
|
||
if (tab === 'ORDER') {
|
||
return allRecords.value.filter(r => (r.type && r.type.includes('订单')) || (r.relatedId && r.relatedId.startsWith('ord_')));
|
||
}
|
||
if (tab === 'RECHARGE') {
|
||
return allRecords.value.filter(r => r.type && (r.type.includes('充值') || r.type.includes('报名')));
|
||
}
|
||
return allRecords.value;
|
||
});
|
||
|
||
const selectTab = (tabKey) => {
|
||
currentTab.value = tabKey;
|
||
};
|
||
|
||
const handleBack = () => {
|
||
const pages = getCurrentPages();
|
||
if (pages && pages.length > 1) {
|
||
uni.navigateBack();
|
||
} else {
|
||
uni.switchTab({
|
||
url: '/pages/user/index'
|
||
});
|
||
}
|
||
};
|
||
|
||
const goToRecharge = () => {
|
||
uni.switchTab({
|
||
url: '/pages/user/index'
|
||
});
|
||
};
|
||
|
||
const goToWithdrawRecords = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/user/withdraw-records'
|
||
});
|
||
};
|
||
|
||
const handleCopyOrderNo = (orderNo) => {
|
||
if (!orderNo) return;
|
||
uni.setClipboardData({
|
||
data: String(orderNo),
|
||
success: () => {
|
||
uni.showToast({ title: '单号已复制', icon: 'none' });
|
||
}
|
||
});
|
||
};
|
||
|
||
const formatAmount = (val) => {
|
||
const n = Number(val || 0);
|
||
return isNaN(n) ? '0.00' : n.toFixed(2);
|
||
};
|
||
|
||
const formatTime = (timeStr) => {
|
||
if (!timeStr) return '刚刚';
|
||
const d = new Date(timeStr);
|
||
if (isNaN(d.getTime())) return String(timeStr);
|
||
const Y = d.getFullYear();
|
||
const M = String(d.getMonth() + 1).padStart(2, '0');
|
||
const D = String(d.getDate()).padStart(2, '0');
|
||
const h = String(d.getHours()).padStart(2, '0');
|
||
const m = String(d.getMinutes()).padStart(2, '0');
|
||
const s = String(d.getSeconds()).padStart(2, '0');
|
||
return `${Y}-${M}-${D} ${h}:${m}:${s}`;
|
||
};
|
||
|
||
const getTypeIcon = (type = '') => {
|
||
if (type.includes('充值') || type.includes('入账')) return ICONS.wallet;
|
||
if (type.includes('订单') || type.includes('付款')) return ICONS.ticket;
|
||
if (type.includes('提现')) return ICONS.wechat;
|
||
if (type.includes('佣金') || type.includes('分销')) return ICONS.chart;
|
||
return ICONS.fileText;
|
||
};
|
||
|
||
const getTypeIconClass = (type = '') => {
|
||
if (type.includes('充值') || type.includes('入账')) return 'icon-green';
|
||
if (type.includes('订单') || type.includes('付款')) return 'icon-indigo';
|
||
if (type.includes('提现')) return 'icon-orange';
|
||
if (type.includes('佣金') || type.includes('分销')) return 'icon-purple';
|
||
return 'icon-default';
|
||
};
|
||
|
||
const loadFinanceData = async () => {
|
||
const uid = uni.getStorageSync('userId') || 2006;
|
||
try {
|
||
const [profile, list] = await Promise.all([
|
||
getUserProfile(uid).catch(() => null),
|
||
getUserFinanceRecords(uid).catch(() => [])
|
||
]);
|
||
|
||
if (profile && profile.balance != null) {
|
||
userBalance.value = Number(profile.balance).toFixed(2);
|
||
}
|
||
|
||
if (Array.isArray(list)) {
|
||
allRecords.value = list;
|
||
let inSum = 0;
|
||
let outSum = 0;
|
||
list.forEach(r => {
|
||
const amt = Number(r.amount || 0);
|
||
if (r.direction === 'IN') inSum += amt;
|
||
if (r.direction === 'OUT') outSum += amt;
|
||
});
|
||
stats.value.totalIn = inSum.toFixed(2);
|
||
stats.value.totalOut = outSum.toFixed(2);
|
||
} else {
|
||
allRecords.value = [];
|
||
}
|
||
} catch (err) {
|
||
allRecords.value = [];
|
||
} finally {
|
||
isLoading.value = false;
|
||
isRefreshing.value = false;
|
||
}
|
||
};
|
||
|
||
const onRefresh = async () => {
|
||
isRefreshing.value = true;
|
||
await loadFinanceData();
|
||
};
|
||
|
||
onMounted(() => {
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI();
|
||
navInfo.value = getNavBarInfo();
|
||
loadFinanceData();
|
||
});
|
||
|
||
onShow(() => {
|
||
currentThemeKey.value = getCurrentThemeKey();
|
||
applyThemeToNativeUI();
|
||
navInfo.value = getNavBarInfo();
|
||
loadFinanceData();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.bill-records-page {
|
||
min-height: 100vh;
|
||
background-color: #0A0F1D;
|
||
color: #F8FAFC;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
/* 顶部沉浸式自定义导航栏 */
|
||
.custom-nav-bar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
height: var(--safe-header-gap);
|
||
background: rgba(10, 15, 29, 0.92);
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
|
||
.nav-content {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: var(--capsule-height);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 24rpx;
|
||
|
||
.btn-nav-back {
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(255, 255, 255, 0.08);
|
||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||
border-radius: 50%;
|
||
|
||
.nav-back-icon {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
}
|
||
}
|
||
|
||
.nav-title-wrap {
|
||
position: absolute;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
text-align: center;
|
||
|
||
.nav-title {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
letter-spacing: 1rpx;
|
||
}
|
||
}
|
||
|
||
.nav-capsule-placeholder {
|
||
width: var(--capsule-right-space);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 主滚动区域 */
|
||
.records-scroll-view {
|
||
flex: 1;
|
||
height: 100vh;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.records-content-body {
|
||
padding-top: calc(var(--safe-header-gap) + 20rpx);
|
||
padding-left: 24rpx;
|
||
padding-right: 24rpx;
|
||
padding-bottom: 60rpx;
|
||
}
|
||
|
||
/* 1. 资产与收支统计总览卡片 */
|
||
.summary-stat-card {
|
||
position: relative;
|
||
background: linear-gradient(135deg, rgba(30, 41, 59, 0.7) 0%, rgba(15, 23, 42, 0.8) 100%);
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
border-radius: 28rpx;
|
||
padding: 32rpx 28rpx;
|
||
margin-bottom: 24rpx;
|
||
overflow: hidden;
|
||
backdrop-filter: blur(16px);
|
||
-webkit-backdrop-filter: blur(16px);
|
||
box-shadow: 0 12rpx 36rpx rgba(0, 0, 0, 0.35);
|
||
|
||
.card-bg-glow {
|
||
position: absolute;
|
||
top: -60rpx;
|
||
right: -60rpx;
|
||
width: 240rpx;
|
||
height: 240rpx;
|
||
background: radial-gradient(circle, rgba(99, 102, 241, 0.25) 0%, transparent 70%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.summary-inner {
|
||
position: relative;
|
||
z-index: 1;
|
||
|
||
.summary-title-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 28rpx;
|
||
|
||
.title-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
|
||
.wallet-icon-box {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
border-radius: 20rpx;
|
||
background: rgba(99, 102, 241, 0.15);
|
||
border: 1px solid rgba(99, 102, 241, 0.3);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.wallet-icon-svg {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
}
|
||
}
|
||
|
||
.title-text-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.summary-card-title {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
}
|
||
|
||
.summary-card-sub {
|
||
font-size: 20rpx;
|
||
color: #94A3B8;
|
||
margin-top: 4rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
|
||
.btn-action-pill {
|
||
padding: 10rpx 20rpx;
|
||
border-radius: 24rpx;
|
||
background: linear-gradient(135deg, #6366F1 0%, #4F46E5 100%);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.btn-action-txt {
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
&.outline {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||
|
||
.btn-action-txt {
|
||
color: #CBD5E1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.balance-display-box {
|
||
background: rgba(255, 255, 255, 0.03);
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
border-radius: 20rpx;
|
||
padding: 20rpx 24rpx;
|
||
margin-bottom: 24rpx;
|
||
|
||
.balance-label {
|
||
font-size: 22rpx;
|
||
color: #94A3B8;
|
||
display: block;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.balance-val-row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 6rpx;
|
||
|
||
.currency-symbol {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #6366F1;
|
||
}
|
||
|
||
.balance-val {
|
||
font-size: 52rpx;
|
||
font-weight: 800;
|
||
color: #F8FAFC;
|
||
letter-spacing: 1rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.summary-numbers-grid {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-around;
|
||
|
||
.stat-col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
|
||
.stat-num-val {
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
margin-bottom: 6rpx;
|
||
|
||
&.green {
|
||
color: #10B981;
|
||
}
|
||
|
||
&.red {
|
||
color: #EF4444;
|
||
}
|
||
|
||
&.count {
|
||
color: #F8FAFC;
|
||
}
|
||
}
|
||
|
||
.stat-num-label {
|
||
font-size: 20rpx;
|
||
color: #64748B;
|
||
}
|
||
}
|
||
|
||
.col-divider {
|
||
width: 1px;
|
||
height: 48rpx;
|
||
background: rgba(255, 255, 255, 0.08);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 2. 分类筛选选项卡 (Tabs) */
|
||
.filter-tabs-card {
|
||
background: rgba(30, 41, 59, 0.5);
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
border-radius: 20rpx;
|
||
padding: 8rpx;
|
||
margin-bottom: 24rpx;
|
||
|
||
.tabs-scroll {
|
||
width: 100%;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.filter-tabs-row {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
min-width: 100%;
|
||
|
||
.tab-item {
|
||
position: relative;
|
||
flex: 1;
|
||
padding: 16rpx 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
border-radius: 14rpx;
|
||
transition: all 0.25s ease;
|
||
|
||
.tab-label {
|
||
font-size: 26rpx;
|
||
color: #94A3B8;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.tab-badge {
|
||
font-size: 18rpx;
|
||
color: #CBD5E1;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
padding: 2rpx 10rpx;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
&.active {
|
||
background: rgba(99, 102, 241, 0.2);
|
||
|
||
.tab-label {
|
||
color: #818CF8;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.tab-badge {
|
||
background: #6366F1;
|
||
color: #FFFFFF;
|
||
}
|
||
}
|
||
|
||
.tab-indicator {
|
||
position: absolute;
|
||
bottom: 2rpx;
|
||
width: 28rpx;
|
||
height: 4rpx;
|
||
background: #818CF8;
|
||
border-radius: 2rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 3. 骨架屏 */
|
||
.skeleton-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
|
||
.skeleton-card {
|
||
height: 180rpx;
|
||
border-radius: 20rpx;
|
||
background: rgba(255, 255, 255, 0.04);
|
||
animation: pulse 1.5s infinite ease-in-out;
|
||
}
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 0.4; }
|
||
50% { opacity: 0.8; }
|
||
}
|
||
|
||
/* 4. 账单明细列表 */
|
||
.records-list-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
|
||
.record-card {
|
||
background: rgba(30, 41, 59, 0.6);
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
border-radius: 24rpx;
|
||
padding: 24rpx 28rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.2);
|
||
transition: transform 0.2s ease, background 0.2s ease;
|
||
|
||
.card-head-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 20rpx;
|
||
padding-bottom: 16rpx;
|
||
border-bottom: 1px dashed rgba(255, 255, 255, 0.08);
|
||
|
||
.order-no-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6rpx;
|
||
|
||
.order-no-label {
|
||
font-size: 22rpx;
|
||
color: #64748B;
|
||
}
|
||
|
||
.order-no-val {
|
||
font-size: 22rpx;
|
||
color: #94A3B8;
|
||
font-family: monospace;
|
||
}
|
||
|
||
.copy-tag {
|
||
font-size: 18rpx;
|
||
color: #818CF8;
|
||
background: rgba(99, 102, 241, 0.12);
|
||
padding: 2rpx 8rpx;
|
||
border-radius: 6rpx;
|
||
margin-left: 6rpx;
|
||
}
|
||
}
|
||
|
||
.type-badge {
|
||
padding: 4rpx 14rpx;
|
||
border-radius: 12rpx;
|
||
|
||
.badge-txt {
|
||
font-size: 20rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
&.in-badge {
|
||
background: rgba(16, 185, 129, 0.15);
|
||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||
.badge-txt {
|
||
color: #34D399;
|
||
}
|
||
}
|
||
|
||
&.out-badge {
|
||
background: rgba(239, 68, 68, 0.15);
|
||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||
.badge-txt {
|
||
color: #F87171;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.card-main-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 16rpx;
|
||
|
||
.main-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
|
||
.type-icon-box {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
border-radius: 18rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.type-icon-svg {
|
||
width: 38rpx;
|
||
height: 38rpx;
|
||
}
|
||
|
||
&.icon-green {
|
||
background: rgba(16, 185, 129, 0.15);
|
||
border: 1px solid rgba(16, 185, 129, 0.25);
|
||
}
|
||
|
||
&.icon-indigo {
|
||
background: rgba(99, 102, 241, 0.15);
|
||
border: 1px solid rgba(99, 102, 241, 0.25);
|
||
}
|
||
|
||
&.icon-orange {
|
||
background: rgba(245, 158, 11, 0.15);
|
||
border: 1px solid rgba(245, 158, 11, 0.25);
|
||
}
|
||
|
||
&.icon-purple {
|
||
background: rgba(168, 85, 247, 0.15);
|
||
border: 1px solid rgba(168, 85, 247, 0.25);
|
||
}
|
||
|
||
&.icon-default {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||
}
|
||
}
|
||
|
||
.title-col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.record-title {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.record-party {
|
||
font-size: 22rpx;
|
||
color: #94A3B8;
|
||
margin-top: 4rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.amount-col {
|
||
.amount-val {
|
||
font-size: 36rpx;
|
||
font-weight: 800;
|
||
letter-spacing: 0.5rpx;
|
||
|
||
&.amount-in {
|
||
color: #10B981;
|
||
}
|
||
|
||
&.amount-out {
|
||
color: #F87171;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.card-foot-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
font-size: 22rpx;
|
||
color: #64748B;
|
||
|
||
.foot-time {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
|
||
.time-icon-svg {
|
||
width: 24rpx;
|
||
height: 24rpx;
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.time-txt {
|
||
color: #64748B;
|
||
}
|
||
}
|
||
|
||
.foot-related {
|
||
.related-txt {
|
||
color: #475569;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.list-end-tip {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
padding: 30rpx 0 10rpx;
|
||
|
||
.tip-line {
|
||
width: 60rpx;
|
||
height: 1px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.tip-txt {
|
||
font-size: 22rpx;
|
||
color: #64748B;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 5. 空状态 */
|
||
.empty-state-card {
|
||
background: rgba(30, 41, 59, 0.4);
|
||
border: 1px dashed rgba(255, 255, 255, 0.1);
|
||
border-radius: 28rpx;
|
||
padding: 80rpx 40rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
margin-top: 20rpx;
|
||
|
||
.empty-icon-wrap {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.04);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 28rpx;
|
||
|
||
.empty-icon-svg {
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
opacity: 0.5;
|
||
}
|
||
}
|
||
|
||
.empty-title {
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #F8FAFC;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.empty-sub {
|
||
font-size: 24rpx;
|
||
color: #64748B;
|
||
line-height: 1.5;
|
||
margin-bottom: 40rpx;
|
||
max-width: 520rpx;
|
||
}
|
||
|
||
.btn-empty-action {
|
||
height: 84rpx;
|
||
line-height: 84rpx;
|
||
padding: 0 60rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #FFFFFF;
|
||
background: linear-gradient(135deg, #6366F1 0%, #4F46E5 100%);
|
||
border-radius: 42rpx;
|
||
box-shadow: 0 8rpx 28rpx rgba(99, 102, 241, 0.4);
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.bottom-spacer {
|
||
height: 40rpx;
|
||
}
|
||
|
||
/* 极客暗黑模式适配 (theme-cool) */
|
||
&.theme-cool {
|
||
background-color: #0A0F1D;
|
||
color: #F8FAFC;
|
||
}
|
||
|
||
/* 暖阳浅色模式适配 (theme-warm) */
|
||
&.theme-warm {
|
||
background-color: #FFFDF9;
|
||
color: #1C1917;
|
||
|
||
.custom-nav-bar {
|
||
background: rgba(255, 253, 249, 0.95);
|
||
border-bottom-color: rgba(234, 88, 12, 0.1);
|
||
|
||
.nav-content {
|
||
.btn-nav-back {
|
||
background: #FFF7ED;
|
||
border: 1px solid #FED7AA;
|
||
}
|
||
|
||
.nav-title-wrap .nav-title {
|
||
color: #1C1917;
|
||
}
|
||
}
|
||
}
|
||
|
||
.summary-stat-card {
|
||
background: linear-gradient(135deg, #FFF7ED 0%, #FFEDD5 100%);
|
||
border: 1px solid #FED7AA;
|
||
box-shadow: 0 8rpx 28rpx rgba(234, 88, 12, 0.08);
|
||
|
||
.card-bg-glow {
|
||
background: radial-gradient(circle, rgba(249, 115, 22, 0.2) 0%, transparent 70%);
|
||
}
|
||
|
||
.summary-inner {
|
||
.summary-title-row {
|
||
.title-left {
|
||
.wallet-icon-box {
|
||
background: #FFEDD5;
|
||
border-color: #FDBA74;
|
||
}
|
||
|
||
.title-text-wrap {
|
||
.summary-card-title {
|
||
color: #9A3412;
|
||
}
|
||
|
||
.summary-card-sub {
|
||
color: #C2410C;
|
||
}
|
||
}
|
||
}
|
||
|
||
.header-actions {
|
||
.btn-action-pill {
|
||
background: linear-gradient(135deg, #EA580C 0%, #C2410C 100%);
|
||
|
||
&.outline {
|
||
background: #FFFFFF;
|
||
border-color: #FDBA74;
|
||
|
||
.btn-action-txt {
|
||
color: #EA580C;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.balance-display-box {
|
||
background: #FFFFFF;
|
||
border-color: #FED7AA;
|
||
|
||
.balance-label {
|
||
color: #78716C;
|
||
}
|
||
|
||
.balance-val-row {
|
||
.currency-symbol {
|
||
color: #EA580C;
|
||
}
|
||
|
||
.balance-val {
|
||
color: #1C1917;
|
||
}
|
||
}
|
||
}
|
||
|
||
.summary-numbers-grid {
|
||
.stat-col {
|
||
.stat-num-val {
|
||
&.green {
|
||
color: #059669;
|
||
}
|
||
|
||
&.red {
|
||
color: #DC2626;
|
||
}
|
||
|
||
&.count {
|
||
color: #1C1917;
|
||
}
|
||
}
|
||
|
||
.stat-num-label {
|
||
color: #78716C;
|
||
}
|
||
}
|
||
|
||
.col-divider {
|
||
background: #FED7AA;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.filter-tabs-card {
|
||
background: #FFFFFF;
|
||
border-color: #FED7AA;
|
||
|
||
.filter-tabs-row {
|
||
.tab-item {
|
||
.tab-label {
|
||
color: #78716C;
|
||
}
|
||
|
||
.tab-badge {
|
||
background: #F5F5F4;
|
||
color: #78716C;
|
||
}
|
||
|
||
&.active {
|
||
background: #FFF7ED;
|
||
|
||
.tab-label {
|
||
color: #EA580C;
|
||
}
|
||
|
||
.tab-badge {
|
||
background: #EA580C;
|
||
color: #FFFFFF;
|
||
}
|
||
}
|
||
|
||
.tab-indicator {
|
||
background: #EA580C;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.records-list-wrap {
|
||
.record-card {
|
||
background: #FFFFFF;
|
||
border-color: #E2E8F0;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||
|
||
.card-head-row {
|
||
border-bottom-color: #F1F5F9;
|
||
|
||
.order-no-wrap {
|
||
.order-no-label {
|
||
color: #94A3B8;
|
||
}
|
||
|
||
.order-no-val {
|
||
color: #64748B;
|
||
}
|
||
|
||
.copy-tag {
|
||
color: #EA580C;
|
||
background: #FFF7ED;
|
||
}
|
||
}
|
||
|
||
.type-badge {
|
||
&.in-badge {
|
||
background: #ECFDF5;
|
||
border-color: #A7F3D0;
|
||
.badge-txt {
|
||
color: #059669;
|
||
}
|
||
}
|
||
|
||
&.out-badge {
|
||
background: #FEF2F2;
|
||
border-color: #FECACA;
|
||
.badge-txt {
|
||
color: #DC2626;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.card-main-row {
|
||
.main-left {
|
||
.type-icon-box {
|
||
&.icon-green {
|
||
background: #ECFDF5;
|
||
border-color: #A7F3D0;
|
||
}
|
||
|
||
&.icon-indigo {
|
||
background: #EEF2FF;
|
||
border-color: #C7D2FE;
|
||
}
|
||
|
||
&.icon-orange {
|
||
background: #FFFBEB;
|
||
border-color: #FDE68A;
|
||
}
|
||
|
||
&.icon-purple {
|
||
background: #FAF5FF;
|
||
border-color: #E9D5FF;
|
||
}
|
||
|
||
&.icon-default {
|
||
background: #F8FAFC;
|
||
border-color: #E2E8F0;
|
||
}
|
||
}
|
||
|
||
.title-col {
|
||
.record-title {
|
||
color: #0F172A;
|
||
}
|
||
|
||
.record-party {
|
||
color: #64748B;
|
||
}
|
||
}
|
||
}
|
||
|
||
.amount-col {
|
||
.amount-val {
|
||
&.amount-in {
|
||
color: #059669;
|
||
}
|
||
|
||
&.amount-out {
|
||
color: #DC2626;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.card-foot-row {
|
||
color: #94A3B8;
|
||
|
||
.foot-time .time-txt {
|
||
color: #94A3B8;
|
||
}
|
||
|
||
.foot-related .related-txt {
|
||
color: #CBD5E1;
|
||
}
|
||
}
|
||
}
|
||
|
||
.list-end-tip {
|
||
.tip-line {
|
||
background: #E2E8F0;
|
||
}
|
||
|
||
.tip-txt {
|
||
color: #94A3B8;
|
||
}
|
||
}
|
||
}
|
||
|
||
.empty-state-card {
|
||
background: #FFFFFF;
|
||
border-color: #E2E8F0;
|
||
|
||
.empty-icon-wrap {
|
||
background: #F8FAFC;
|
||
}
|
||
|
||
.empty-title {
|
||
color: #0F172A;
|
||
}
|
||
|
||
.empty-sub {
|
||
color: #64748B;
|
||
}
|
||
|
||
.btn-empty-action {
|
||
background: linear-gradient(135deg, #EA580C 0%, #C2410C 100%);
|
||
box-shadow: 0 8rpx 28rpx rgba(234, 88, 12, 0.3);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.btn-active:active {
|
||
transform: scale(0.96);
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.card-active:active {
|
||
transform: translateY(2rpx);
|
||
}
|
||
</style>
|