feat: 新增部分页面静态页面

This commit is contained in:
admin
2026-01-24 17:45:54 +08:00
parent 849647d3c9
commit 8c1224999d
20 changed files with 1828 additions and 339 deletions

View File

@@ -0,0 +1,184 @@
<template>
<s-layout :title="titleText" class="salary-list-page">
<view class="page-wrap">
<scroll-view class="list" scroll-y>
<view class="salary-item" v-for="item in items" :key="item.id">
<view class="left">
<text class="date">{{ item.date }}</text>
<text class="desc" :class="{ 'muted': !item.desc }">{{ item.desc || '' }}</text>
</view>
<text
class="amount"
:class="{ positive: item.amount >= 0, negative: item.amount < 0 }"
>
{{ formatSigned(item.amount) }}
</text>
</view>
<view v-if="!items.length" class="empty">没有更多了~</view>
</scroll-view>
</view>
</s-layout>
</template>
<script setup>
import { ref } from 'vue';
import PayWalletApi from '@/sheep/api/pay/wallet';
import { onShow } from '@dcloudio/uni-app';
const items = ref([]);
const year = ref('');
const month = ref('');
const titleText = ref('薪资明细');
function pad(n) {
if (!n && n !== 0) return n;
return String(n).padStart(2, '0');
}
function formatSigned(val) {
const n = Number(val) || 0;
const sign = n > 0 ? '+' : n < 0 ? '' : '+';
// 保留两位小数
return `${sign}${Math.abs(n).toFixed(2)}`;
}
function toDateString(t) {
const d = t ? new Date(t) : null;
if (!d || isNaN(d.getTime())) return '';
const y = d.getFullYear();
const m = pad(d.getMonth() + 1);
const day = pad(d.getDate());
return `${y}-${m}-${day}`;
}
// 本地回退示例明细(供开发查看)
const testList = [
{ id: 't1', date: '2025-10-31', desc: '配送订单佣金收入23.9 时段考勤收入23.21', amount: 71.89 },
{ id: 't2', date: '2025-10-30', desc: '配送订单佣金收入23.9 时段考勤收入23.21', amount: 71.89 },
{ id: 't3', date: '2025-10-31', desc: '配送订单佣金收入23.9 时段考勤收入23.21', amount: 71.89 },
{ id: 't4', date: '2025-10-30', desc: '配送订单佣金收入23.9 时段考勤收入23.21', amount: 71.89 },
];
// 加载并过滤流水(按 year/month
async function loadList() {
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;
}
}
// 若有返回,则客户端过滤指定年月
if (records && records.length) {
const 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;
const y = d.getFullYear();
const m = d.getMonth() + 1;
return Number(y) === Number(year.value) && Number(m) === Number(month.value);
});
items.value = filtered.map((r, idx) => {
const t = r.createTime ?? r.createdAt ?? r.time ?? r.create_date ?? r.date;
return {
id: r.id ?? `r-${idx}`,
date: toDateString(t),
desc: r.remark ?? r.note ?? r.title ?? r.typeName ?? '',
amount: Number(r.amount ?? r.price ?? r.income ?? r.value ?? r.money ?? 0),
};
});
// 如果过滤后为空则回退到示例数据,便于开发查看
if (!items.value.length) {
items.value = testList;
}
return;
}
} catch (e) {
// ignore
}
// 无接口返回,使用回退示例
items.value = testList;
}
// 获取路由参数uni-app 页面可通过 getCurrentPages 读取 options
function initParams() {
try {
const pages = getCurrentPages();
const cur = pages[pages.length - 1] || {};
const opts = cur.options || {};
year.value = opts.year || opts.y || String(new Date().getFullYear());
month.value = opts.month || opts.m || String(new Date().getMonth() + 1);
titleText.value = `${String(year.value).slice(-2)}${pad(month.value)}月薪资`;
} catch (e) {
year.value = String(new Date().getFullYear());
month.value = String(new Date().getMonth() + 1);
titleText.value = `${String(year.value).slice(-2)}${pad(month.value)}月薪资`;
}
}
// 页面显示时初始化并加载
onShow(() => {
initParams();
loadList();
});
</script>
<style scoped>
.page-wrap {
padding: 16px;
background: transparent;
min-height: 100vh;
}
.list {
margin-top: 8px;
}
.salary-item {
background: #fff;
padding: 14px 12px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #f0f0f0;
}
.salary-item .left {
flex: 1;
}
.date {
color: #333;
font-weight: 600;
display: block;
margin-bottom: 6px;
}
.desc {
color: #999;
font-size: 13px;
display: block;
}
.desc.muted {
opacity: 0.8;
}
.amount {
width: 160rpx;
text-align: right;
font-weight: 700;
}
.amount.positive {
color: #e74c3c;
}
.amount.negative {
color: #2db7a3;
}
.empty {
text-align: center;
color: #bbb;
padding: 20px 0;
}
</style>

View File

@@ -0,0 +1,252 @@
<template>
<s-layout title="账户余额" class="record-list-page">
<view class="page-wrap">
<!-- 余额卡片 -->
<view class="balance-card">
<view class="balance-top">
<text class="symbol">¥</text>
<text class="balance">{{ formatBalance(balance) }}</text>
</view>
<view class="balance-sub" @tap="openDetail()">
本月已入账 <up-icon name="arrow-right" color="#949494" size="15"></up-icon>
</view>
</view>
<!-- 列表按年分组的薪资账单 -->
<scroll-view class="list" scroll-y>
<view v-for="section in sections" :key="section.year" class="year-section">
<view class="year-header">{{ section.year }}年已出账薪资账单</view>
<view
class="month-row"
v-for="item in section.items"
:key="section.year + '-' + item.month"
@tap="openDetail(section.year, item.month)"
>
<text class="month-label">{{ item.month }}月份薪资</text>
<view class="month-right">
<text class="month-amount">¥ {{ formatAmount(item.amount) }}</text>
<up-icon name="arrow-right" color="#c7c7c7" size="16"></up-icon>
</view>
</view>
</view>
<view class="no-more">没有更多了~</view>
</scroll-view>
</view>
</s-layout>
</template>
<script setup>
import { ref } from 'vue';
import PayWalletApi from '@/sheep/api/pay/wallet';
import { onShow } from '@dcloudio/uni-app';
const balance = ref(0);
const sections = ref([]);
// 格式化显示余额(保留两位小数)
function formatBalance(val) {
if (val == null) return '0.00';
return (Number(val) || 0).toFixed(2);
}
// 列表金额格式(示例中是带一位小数,这里为兼容保留一位或两位)
function formatAmount(val) {
if (val == null) return '0.0';
// 如果本身有小数位则保留一位,否则保留一位
return (Number(val) || 0).toFixed(1);
}
// 将流水按 年 -> 月 聚合(支持后端 records 数组record.createTime 字段)
function groupTransactions(records = []) {
const map = {}; // { year: { month: amount } }
records.forEach((r) => {
// 兼容不同字段名,尝试 createTime 或 createdAt 或 time
const t = r.createTime ?? r.createdAt ?? r.time ?? r.create_date ?? r.date;
let d = t ? new Date(t) : null;
if (!d || isNaN(d.getTime())) {
// 如果没有时间则跳过
return;
}
const year = d.getFullYear();
const month = d.getMonth() + 1;
// 取金额字段amount / price / income / value / money
const amt = Number(r.amount ?? r.price ?? r.income ?? r.value ?? r.money ?? 0) || 0;
map[year] = map[year] || {};
map[year][month] = (map[year][month] || 0) + amt;
});
// 转换为数组并排序(年降序,月降序)
const result = Object.keys(map)
.map((y) => {
const monthsObj = map[y];
const items = Object.keys(monthsObj)
.map((m) => ({
month: Number(m),
amount: monthsObj[m],
}))
.sort((a, b) => b.month - a.month);
return {
year: Number(y),
items,
};
})
.sort((a, b) => b.year - a.year);
return result;
}
async function loadWallet() {
try {
const res = await PayWalletApi.getPayWallet();
if (res && res.code === 0 && res.data) {
balance.value = res.data.balance ?? res.data?.wallet?.balance ?? 0;
return;
}
} catch (e) {
// ignore
}
// 回退占位数据
balance.value = 1999.91;
}
async function loadTransactions() {
try {
// 请求分页(取足够多条用于按月聚合)
const res = await PayWalletApi.getWalletTransactionPage({ page: 1, size: 200 });
// 兼容常见返回结构:{ code:0, data: { records: [] } } 或 { code:0, data: [] }
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;
}
}
if (records.length) {
sections.value = groupTransactions(records);
return;
}
} catch (e) {
// ignore
}
// 回退示例数据(与原型一致)
sections.value = [
{
year: 2025,
items: [
{ month: 5, amount: 4563.5 },
{ month: 4, amount: 4563.5 },
{ month: 3, amount: 4563.5 },
{ month: 2, amount: 4563.5 },
{ month: 1, amount: 4563.5 },
],
},
{
year: 2024,
items: [{ month: 12, amount: 4563.5 }],
},
];
}
function openDetail(year, month) {
// 占位跳转:可以替换为真实的工资明细页
uni.navigateTo({
url: `/pages/user/salary/salaryList?year=${year}&month=${month}`,
});
}
function openMonthSummary() {
// 占位跳转到本月流水汇总
uni.navigateTo({
url: '/pages/user/walletSummary',
});
}
onShow(() => {
loadWallet();
loadTransactions();
});
</script>
<style>
.page-wrap {
padding: 16px;
background: transparent;
min-height: 100vh;
}
.balance-card {
background: #fff;
border-radius: 8px;
padding: 30px 16px;
margin-bottom: 12px;
text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}
.balance-top {
display: flex;
justify-content: center;
align-items: baseline;
gap: 8px;
}
.symbol {
font-size: 28px;
color: #333;
margin-right: 6px;
}
.balance {
font-size: 40px;
font-weight: 700;
color: #333;
}
.balance-sub {
display: flex;
justify-content: center;
color: #999;
margin-top: 10px;
font-size: 14px;
}
.list {
margin-top: 8px;
}
.year-section {
margin-bottom: 12px;
}
.year-header {
background: #f5f5f5;
color: #666;
padding: 10px 12px;
font-weight: 600;
border-radius: 4px;
margin-bottom: 8px;
}
.month-row {
background: #fff;
padding: 14px 12px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #f0f0f0;
}
.month-label {
color: #333;
font-size: 15px;
}
.month-right {
display: flex;
align-items: center;
gap: 8px;
color: #999;
}
.month-amount {
color: #333;
font-weight: 600;
margin-right: 6px;
}
.no-more {
text-align: center;
color: #bbb;
padding: 20px 0;
}
</style>