feat(uniapp): 对齐钱包余额页面黑金尊享卡片与收支明细卡片流
这个提交包含在:
@@ -0,0 +1,75 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, testName, detail = '') {
|
||||
if (condition) {
|
||||
console.log(` [PASS] ${testName}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(` [FAIL] ${testName} - ${detail}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("========================================================");
|
||||
console.log(" 钱包余额页面对齐黑金卡片与流水卡片流规格测试");
|
||||
console.log("========================================================\n");
|
||||
|
||||
// 1. 验证 money.vue 存在性
|
||||
const moneyVuePath = path.resolve(__dirname, '../uniapp/pages/user/wallet/money.vue');
|
||||
assert(fs.existsSync(moneyVuePath), 'money.vue 文件存在');
|
||||
const content = fs.readFileSync(moneyVuePath, 'utf-8');
|
||||
|
||||
// 2. 验证黑金尊享卡片结构
|
||||
assert(
|
||||
content.includes('asset-card') && content.includes('money-hero'),
|
||||
'包含黑金资产卡片与核心大字数结构',
|
||||
'未升级为 asset-card 结构'
|
||||
);
|
||||
|
||||
// 3. 验证余额与充值按钮
|
||||
assert(
|
||||
content.includes('userInfo.money') &&
|
||||
content.includes('/pages/pay/recharge') &&
|
||||
content.includes('充值'),
|
||||
'包含余额展示与充值跳转入口',
|
||||
'缺少余额绑定或充值入口'
|
||||
);
|
||||
|
||||
// 4. 验证金额显隐切换
|
||||
assert(
|
||||
content.includes('showMoney') &&
|
||||
(content.includes('cicon-eye') || content.includes('cicon-eye-off')),
|
||||
'支持余额明文/掩码显隐切换',
|
||||
'缺少显隐切换逻辑'
|
||||
);
|
||||
|
||||
// 5. 验证流水列表卡片流升级
|
||||
assert(
|
||||
content.includes('log-card'),
|
||||
'收支明细升级为独立 log-card 卡片流',
|
||||
'仍采用普通边框单行列表'
|
||||
);
|
||||
|
||||
// 6. 验证总收入与总支出统计
|
||||
assert(
|
||||
content.includes('pagination.income') && content.includes('pagination.expense'),
|
||||
'包含当前筛选周期总收入与总支出统计',
|
||||
'缺少收支统计'
|
||||
);
|
||||
|
||||
// 7. 移除失效的 wallet_card_bg.png 依赖
|
||||
assert(
|
||||
!content.includes('wallet_card_bg.png'),
|
||||
'移除对失效旧背景图 wallet_card_bg.png 的硬编码依赖',
|
||||
'仍依赖旧版失效背景图'
|
||||
);
|
||||
|
||||
console.log("\n--------------------------------------------------------");
|
||||
console.log(`测试执行结果: 通过 ${passed} 项, 失败 ${failed} 项`);
|
||||
console.log("========================================================");
|
||||
|
||||
process.exit(failed === 0 ? 0 : 1);
|
||||
@@ -1,356 +1,633 @@
|
||||
<template>
|
||||
<s-layout class="wallet-wrap" title="钱包">
|
||||
<!-- 钱包卡片 -->
|
||||
<view class="header-box ss-flex ss-row-center ss-col-center">
|
||||
<view class="card-box ui-BG-Main ui-Shadow-Main">
|
||||
<view class="card-head ss-flex ss-col-center">
|
||||
<view class="card-title ss-m-r-10">钱包余额(元)</view>
|
||||
<view
|
||||
@tap="state.showMoney = !state.showMoney"
|
||||
class="ss-eye-icon"
|
||||
:class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'"
|
||||
></view>
|
||||
</view>
|
||||
<view class="ss-flex ss-row-between ss-col-center ss-m-t-64">
|
||||
<view class="money-num">{{ state.showMoney ? userInfo.money : '*****' }}</view>
|
||||
<button class="ss-reset-button topup-btn" @tap="sheep.$router.go('/pages/pay/recharge')">
|
||||
充值
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<su-sticky>
|
||||
<!-- 统计 -->
|
||||
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
|
||||
<uni-datetime-picker
|
||||
v-model="state.data"
|
||||
type="daterange"
|
||||
@change="onChangeTime"
|
||||
:end="state.today"
|
||||
>
|
||||
<button class="ss-reset-button date-btn">
|
||||
<text>{{ dateFilterText }}</text>
|
||||
<text class="cicon-drop-down ss-seldate-icon"></text>
|
||||
</button>
|
||||
</uni-datetime-picker>
|
||||
|
||||
<view class="total-box">
|
||||
<view class="ss-m-b-10">总收入¥{{ state.pagination.income.toFixed(2) }}</view>
|
||||
<view>总支出¥{{ (-state.pagination.expense).toFixed(2) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<su-tabs
|
||||
:list="tabMaps"
|
||||
@change="onChange"
|
||||
:scrollable="false"
|
||||
:current="state.currentTab"
|
||||
></su-tabs>
|
||||
</su-sticky>
|
||||
<s-empty v-if="state.pagination.total === 0" text="暂无数据" icon="/static/data-empty.png" />
|
||||
<!-- 钱包记录 -->
|
||||
<view v-if="state.pagination.total > 0">
|
||||
<view
|
||||
class="wallet-list ss-flex border-bottom"
|
||||
v-for="item in state.pagination.data"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="list-content">
|
||||
<view class="title-box ss-flex ss-row-between ss-m-b-20">
|
||||
<text class="title ss-line-1"
|
||||
>{{ item.event_text }}{{ item.memo ? '-' + item.memo : '' }}</text
|
||||
>
|
||||
<view class="money">
|
||||
<text v-if="item.amount >= 0" class="add">+{{ item.amount }}</text>
|
||||
<text v-else class="minus">{{ item.amount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="time">{{ item.createtime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, watch, reactive } from 'vue';
|
||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
|
||||
const headerBg = sheep.$url.css('/assets/addons/shopro/uniapp/user/wallet_card_bg.png');
|
||||
|
||||
const pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
expense: 0,
|
||||
income: 0,
|
||||
};
|
||||
// 数据
|
||||
const state = reactive({
|
||||
showMoney: false,
|
||||
date: [],
|
||||
currentTab: 0,
|
||||
pagination,
|
||||
loadStatus: '',
|
||||
today: '',
|
||||
});
|
||||
|
||||
const tabMaps = [
|
||||
{
|
||||
name: '全部',
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
name: '收入',
|
||||
value: 'income',
|
||||
},
|
||||
{
|
||||
name: '支出',
|
||||
value: 'expense',
|
||||
},
|
||||
];
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
const dateFilterText = computed(() => {
|
||||
if (state.date[0] === state.date[1]) {
|
||||
return state.date[0];
|
||||
} else {
|
||||
return state.date.join('~');
|
||||
}
|
||||
});
|
||||
|
||||
async function getLogList(page = 1, list_rows = 8) {
|
||||
state.loadStatus = 'loading';
|
||||
let res = await sheep.$api.user.wallet.log({
|
||||
type: 'money',
|
||||
tab: tabMaps[state.currentTab].value,
|
||||
list_rows,
|
||||
page,
|
||||
date: appendTimeHMS(state.date),
|
||||
});
|
||||
if (res.code === 1) {
|
||||
let list = _.concat(state.pagination.data, res.data.list.data);
|
||||
state.pagination = {
|
||||
...res.data.list,
|
||||
data: list,
|
||||
income: res.data.income,
|
||||
expense: res.data.expense,
|
||||
};
|
||||
if (state.pagination.current_page < state.pagination.last_page) {
|
||||
state.loadStatus = 'more';
|
||||
} else {
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
}
|
||||
}
|
||||
onLoad(async (options) => {
|
||||
state.today = dayjs().format('YYYY-MM-DD');
|
||||
state.date = [state.today, state.today];
|
||||
getLogList();
|
||||
});
|
||||
|
||||
function onChange(e) {
|
||||
state.pagination = pagination;
|
||||
state.currentTab = e.index;
|
||||
getLogList();
|
||||
}
|
||||
|
||||
function onChangeTime(e) {
|
||||
state.date[0] = e[0];
|
||||
state.date[1] = e[e.length - 1];
|
||||
state.pagination = pagination;
|
||||
getLogList();
|
||||
}
|
||||
|
||||
function appendTimeHMS(arr) {
|
||||
return [arr[0] + ' 00:00:00', arr[1] + ' 23:59:59'];
|
||||
}
|
||||
|
||||
onReachBottom(() => {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getLogList(state.pagination.current_page + 1);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 钱包
|
||||
.header-box {
|
||||
background-color: $white;
|
||||
padding: 30rpx;
|
||||
|
||||
.card-box {
|
||||
width: 100%;
|
||||
min-height: 300rpx;
|
||||
padding: 40rpx;
|
||||
background-size: 100% 100%;
|
||||
border-radius: 30rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: v-bind(headerBg) no-repeat;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.card-head {
|
||||
color: $white;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.ss-eye-icon {
|
||||
font-size: 40rpx;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.money-num {
|
||||
font-size: 70rpx;
|
||||
line-height: 70rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
|
||||
.reduce-num {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.topup-btn {
|
||||
width: 120rpx;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
border-radius: 30px;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
background-color: $white;
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 筛选
|
||||
|
||||
.filter-box {
|
||||
height: 114rpx;
|
||||
background-color: $bg-page;
|
||||
|
||||
.total-box {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.date-btn {
|
||||
background-color: $white;
|
||||
line-height: 54rpx;
|
||||
border-radius: 27rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: $dark-6;
|
||||
|
||||
.ss-seldate-icon {
|
||||
font-size: 50rpx;
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-box {
|
||||
background: $white;
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
}
|
||||
|
||||
// tab
|
||||
.wallet-tab-card {
|
||||
.tab-item {
|
||||
height: 80rpx;
|
||||
position: relative;
|
||||
|
||||
.tab-title {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.cur-tab-title {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
.tab-line {
|
||||
width: 60rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 6rpx;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 2rpx;
|
||||
background-color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 钱包记录
|
||||
.wallet-list {
|
||||
padding: 30rpx;
|
||||
background-color: #ffff;
|
||||
|
||||
.head-img {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border-radius: 50%;
|
||||
background: $gray-c;
|
||||
}
|
||||
|
||||
.list-content {
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
flex: 1;
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
color: $dark-3;
|
||||
width: 400rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: $gray-c;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.money {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
font-family: OPPOSANS;
|
||||
.add {
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.minus {
|
||||
color: $dark-3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<s-layout class="money-wrap" title="钱包">
|
||||
<!-- 顶部黑金尊享资产卡片 -->
|
||||
<view class="header-box">
|
||||
<view class="asset-card">
|
||||
<!-- 卡片顶栏 -->
|
||||
<view class="card-top ss-flex ss-row-between ss-col-center">
|
||||
<view class="top-tag ss-flex ss-col-center">
|
||||
<text class="tag-badge">现金资产</text>
|
||||
<text class="top-title">账户钱包余额</text>
|
||||
</view>
|
||||
<view
|
||||
class="eye-btn ss-flex ss-col-center"
|
||||
@tap="state.showMoney = !state.showMoney"
|
||||
>
|
||||
<text class="eye-btn-text">{{ state.showMoney ? '隐藏金额' : '显示金额' }}</text>
|
||||
<text
|
||||
class="ss-m-l-6"
|
||||
:class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'"
|
||||
></text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 余额核心大字数 -->
|
||||
<view class="money-hero ss-flex ss-col-bottom ss-row-between">
|
||||
<view class="hero-left ss-flex ss-col-bottom">
|
||||
<text class="hero-symbol">¥</text>
|
||||
<text class="hero-num">{{ state.showMoney ? (userInfo.money || '0.00') : '*****' }}</text>
|
||||
<text class="hero-unit">元</text>
|
||||
</view>
|
||||
<button
|
||||
class="ss-reset-button topup-btn ss-flex ss-row-center ss-col-center"
|
||||
@tap="sheep.$router.go('/pages/pay/recharge')"
|
||||
>
|
||||
<text class="cicon-moneybag ss-m-r-6"></text>
|
||||
<text>充值</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 动态指示胶囊 -->
|
||||
<view class="rate-capsule ss-flex ss-row-between ss-col-center">
|
||||
<view class="capsule-left ss-flex ss-col-center">
|
||||
<text class="capsule-dot"></text>
|
||||
<text class="capsule-text">线上充值与消费余额 · 下单直接抵扣</text>
|
||||
</view>
|
||||
<text class="capsule-tag">即时可用</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部数据栏:当前周期总收入 / 总支出 -->
|
||||
<view class="card-footer ss-flex ss-row-between ss-col-center">
|
||||
<view class="footer-metric">
|
||||
<text class="metric-label">周期总收入</text>
|
||||
<view class="metric-val-wrap ss-flex ss-col-bottom">
|
||||
<text class="currency-symbol">¥</text>
|
||||
<text class="metric-val">{{ state.showMoney ? (state.pagination.income || 0).toFixed(2) : '***' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="footer-metric">
|
||||
<text class="metric-label">周期总支出</text>
|
||||
<view class="metric-val-wrap ss-flex ss-col-bottom">
|
||||
<text class="currency-symbol">¥</text>
|
||||
<text class="metric-val">{{ state.showMoney ? (-state.pagination.expense || 0).toFixed(2) : '***' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 吸顶筛选与 Tabs -->
|
||||
<su-sticky>
|
||||
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
|
||||
<uni-datetime-picker
|
||||
v-model="state.date"
|
||||
type="daterange"
|
||||
@change="onChangeTime"
|
||||
:end="state.today"
|
||||
>
|
||||
<button class="ss-reset-button date-btn ss-flex ss-col-center">
|
||||
<text class="cicon-calendar ss-m-r-8"></text>
|
||||
<text>{{ dateFilterText }}</text>
|
||||
<text class="cicon-drop-down ss-m-l-6"></text>
|
||||
</button>
|
||||
</uni-datetime-picker>
|
||||
|
||||
<view class="stats-summary ss-flex ss-col-center">
|
||||
<view class="stat-tag income ss-m-r-12">
|
||||
入 +¥{{ (state.pagination.income || 0).toFixed(2) }}
|
||||
</view>
|
||||
<view class="stat-tag expense">
|
||||
出 -¥{{ (-state.pagination.expense || 0).toFixed(2) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<su-tabs
|
||||
:list="tabMaps"
|
||||
@change="onChange"
|
||||
:scrollable="false"
|
||||
:current="state.currentTab"
|
||||
></su-tabs>
|
||||
</su-sticky>
|
||||
|
||||
<!-- 列表标题区 -->
|
||||
<view class="section-header ss-flex ss-row-between ss-col-center">
|
||||
<view class="section-title-wrap ss-flex ss-col-center">
|
||||
<view class="title-bar"></view>
|
||||
<text class="section-title">收支明细</text>
|
||||
</view>
|
||||
<text class="section-subtitle">实时流水入账</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-wrap" v-if="state.pagination.total === 0">
|
||||
<s-empty text="暂无收支明细" icon="/static/data-empty.png" />
|
||||
</view>
|
||||
|
||||
<!-- 流水卡片列表 -->
|
||||
<view class="list-container" v-if="state.pagination.total > 0">
|
||||
<view
|
||||
class="log-card"
|
||||
v-for="item in state.pagination.data"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="log-top ss-flex ss-row-between ss-col-center">
|
||||
<view class="log-title-box ss-flex ss-col-center">
|
||||
<view
|
||||
class="coin-icon ss-flex ss-row-center ss-col-center"
|
||||
:class="item.amount >= 0 ? 'income' : 'expense'"
|
||||
>
|
||||
<text :class="item.amount >= 0 ? 'cicon-wallet' : 'cicon-order'"></text>
|
||||
</view>
|
||||
<view class="log-main-info">
|
||||
<text class="log-title">{{ item.event_text }}{{ item.memo ? ' · ' + item.memo : '' }}</text>
|
||||
<text class="log-date">{{ item.createtime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="log-amount ss-flex ss-col-center">
|
||||
<text v-if="item.amount >= 0" class="amount-plus">+</text>
|
||||
<text class="amount-num" :class="item.amount >= 0 ? 'income-num' : 'expense-num'">
|
||||
{{ item.amount }}
|
||||
</text>
|
||||
<text class="amount-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more
|
||||
v-if="state.pagination.total > 0"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '上拉加载更多',
|
||||
}"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
|
||||
const pagination = {
|
||||
data: [],
|
||||
current_page: 1,
|
||||
total: 1,
|
||||
last_page: 1,
|
||||
expense: 0,
|
||||
income: 0,
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
showMoney: true,
|
||||
date: [],
|
||||
currentTab: 0,
|
||||
pagination,
|
||||
loadStatus: '',
|
||||
today: '',
|
||||
});
|
||||
|
||||
const tabMaps = [
|
||||
{
|
||||
name: '全部',
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
name: '收入',
|
||||
value: 'income',
|
||||
},
|
||||
{
|
||||
name: '支出',
|
||||
value: 'expense',
|
||||
},
|
||||
];
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo || {});
|
||||
|
||||
const dateFilterText = computed(() => {
|
||||
if (!state.date || state.date.length === 0) return '选择日期';
|
||||
if (state.date[0] === state.date[1]) {
|
||||
return state.date[0];
|
||||
} else {
|
||||
return `${state.date[0]} ~ ${state.date[1]}`;
|
||||
}
|
||||
});
|
||||
|
||||
async function getLogList(page = 1, list_rows = 10) {
|
||||
state.loadStatus = 'loading';
|
||||
try {
|
||||
const res = await sheep.$api.user.wallet.log({
|
||||
type: 'money',
|
||||
tab: tabMaps[state.currentTab].value,
|
||||
list_rows,
|
||||
page,
|
||||
date: appendTimeHMS(state.date),
|
||||
});
|
||||
if (res?.code === 1) {
|
||||
const list = page === 1 ? res.data.list.data : _.concat(state.pagination.data, res.data.list.data);
|
||||
state.pagination = {
|
||||
...res.data.list,
|
||||
data: list,
|
||||
income: res.data.income || 0,
|
||||
expense: res.data.expense || 0,
|
||||
};
|
||||
state.loadStatus = state.pagination.current_page < state.pagination.last_page ? 'more' : 'noMore';
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
|
||||
onLoad(async () => {
|
||||
state.today = dayjs().format('YYYY-MM-DD');
|
||||
state.date = [state.today, state.today];
|
||||
sheep.$store('user').getInfo();
|
||||
getLogList(1);
|
||||
});
|
||||
|
||||
onPullDownRefresh(async () => {
|
||||
await sheep.$store('user').getInfo();
|
||||
await getLogList(1);
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
|
||||
function onChange(e) {
|
||||
state.pagination = { ...pagination, data: [] };
|
||||
state.currentTab = e.index;
|
||||
getLogList(1);
|
||||
}
|
||||
|
||||
function onChangeTime(e) {
|
||||
state.date[0] = e[0];
|
||||
state.date[1] = e[e.length - 1];
|
||||
state.pagination = { ...pagination, data: [] };
|
||||
getLogList(1);
|
||||
}
|
||||
|
||||
function appendTimeHMS(arr) {
|
||||
if (!arr || arr.length < 2) return undefined;
|
||||
return [`${arr[0]} 00:00:00`, `${arr[1]} 23:59:59`];
|
||||
}
|
||||
|
||||
onReachBottom(() => {
|
||||
if (state.loadStatus !== 'noMore') {
|
||||
getLogList(state.pagination.current_page + 1);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.money-wrap {
|
||||
min-height: 100vh;
|
||||
background-color: #f7f8fa;
|
||||
}
|
||||
|
||||
/* 顶部资产区域 */
|
||||
.header-box {
|
||||
padding: 24rpx 30rpx 10rpx;
|
||||
|
||||
.asset-card {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 36rpx 32rpx 30rpx;
|
||||
border-radius: 28rpx;
|
||||
background: linear-gradient(138deg, #20222a 0%, #15161a 52%, #2c2419 100%);
|
||||
box-shadow: 0 16rpx 36rpx rgba(18, 19, 23, 0.18), inset 0 1rpx 0 rgba(247, 219, 168, 0.28);
|
||||
border: 1rpx solid rgba(235, 197, 131, 0.26);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: -60rpx;
|
||||
top: -60rpx;
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
background: radial-gradient(circle, rgba(235, 197, 131, 0.16) 0%, rgba(235, 197, 131, 0) 70%);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.card-top {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
.top-tag {
|
||||
.tag-badge {
|
||||
font-size: 20rpx;
|
||||
color: #2a2012;
|
||||
background: linear-gradient(90deg, #f7dfa6 0%, #e2be78 100%);
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 600;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.top-title {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-weight: 400;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.eye-btn {
|
||||
padding: 6rpx 18rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1rpx solid rgba(235, 197, 131, 0.3);
|
||||
border-radius: 24rpx;
|
||||
|
||||
.eye-btn-text {
|
||||
font-size: 22rpx;
|
||||
color: #f2cf8f;
|
||||
}
|
||||
|
||||
.cicon-eye,
|
||||
.cicon-eye-off {
|
||||
font-size: 24rpx;
|
||||
color: #f2cf8f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 核心数值展示 */
|
||||
.money-hero {
|
||||
margin-top: 24rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
.hero-left {
|
||||
.hero-symbol {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin-right: 6rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.hero-num {
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
line-height: 1.1;
|
||||
font-family: OPPOSANS, DIN, -apple-system, sans-serif;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.hero-unit {
|
||||
font-size: 26rpx;
|
||||
color: #f4d397;
|
||||
font-weight: 600;
|
||||
margin-left: 12rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.topup-btn {
|
||||
height: 56rpx;
|
||||
padding: 0 28rpx;
|
||||
background: linear-gradient(90deg, #f7dfa6 0%, #e2be78 100%);
|
||||
border-radius: 28rpx;
|
||||
color: #3b2b10;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 6rpx 16rpx rgba(226, 190, 120, 0.28);
|
||||
}
|
||||
}
|
||||
|
||||
/* 胶囊指示栏 */
|
||||
.rate-capsule {
|
||||
margin-top: 24rpx;
|
||||
background: rgba(244, 211, 151, 0.1);
|
||||
border: 1rpx solid rgba(244, 211, 151, 0.22);
|
||||
border-radius: 12rpx;
|
||||
padding: 14rpx 20rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
.capsule-left {
|
||||
.capsule-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #eabf76;
|
||||
margin-right: 12rpx;
|
||||
box-shadow: 0 0 8rpx #eabf76;
|
||||
}
|
||||
|
||||
.capsule-text {
|
||||
font-size: 24rpx;
|
||||
color: #fce8bf;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.capsule-tag {
|
||||
font-size: 20rpx;
|
||||
color: #f7d598;
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部收支汇总栏 */
|
||||
.card-footer {
|
||||
margin-top: 26rpx;
|
||||
padding-top: 22rpx;
|
||||
border-top: 1rpx solid rgba(255, 255, 255, 0.08);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
.footer-metric {
|
||||
flex: 1;
|
||||
|
||||
.metric-label {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.metric-val-wrap {
|
||||
.currency-symbol {
|
||||
font-size: 24rpx;
|
||||
color: #f4d397;
|
||||
font-weight: 600;
|
||||
margin-right: 2rpx;
|
||||
margin-bottom: 2rpx;
|
||||
}
|
||||
|
||||
.metric-val {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #f4d397;
|
||||
font-family: OPPOSANS, DIN, -apple-system, sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 筛选区域 */
|
||||
.filter-box {
|
||||
height: 104rpx;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1rpx solid #f1f2f5;
|
||||
|
||||
.date-btn {
|
||||
background-color: #f7f8fa;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
border-radius: 30rpx;
|
||||
padding: 0 24rpx;
|
||||
font-size: 24rpx;
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
|
||||
.cicon-calendar {
|
||||
font-size: 28rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.cicon-drop-down {
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
.stats-summary {
|
||||
.stat-tag {
|
||||
font-size: 22rpx;
|
||||
font-family: OPPOSANS;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 500;
|
||||
|
||||
&.income {
|
||||
background-color: #e6f9f0;
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
&.expense {
|
||||
background-color: #fef2f2;
|
||||
color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 列表标题区 */
|
||||
.section-header {
|
||||
padding: 30rpx 32rpx 16rpx;
|
||||
|
||||
.section-title-wrap {
|
||||
.title-bar {
|
||||
width: 6rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(180deg, #dfb775 0%, #ba8938 100%);
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #22252a;
|
||||
}
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
font-size: 22rpx;
|
||||
color: #999ea6;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-wrap {
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
|
||||
/* 卡片流列表 */
|
||||
.list-container {
|
||||
padding: 0 30rpx 20rpx;
|
||||
|
||||
.log-card {
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 26rpx 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(35, 42, 60, 0.04);
|
||||
border: 1rpx solid #f0f1f5;
|
||||
|
||||
.log-top {
|
||||
.log-title-box {
|
||||
.coin-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 18rpx;
|
||||
|
||||
&.income {
|
||||
background: linear-gradient(135deg, #e6f9f0 0%, #c9f4de 100%);
|
||||
.cicon-wallet {
|
||||
font-size: 32rpx;
|
||||
color: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
&.expense {
|
||||
background: linear-gradient(135deg, #fef2f2 0%, #fde2e2 100%);
|
||||
.cicon-order {
|
||||
font-size: 32rpx;
|
||||
color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.log-main-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.log-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #23272e;
|
||||
margin-bottom: 6rpx;
|
||||
max-width: 380rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.log-date {
|
||||
font-size: 22rpx;
|
||||
color: #9ea4af;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.log-amount {
|
||||
.amount-plus {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #b58028;
|
||||
margin-right: 2rpx;
|
||||
}
|
||||
|
||||
.amount-num {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
font-family: OPPOSANS, DIN, -apple-system, sans-serif;
|
||||
|
||||
&.income-num {
|
||||
color: #b58028;
|
||||
}
|
||||
|
||||
&.expense-num {
|
||||
color: #1f2937;
|
||||
}
|
||||
}
|
||||
|
||||
.amount-unit {
|
||||
font-size: 22rpx;
|
||||
color: #6b7280;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
在新工单中引用
屏蔽一个用户