fjrcloud-community-app/pages/sub/income/index.vue

550 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!-- 收益公示页面 -->
<template>
<s-layout title="收益公示" :bgStyle="{ backgroundColor: '#EDDCD2' }" navbar="inner" color="#333333">
<view class="income-page">
<!-- 渐变背景装饰 -->
<view class="gradient-bg"></view>
<!-- 固定头部财务卡片 + Tab -->
<view class="page-header">
<!-- 财务总览卡片 -->
<view class="finance-card">
<!-- 背景图 -->
<image class="finance-bg" src="/static/img/jr-icon3.png" mode="aspectFill" />
<!-- 右上角标签 -->
<view class="corner-badge">公共财务</view>
<!-- 左侧内容区 -->
<view class="card-left">
<!-- 账单标题 -->
<view class="card-top">
<image class="bill-icon" src="/static/img/jr-icon2.png" mode="aspectFit" />
<text class="bill-title">2025年1月财务账单</text>
</view>
<!-- 余额 -->
<view class="balance-row">
<text class="balance-label">余额</text>
<text class="balance-value">{{ financeInfo.balance }}</text>
</view>
<!-- 收支统计 -->
<view class="stats-row">
<view class="stats-item">
<text class="stats-label">收入</text>
<text class="stats-value">{{ financeInfo.income }}</text>
</view>
<view class="stats-item">
<text class="stats-label">支出</text>
<text class="stats-value">{{ financeInfo.expense }}</text>
</view>
<view class="stats-item">
<text class="stats-label">未收</text>
<text class="stats-value">{{ financeInfo.unreceived }}</text>
</view>
</view>
</view>
</view>
<!-- 分类Tab栏 -->
<scroll-view class="tab-scroll" scroll-x :show-scrollbar="false">
<view class="tab-bar">
<view
class="tab-item"
:class="{ active: currentTab === tab.key }"
v-for="(tab, index) in tabList"
:key="index"
@tap="currentTab = tab.key"
>
<text class="tab-text">{{ tab.label }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- 明细列表 - 独立滚动区域 -->
<scroll-view class="income-scroll" scroll-y :style="{ height: scrollHeight + 'px' }">
<view class="income-list">
<!-- 分类项 -->
<view
class="category-block"
v-for="(item, index) in filteredIncomeList"
:key="item.id"
>
<!-- 分类标题行 -->
<view class="category-header" @tap="toggleExpand(item)">
<view class="header-left">
<image class="category-icon" src="/static/img/jr-icon3.png" mode="aspectFit" />
<text class="category-name">{{ item.name }}</text>
</view>
<view class="header-right">
<text class="category-total">{{ item.total }}</text>
<image
class="arrow-icon"
:class="{ 'arrow-up': item.expanded }"
src="/static/img/jr-icon4.png"
mode="aspectFit"
/>
</view>
</view>
<!-- 子项列表(展开时显示) -->
<view class="sub-list" v-if="item.expanded">
<view
class="sub-item"
v-for="(sub, subIndex) in item.children"
:key="subIndex"
>
<text class="sub-name">{{ sub.name }}</text>
<text class="sub-value">{{ sub.value }}</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="filteredIncomeList.length === 0">
<text class="empty-text"></text>
</view>
</view>
</scroll-view>
</view>
</s-layout>
</template>
<script setup>
import { ref, computed, onMounted, nextTick, getCurrentInstance } from 'vue';
// scroll-view 滚动高度
const scrollHeight = ref(0);
// 当前选中的分类Tab
const currentTab = ref('income');
// Tab列表
const tabList = ref([
{ key: 'income', label: '收入' },
{ key: 'expense', label: '支出' },
{ key: 'unreceived', label: '未收' }
]);
// 财务总览数据
const financeInfo = ref({
balance: '16,520.00',
income: '19,700.00',
expense: '3,180.00',
unreceived: '0.00'
});
// 收入明细数据
const incomeData = ref([
{
id: 1,
category: 'income',
name: '物业费',
total: '¥20000.50',
expanded: true,
children: [
{ name: '物业费', value: '¥10000.50' },
{ name: '物业费', value: '¥10000.00' }
]
},
{
id: 2,
category: 'income',
name: '水电公摊费',
total: '¥200.50',
expanded: false,
children: [
{ name: '水费', value: '¥100.50' },
{ name: '电费', value: '¥100.00' }
]
},
{
id: 3,
category: 'income',
name: '广告创收',
total: '¥200.50',
expanded: true,
children: [
{ name: '电梯广告', value: '¥200.50' },
{ name: '门闸广告', value: '¥200.50' },
{ name: '公区场地广告', value: '¥200.50' }
]
},
{
id: 4,
category: 'income',
name: '公共场地创收',
total: '¥200.50',
expanded: false,
children: [
{ name: '场地租赁', value: '¥200.50' }
]
},
{
id: 5,
category: 'income',
name: '停车收费',
total: '¥200.50',
expanded: false,
children: [
{ name: '月租停车', value: '¥200.50' }
]
}
]);
// 支出明细数据
const expenseData = ref([
{
id: 6,
category: 'expense',
name: '保洁费用',
total: '¥1,000.00',
expanded: false,
children: [
{ name: '日常保洁', value: '¥500.00' },
{ name: '垃圾清运', value: '¥500.00' }
]
},
{
id: 7,
category: 'expense',
name: '维修费用',
total: '¥1,500.00',
expanded: false,
children: [
{ name: '电梯维修', value: '¥1,000.00' },
{ name: '水管维修', value: '¥500.00' }
]
},
{
id: 8,
category: 'expense',
name: '绿化养护',
total: '¥680.00',
expanded: false,
children: [
{ name: '绿植采购', value: '¥380.00' },
{ name: '养护人工', value: '¥300.00' }
]
}
]);
// 未收明细数据
const unreceivedData = ref([]);
// 根据当前Tab筛选列表
const filteredIncomeList = computed(() => {
const allData = [...incomeData.value, ...expenseData.value, ...unreceivedData.value];
return allData.filter(item => item.category === currentTab.value);
});
// 展开/收起子项
function toggleExpand(item) {
item.expanded = !item.expanded;
}
// 渲染完成后计算scroll-view高度
onMounted(() => {
const instance = getCurrentInstance();
nextTick(() => {
const sysInfo = uni.getSystemInfoSync();
uni.createSelectorQuery()
.in(instance)
.select('.page-header')
.boundingClientRect((rect) => {
if (rect) {
scrollHeight.value = sysInfo.windowHeight - rect.height - rect.top;
}
})
.exec();
});
});
</script>
<style lang="scss" scoped>
/* ==================== 页面容器 ==================== */
.income-page {
position: relative;
min-height: 100vh;
}
/* ==================== 渐变背景 ==================== */
.gradient-bg {
position: absolute;
top: -176rpx;
left: 0;
right: 0;
height: calc(100% + 176rpx);
background: linear-gradient(180deg, #F8EDE8 0%, #FFFFFF 30%);
z-index: -1;
}
/* ==================== 固定头部区域 ==================== */
.page-header {
padding-bottom: 16rpx;
}
/* ==================== 财务总览卡片 ==================== */
.finance-card {
margin: 24rpx 32rpx 24rpx;
padding: 28rpx 0 24rpx 32rpx;
border-radius: 24rpx;
display: flex;
align-items: flex-start;
justify-content: space-between;
position: relative;
overflow: hidden;
min-height: 300rpx;
/* 背景图 */
.finance-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
/* 右上角标签 */
.corner-badge {
position: absolute;
top: 0;
right: 30rpx;
background: linear-gradient(135deg, #FA7E49 0%, #E86935 100%);
color: #FFFFFF;
font-size: 28rpx;
font-weight: 500;
padding: 12rpx 28rpx;
border-radius: 0 24rpx 0 24rpx;
z-index: 2;
}
/* 左侧内容区 */
.card-left {
flex: 1;
min-width: 0;
position: relative;
z-index: 1;
}
/* 卡片顶部:账单标题 */
.card-top {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.bill-icon {
width: 40rpx;
height: 40rpx;
margin-right: 12rpx;
flex-shrink: 0;
}
.bill-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
}
}
/* 余额行 - 纵向排列 */
.balance-row {
display: flex;
flex-direction: column;
margin-bottom: 20rpx;
.balance-label {
font-size: 26rpx;
color: #666666;
margin-bottom: 8rpx;
}
.balance-value {
font-size: 52rpx;
font-weight: 700;
color: #FA7E49;
}
}
/* 收支统计行 */
.stats-row {
display: flex;
justify-content: flex-start;
gap: 48rpx;
.stats-item {
display: flex;
flex-direction: column;
.stats-label {
font-size: 24rpx;
color: #666666;
margin-bottom: 8rpx;
}
.stats-value {
font-size: 28rpx;
font-weight: 600;
color: #333333;
}
}
}
}
/* ==================== 分类Tab栏 ==================== */
.tab-scroll {
white-space: nowrap;
margin: 0 0 8rpx;
}
.tab-bar {
display: inline-flex;
padding: 0 32rpx;
.tab-item {
padding: 20rpx 0;
margin-right: 40rpx;
position: relative;
flex-shrink: 0;
&:last-child {
margin-right: 0;
}
.tab-text {
font-size: 28rpx;
color: #999999;
transition: all 0.2s;
}
/* 选中状态 */
&.active {
.tab-text {
color: #FA7E49;
font-weight: 600;
}
/* 底部橙色指示条 */
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 48rpx;
height: 4rpx;
background: #FA7E49;
border-radius: 2rpx;
}
}
}
}
/* ==================== 明细列表滚动区域 ==================== */
.income-scroll {
width: 100%;
}
/* ==================== 明细列表内容 ==================== */
.income-list {
padding: 0 32rpx 160rpx;
}
/* ==================== 分类块 ==================== */
.category-block {
background-color: #FFFFFF;
border-radius: 16rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04);
}
/* ==================== 分类标题行 ==================== */
.category-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 24rpx;
.header-left {
display: flex;
align-items: center;
.category-icon {
width: 40rpx;
height: 40rpx;
margin-right: 16rpx;
flex-shrink: 0;
}
.category-name {
font-size: 30rpx;
font-weight: 500;
color: #333333;
}
}
.header-right {
display: flex;
align-items: center;
.category-total {
font-size: 30rpx;
font-weight: 600;
color: #FA7E49;
margin-right: 16rpx;
}
.arrow-icon {
width: 32rpx;
height: 32rpx;
transition: transform 0.3s;
&.arrow-up {
transform: rotate(180deg);
}
}
}
}
/* ==================== 子项列表 ==================== */
.sub-list {
padding: 0 24rpx 16rpx;
.sub-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 0;
border-top: 1rpx solid #F5F5F5;
.sub-name {
font-size: 26rpx;
color: #999999;
}
.sub-value {
font-size: 26rpx;
font-weight: 500;
color: #FA7E49;
}
}
}
/* ==================== 空状态 ==================== */
.empty-state {
display: flex;
align-items: center;
justify-content: center;
padding: 200rpx 0;
.empty-text {
font-size: 28rpx;
color: #999999;
}
}
</style>