Files
2026-01-24 17:45:54 +08:00

270 lines
7.1 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<s-layout title="我的账户" class="account-page">
<view class="page-wrap">
<!-- 顶部周期选择 -->
<view class="top-row">
<view class="period-select" @tap="showPicker = true">
<text class="period-label">{{ selectedLabel }}</text>
<up-icon name="arrow-down" color="#333" size="14"></up-icon>
</view>
</view>
<!-- 汇总卡片 -->
<view class="summary-card">
<text class="summary-amount">{{ formatSigned(totalAmount) }}</text>
<text class="summary-sub">预计收入</text>
<up-divider text=""></up-divider>
<text class="summary-note">{{ summaryNote }}</text>
</view>
<!-- 交易列表 -->
<scroll-view class="list" scroll-y>
<view class="txn-item" v-for="item in items" :key="item.id">
<view class="left" style="width:530rpx;">
<text class="txn-title">{{ item.desc }}</text>
<text class="txn-sub">{{ item.date }} {{ item.time }}</text>
</view>
<view class="right">
<text class="txn-amount" :class="{ positive: item.amount >= 0 }">
{{ formatSigned(item.amount) }}
</text>
<text class="txn-status">{{ item.statusText }}</text>
</view>
</view>
<view class="no-more">没有更多了~</view>
</scroll-view>
<!-- picker -->
<up-picker
:show="showPicker"
:columns="columns"
@confirm="onConfirm"
@cancel="showPicker = false"
@close="showPicker = false"
></up-picker>
</view>
</s-layout>
</template>
<script setup>
import { ref, computed } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import PayWalletApi from '@/sheep/api/pay/wallet';
// picker
const showPicker = ref(false);
const selectedLabel = ref('今日账单');
const columns = [
[
{ text: '今日账单', value: 'today' },
{ text: '昨日账单', value: 'yesterday' },
{ text: '本月账单', value: 'month' },
],
];
// 数据
const items = ref([]);
const totalAmount = ref(0);
const summaryNote = ref('');
// 本地回退示例(便于开发)
const testList = [
{ id: 't1', time: '18:02', date: '06-15', desc: '配送收入-#59-林记番薯粥(潮汕白粥,小炒,海鲜鱼)', amount: 6.1, statusText: '未到账' },
{ id: 't2', time: '17:49', date: '06-15', desc: '配送收入-#57-林记番薯粥(潮汕白粥,小炒,海鲜鱼)', amount: 5.3, statusText: '未到账' },
{ id: 't3', time: '17:42', date: '06-15', desc: '配送收入-#25-桐坑粿条(原汤猪肠·柠檬粿条)', amount: 4.6, statusText: '未到账' },
{ id: 't4', time: '17:27', date: '06-15', desc: '配送收入-#110-万辉超市(潮阳店)', amount: 3.6, statusText: '未到账' },
{ id: 't5', time: '16:56', date: '06-15', desc: '配送收入-#145-仓鼠便利超市(潮阳店)', amount: 3.1, statusText: '未到账' },
];
function formatSigned(val) {
const n = Number(val) || 0;
const sign = n > 0 ? '+' : n < 0 ? '' : '+';
return `${sign}${Math.abs(n).toFixed(2)}`;
}
function toDateParts(t) {
const d = t ? new Date(t) : null;
if (!d || isNaN(d.getTime())) return { date: '', time: '' };
const mm = String(d.getMonth() + 1).padStart(2, '0');
const dd = String(d.getDate()).padStart(2, '0');
const hh = String(d.getHours()).padStart(2, '0');
const mi = String(d.getMinutes()).padStart(2, '0');
return { date: `${mm}-${dd}`, time: `${hh}:${mi}` };
}
// 根据 picker value 计算时间范围(返回 start/end Date
function rangeFor(value) {
const now = new Date();
if (value === 'today') {
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59);
return [start, end];
}
if (value === 'yesterday') {
const y = new Date(now);
y.setDate(now.getDate() - 1);
const start = new Date(y.getFullYear(), y.getMonth(), y.getDate(), 0, 0, 0);
const end = new Date(y.getFullYear(), y.getMonth(), y.getDate(), 23, 59, 59);
return [start, end];
}
// month
const start = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0);
const end = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59);
return [start, end];
}
// 加载并过滤流水
async function load() {
try {
const res = await PayWalletApi.getWalletTransactionPage({ page: 1, size: 200 });
let records = [];
if (res && res.code === 0) {
if (Array.isArray(res.data)) {
records = res.data;
} else if (res.data && Array.isArray(res.data.records)) {
records = res.data.records;
}
}
const pickerValue = columns[0].find((c) => c.text === selectedLabel.value)?.value ?? 'today';
const [start, end] = rangeFor(pickerValue);
let filtered = [];
if (records && records.length) {
filtered = records.filter((r) => {
const t = r.createTime ?? r.createdAt ?? r.time ?? r.create_date ?? r.date;
if (!t) return false;
const d = new Date(t);
if (isNaN(d.getTime())) return false;
return d >= start && d <= end;
});
}
if (!filtered.length) {
// 使用本地回退示例
items.value = testList;
} else {
items.value = filtered.map((r, idx) => {
const t = r.createTime ?? r.createdAt ?? r.time ?? r.create_date ?? r.date;
const parts = toDateParts(t);
return {
id: r.id ?? `r-${idx}`,
date: parts.date,
time: parts.time,
desc: r.remark ?? r.note ?? r.title ?? r.typeName ?? r.description ?? '',
amount: Number(r.amount ?? r.price ?? r.income ?? r.value ?? r.money ?? 0),
statusText: r.statusText ?? r.stateText ?? r.payStatus ?? '',
};
});
}
} catch (e) {
// 回退示例
items.value = testList;
}
// 计算汇总
totalAmount.value = items.value.reduce((s, it) => s + (Number(it.amount) || 0), 0);
// 汇总说明(取前两条简短描述拼接)
summaryNote.value = items.value.slice(0, 2).map((i) => i.desc).join(' ');
}
function onConfirm(e) {
const v = e && e.value && e.value[0];
if (v) {
selectedLabel.value = v.text || String(v);
}
showPicker.value = false;
load();
}
onShow(() => {
load();
});
</script>
<style scoped>
.page-wrap {
padding: 12px;
background: transparent;
min-height: 100vh;
}
.top-row {
margin-bottom: 10px;
display: flex;
justify-content: flex-start;
}
.period-select {
display: flex;
align-items: center;
gap: 8px;
}
.period-label {
font-weight: 700;
}
.summary-card {
background: #fff;
padding: 16px;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
margin-bottom: 12px;
}
.summary-amount {
font-size: 20px;
font-weight: 700;
color: #e74c3c;
display: block;
margin-bottom: 6px;
}
.summary-sub {
color: #999;
margin-bottom: 8px;
display: block;
}
.summary-note {
color: #bbb;
font-size: 13px;
}
.list {
margin-top: 10px;
}
.txn-item {
background: #fff;
padding: 12px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #f0f0f0;
}
.txn-title {
font-weight: 600;
color: #333;
display: block;
margin-bottom: 6px;
}
.txn-sub {
color: #999;
font-size: 13px;
}
.txn-amount {
font-weight: 700;
color: #e74c3c;
text-align: right;
}
.txn-amount.positive {
color: #e74c3c;
}
.txn-status {
color: #999;
font-size: 12px;
margin-top: 6px;
display: block;
text-align: right;
}
.no-more {
text-align: center;
color: #bbb;
padding: 20px 0;
}
</style>