588 lines
20 KiB
Vue
588 lines
20 KiB
Vue
<template>
|
||
<view class="page_padding">
|
||
<!-- 状态栏 -->
|
||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||
|
||
<!-- 导航栏 -->
|
||
<uni-nav-bar title="实收统计" backgroundColor="transparent" :border="false" left-icon="left" @click-left="goBack" />
|
||
|
||
<!-- 筛选条件 -->
|
||
<view class="filter-section">
|
||
<view class="filter-row">
|
||
<!-- 全部项目 -->
|
||
<view class="filter-item">
|
||
<picker mode="selector" :range="projectOptions" range-key="label" @change="onProjectChange">
|
||
<view class="filter-label">
|
||
<text>{{ selectedProjectLabel || '全部项目' }}</text>
|
||
<text class="filter-arrow">▾</text>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
|
||
<!-- 全部收费类型 -->
|
||
<view class="filter-item">
|
||
<picker mode="selector" :range="feeTypeOptions" range-key="label" @change="onFeeTypeChange">
|
||
<view class="filter-label">
|
||
<text>{{ selectedFeeTypeLabel || '全部收费类型' }}</text>
|
||
<text class="filter-arrow">▾</text>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 日期范围 -->
|
||
<view class="date-row">
|
||
<uni-datetime-picker
|
||
class="date-picker"
|
||
v-model="datetimerange"
|
||
type="daterange"
|
||
rangeSeparator="至"
|
||
placeholder="请选择日期区间"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 搜索 & 重置按钮 -->
|
||
<view class="btn-row">
|
||
<button class="reset-btn" @click="onReset">
|
||
重置
|
||
</button>
|
||
<button class="search-btn" @click="onSearch">
|
||
搜索
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 统计表格列表 -->
|
||
<scroll-view class="table-scroll" scroll-y show-scrollbar="false">
|
||
<!-- 循环渲染每个表格 -->
|
||
<view v-for="(table, index) in tableConfigs" :key="index" class="table-wrapper">
|
||
<!-- 标题独立于卡片外 -->
|
||
<view class="table-title">{{ table.title }}</view>
|
||
|
||
<!-- 卡片区域(仅包含表格内容) -->
|
||
<view class="table-card">
|
||
<!-- 表头 -->
|
||
<view class="table-header">
|
||
<text class="col-name">{{ table.colName }}</text>
|
||
<text class="col-amount">实际收款(元)</text>
|
||
<text class="col-discount">折扣(元)</text>
|
||
</view>
|
||
|
||
<!-- 暂无数据 -->
|
||
<view v-if="!table.data || table.data.length === 0" class="empty-row">
|
||
<text>暂无数据</text>
|
||
</view>
|
||
|
||
<!-- 数据行 -->
|
||
<template v-else>
|
||
<view class="table-row" v-for="item in table.data" :key="item.name">
|
||
<text class="col-name">{{ item.name }}</text>
|
||
<text class="col-amount">{{ item.amount.toFixed(2) }}</text>
|
||
<text class="col-discount">{{ item.discount.toFixed(2) }}</text>
|
||
</view>
|
||
|
||
<!-- 汇总行 -->
|
||
<view class="table-row total-row">
|
||
<text class="col-name">汇总</text>
|
||
<text class="col-amount">{{ table.total.amount.toFixed(2) }}</text>
|
||
<text class="col-discount">{{ table.total.discount.toFixed(2) }}</text>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部占位 -->
|
||
<view class="bottom-placeholder"></view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive } from 'vue'
|
||
import { onShow } from '@dcloudio/uni-app'
|
||
import { getVillageList,getFeeTypes,getTableList} from '../../api/api.js'
|
||
|
||
// ============ 系统信息 ============
|
||
const statusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight || 0)
|
||
|
||
// ============ 筛选数据 ============
|
||
// 项目选项(从接口获取)
|
||
const projectOptions = ref([])
|
||
const selectedProject = ref('')
|
||
const selectedProjectLabel = ref('全部项目')
|
||
|
||
// 收费类型选项(从接口获取)
|
||
const feeTypeOptions = ref([])
|
||
const selectedFeeType = ref('')
|
||
const selectedFeeTypeLabel = ref('全部收费类型')
|
||
|
||
// 日期范围
|
||
const datetimerange = ref([])
|
||
|
||
// ============ 表格数据 ============
|
||
const tableConfigs = reactive([
|
||
{
|
||
title: '项目总览',
|
||
colName: '项目名称',
|
||
data: [],
|
||
total: { amount: 0, discount: 0 }
|
||
},
|
||
{
|
||
title: '收费类型总览',
|
||
colName: '收费类型',
|
||
data: [],
|
||
total: { amount: 0, discount: 0 }
|
||
},
|
||
{
|
||
title: '房屋类型总览',
|
||
colName: '房屋类型',
|
||
data: [],
|
||
total: { amount: 0, discount: 0 }
|
||
},
|
||
])
|
||
|
||
// ============ 获取小区列表 ============
|
||
const fetchVillageList = () => {
|
||
getVillageList()
|
||
.then((res) => {
|
||
if (res.code === 200 && res.data) {
|
||
const list = res.data.map((item) => ({
|
||
value: String(item.id),
|
||
label: item.villageName
|
||
}))
|
||
projectOptions.value = [
|
||
{ value: '', label: '全部项目' },
|
||
...list
|
||
]
|
||
// 默认选中全部项目
|
||
selectedProject.value = ''
|
||
selectedProjectLabel.value = '全部项目'
|
||
}
|
||
})
|
||
.catch(() => {})
|
||
}
|
||
|
||
// ============ 获取收费类型列表 ============
|
||
const fetchFeeTypes = () => {
|
||
getFeeTypes()
|
||
.then((res) => {
|
||
if (res.code === 200 && res.data) {
|
||
const list = res.data.map((item) => ({
|
||
value: String(item.id),
|
||
label: item.name
|
||
}))
|
||
feeTypeOptions.value = [
|
||
{ value: '', label: '全部收费类型' },
|
||
...list
|
||
]
|
||
selectedFeeType.value = ''
|
||
selectedFeeTypeLabel.value = '全部收费类型'
|
||
}
|
||
})
|
||
.catch(() => {})
|
||
}
|
||
|
||
// ============ 获取表格数据 ============
|
||
const fetchTableList = () => {
|
||
const params = {
|
||
pageNum: '1',
|
||
pageSize: '10'
|
||
}
|
||
if (selectedProject.value) {
|
||
params.villageId = selectedProject.value
|
||
}
|
||
if (selectedFeeType.value) {
|
||
params.chargeTypeId = selectedFeeType.value
|
||
}
|
||
if (datetimerange.value && datetimerange.value.length === 2) {
|
||
params.startDate = datetimerange.value[0]
|
||
params.endDate = datetimerange.value[1]
|
||
}
|
||
getTableList(params)
|
||
.then((res) => {
|
||
if (res.code === 200 && res.data) {
|
||
// 项目总览
|
||
const { projectOverview, feeTypeOverview, houseTypeOverview } = res.data
|
||
if (projectOverview) {
|
||
tableConfigs[0].data = (projectOverview.items).map((item) => ({
|
||
name: item.name,
|
||
amount: Number(item.actualAmount),
|
||
discount: Number(item.discountAmount)
|
||
}))
|
||
tableConfigs[0].total = {
|
||
amount: Number(projectOverview.totalActualAmount),
|
||
discount: Number(projectOverview.totalDiscountAmount)
|
||
}
|
||
}
|
||
// 收费类型总览
|
||
if (feeTypeOverview) {
|
||
tableConfigs[1].data = (feeTypeOverview.items).map((item) => ({
|
||
name: item.name,
|
||
amount: Number(item.actualAmount),
|
||
discount: Number(item.discountAmount)
|
||
}))
|
||
tableConfigs[1].total = {
|
||
amount: Number(feeTypeOverview.totalActualAmount),
|
||
discount: Number(feeTypeOverview.totalDiscountAmount)
|
||
}
|
||
}
|
||
// 房屋类型总览
|
||
if (houseTypeOverview) {
|
||
tableConfigs[2].data = (houseTypeOverview.items).map((item) => ({
|
||
name: item.name,
|
||
amount: Number(item.actualAmount),
|
||
discount: Number(item.discountAmount)
|
||
}))
|
||
tableConfigs[2].total = {
|
||
amount: Number(houseTypeOverview.totalActualAmount),
|
||
discount: Number(houseTypeOverview.totalDiscountAmount)
|
||
}
|
||
}
|
||
}
|
||
})
|
||
.catch(() => {})
|
||
}
|
||
|
||
// ============ 生命周期 ============
|
||
onShow(() => {
|
||
fetchVillageList()
|
||
fetchFeeTypes()
|
||
fetchTableList()
|
||
})
|
||
|
||
// ============ 事件处理 ============
|
||
const goBack = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
const onProjectChange = (e) => {
|
||
const index = e.detail.value
|
||
const option = projectOptions.value[index]
|
||
selectedProject.value = option.value
|
||
selectedProjectLabel.value = option.label
|
||
}
|
||
|
||
const onFeeTypeChange = (e) => {
|
||
const index = e.detail.value
|
||
const option = feeTypeOptions.value[index]
|
||
selectedFeeType.value = option.value
|
||
selectedFeeTypeLabel.value = option.label
|
||
}
|
||
|
||
const onSearch = () => {
|
||
fetchTableList()
|
||
}
|
||
|
||
const onReset = () => {
|
||
selectedProject.value = ''
|
||
selectedProjectLabel.value = '全部项目'
|
||
selectedFeeType.value = ''
|
||
selectedFeeTypeLabel.value = '全部收费类型'
|
||
datetimerange.value = []
|
||
fetchTableList()
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.page_padding {
|
||
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
|
||
padding: 0 30rpx;
|
||
position: relative;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
|
||
.status-bar {
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
// ===== 筛选区域(取消背景) =====
|
||
.filter-section {
|
||
padding: 24rpx 0 16rpx;
|
||
flex-shrink: 0;
|
||
|
||
.filter-row {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.filter-item {
|
||
flex: 1;
|
||
min-width: 0;
|
||
|
||
& + .filter-item {
|
||
margin-left: 20rpx;
|
||
}
|
||
|
||
.filter-label {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background: transparent;
|
||
border-radius: 12rpx;
|
||
padding: 16rpx 24rpx;
|
||
font-size: 26rpx;
|
||
color: #333333;
|
||
border: 2rpx solid #DCDFE6;
|
||
transition: all 0.2s;
|
||
|
||
.filter-arrow {
|
||
font-size: 20rpx;
|
||
color: #999;
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
&:active {
|
||
background: #f5f7fa;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 日期选择器
|
||
.date-row {
|
||
margin-top: 16rpx;
|
||
|
||
.date-picker {
|
||
// 透明背景
|
||
}
|
||
}
|
||
|
||
// 搜索 & 重置按钮
|
||
.btn-row {
|
||
display: flex;
|
||
margin-top: 24rpx;
|
||
|
||
.reset-btn,
|
||
.search-btn {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 80rpx;
|
||
border-radius: 40rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 500;
|
||
|
||
&:active {
|
||
opacity: 0.85;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
.reset-btn {
|
||
background: #ffffff;
|
||
color: #4D5DFA;
|
||
border: 2rpx solid #4D5DFA;
|
||
}
|
||
|
||
.search-btn {
|
||
margin-left: 24rpx;
|
||
background: linear-gradient(135deg, #4D5DFA 0%, #6C7AFF 100%);
|
||
color: #ffffff;
|
||
box-shadow: 0 8rpx 20rpx rgba(77, 93, 250, 0.25);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ===== 表格滚动容器 =====
|
||
.table-scroll {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
|
||
// 每个表格的外层包装
|
||
.table-wrapper {
|
||
margin-bottom: 32rpx;
|
||
|
||
// 标题独立于卡片外
|
||
.table-title {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #1a2332;
|
||
padding: 0 0 16rpx 8rpx;
|
||
letter-spacing: 2rpx;
|
||
}
|
||
|
||
// 卡片(仅包含表格内容,不含标题)
|
||
.table-card {
|
||
background: #ffffff;
|
||
border-radius: 24rpx;
|
||
padding: 24rpx 28rpx;
|
||
box-shadow: 0 6rpx 30rpx rgba(0, 0, 0, 0.05);
|
||
|
||
// 表头(添加背景色 #EBEDFF)
|
||
.table-header {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16rpx 20rpx;
|
||
background-color: #EBEDFF;
|
||
border-radius: 16rpx 16rpx 0 0;
|
||
font-size: 26rpx;
|
||
color: #2c3a4b;
|
||
font-weight: 600;
|
||
|
||
.col-name {
|
||
flex: 2;
|
||
text-align: left;
|
||
}
|
||
.col-amount {
|
||
flex: 1.2;
|
||
text-align: right;
|
||
padding-right: 8rpx;
|
||
}
|
||
.col-discount {
|
||
flex: 1;
|
||
text-align: right;
|
||
padding-right: 4rpx;
|
||
}
|
||
}
|
||
|
||
// 数据行
|
||
.table-row {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 18rpx 20rpx;
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
border-bottom: 1rpx solid #f7f8fa;
|
||
|
||
&:last-of-type {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.col-name {
|
||
flex: 2;
|
||
text-align: left;
|
||
font-weight: 400;
|
||
color: #2c3a4b;
|
||
}
|
||
.col-amount {
|
||
flex: 1.2;
|
||
text-align: right;
|
||
padding-right: 8rpx;
|
||
font-weight: 500;
|
||
color: #1a2332;
|
||
}
|
||
.col-discount {
|
||
flex: 1;
|
||
text-align: right;
|
||
padding-right: 4rpx;
|
||
color: #666666;
|
||
font-weight: 500;
|
||
}
|
||
|
||
// 汇总行
|
||
&.total-row {
|
||
background: #f7f9fc;
|
||
border-radius: 0 0 12rpx 12rpx;
|
||
margin-top: 4rpx;
|
||
padding: 18rpx 20rpx;
|
||
font-weight: 600;
|
||
border-bottom: none;
|
||
|
||
.col-name {
|
||
color: #4D5DFA;
|
||
font-weight: 600;
|
||
}
|
||
.col-amount {
|
||
color: #4D5DFA;
|
||
font-weight: 600;
|
||
}
|
||
.col-discount {
|
||
color: #4D5DFA;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 隔行变色(数据行)
|
||
.table-row:not(.total-row):nth-child(even) {
|
||
background: #fafbfc;
|
||
border-radius: 0;
|
||
}
|
||
|
||
.empty-row {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 60rpx 0;
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
|
||
.bottom-placeholder {
|
||
height: 40rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ===== 响应式适配 =====
|
||
@media screen and (max-width: 374px) {
|
||
.page_padding {
|
||
padding: 0 20rpx;
|
||
|
||
.filter-section {
|
||
padding: 16rpx 0 12rpx;
|
||
|
||
.filter-row {
|
||
.filter-item {
|
||
& + .filter-item {
|
||
margin-left: 12rpx;
|
||
}
|
||
|
||
.filter-label {
|
||
padding: 12rpx 16rpx;
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.table-scroll .table-wrapper {
|
||
.table-card {
|
||
padding: 16rpx 16rpx;
|
||
|
||
.table-header,
|
||
.table-row {
|
||
padding: 14rpx 16rpx;
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
.table-title {
|
||
font-size: 30rpx;
|
||
padding-bottom: 12rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@media screen and (min-width: 600px) {
|
||
.page_padding {
|
||
padding: 0 60rpx;
|
||
|
||
.filter-section {
|
||
padding: 30rpx 0 20rpx;
|
||
}
|
||
|
||
.table-scroll .table-wrapper {
|
||
.table-card {
|
||
padding: 32rpx 40rpx;
|
||
}
|
||
.table-title {
|
||
font-size: 38rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ===== uni-datetime-picker 透明背景 =====
|
||
:deep(.uni-date-x) {
|
||
background: transparent !important;
|
||
}
|
||
</style>
|