1184 lines
30 KiB
Vue
1184 lines
30 KiB
Vue
<template>
|
||
<page-meta :page-style="`overflow:${showHouseDetail ? 'hidden' : 'visible'};`"></page-meta>
|
||
<view class="call-page">
|
||
<!-- 自定义导航栏 -->
|
||
<wd-navbar :bordered="false"
|
||
custom-style="background: transparent !important; backdrop-filter: blur(10px) !important; -webkit-backdrop-filter: blur(10px) !important;"
|
||
safeAreaInsetTop fixed placeholder>
|
||
<template #left>
|
||
<view class="li-ml-15 li-mt-10 li-flex li-items-center">
|
||
<text v-if="hasMultiplePages" class="ri-arrow-left-s-line li-text-70"
|
||
@click="toPages({type:'nav'})"></text>
|
||
<text v-if="!hasMultiplePages" class="ri-home-5-line li-text-55 li-mb-8 li-mr-10"
|
||
@click="toPages({type:'home'})"></text>
|
||
<text class="li-text-42">物业费催缴</text>
|
||
</view>
|
||
</template>
|
||
<!-- #ifdef MP-WEIXIN -->
|
||
<!-- <template #right>
|
||
<view class="li-mr-180 li-flex li-items-center">
|
||
<view class="community-switcher" @click="showCommunityPicker = true">
|
||
<text class="community-name">{{activeCommunity.name}}</text>
|
||
<text class="arrow-icon ri-arrow-down-s-line"></text>
|
||
</view>
|
||
</view>
|
||
</template> -->
|
||
<!-- #endif -->
|
||
<!-- #ifndef MP-WEIXIN -->
|
||
<!-- <template #right>
|
||
<view class="li-mr-25 li-flex li-items-center">
|
||
<view class="community-switcher" @click="showCommunityPicker = true">
|
||
<text class="community-name">{{activeCommunity.name}}</text>
|
||
<text class="arrow-icon ri-arrow-down-s-line"></text>
|
||
</view>
|
||
</view>
|
||
</template> -->
|
||
<!-- #endif -->
|
||
</wd-navbar>
|
||
<!-- 导航栏背景 -->
|
||
<view class="nav-bg-layer"></view>
|
||
|
||
<!-- 页面内容 -->
|
||
<view class="content">
|
||
<!-- 小区信息 -->
|
||
<view class="community-card li-mx-40 li-mt-40">
|
||
<view class="community-header li-p-30">
|
||
<view class="li-flex li-justify-between li-items-center">
|
||
<text
|
||
class="community-name li-text-42 li-font-bold li-text-white">{{activeCommunity.name}}</text>
|
||
<button class="refresh-btn" @tap.stop="handleRefreshClick">
|
||
<text class="li-text-24 li-text-white">刷新数据</text>
|
||
</button>
|
||
</view>
|
||
<view class="li-flex li-items-center li-mt-20">
|
||
<text class="li-text-28 li-text-white li-opacity-80">{{activeCommunity.address}}</text>
|
||
</view>
|
||
<view class="li-flex li-items-center li-mt-10">
|
||
<text class="li-text-28 li-text-white li-opacity-80">欠费总量:</text>
|
||
<text class="li-text-32 li-text-white li-font-bold">¥{{activeCommunity.totalUnpaid}}</text>
|
||
</view>
|
||
</view>
|
||
<view class="community-footer li-p-30 li-bg-white">
|
||
<view class="li-flex li-items-center">
|
||
<text class="ri-community-line li-text-28 li-text-#666 li-mr-10"></text>
|
||
<text class="li-text-28 li-text-#666">物业管理中心:</text>
|
||
<text class="li-text-28 li-text-#333">{{activeCommunity.managementAddress}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="building-selector-container li-mt-30">
|
||
<view class="building-sidebar-layout">
|
||
<!-- 左侧楼栋侧边导航 -->
|
||
<view class="sidebar-container">
|
||
<wd-sidebar v-model="activeBuilding" custom-class="building-sidebar">
|
||
<wd-sidebar-item v-for="item in buildingList" :key="item.id" :value="String(item.id)"
|
||
:label="item.name" :badge="getBuildingUnpaidCount(item.id)"
|
||
:badge-props="{ type: 'primary', bgColor: '#ff4d4f' }" />
|
||
</wd-sidebar>
|
||
</view>
|
||
|
||
<!-- 右侧单元与房屋内容 -->
|
||
<view class="content-container">
|
||
<!-- 单元选择 -->
|
||
<view class="unit-selector li-px-30 li-py-20">
|
||
<view class="li-flex li-justify-between li-items-center li-mb-15">
|
||
<text class="li-text-30 li-font-bold">{{getActiveBuilding().name}}</text>
|
||
<text class="li-text-24 li-text-#3e9bff">单元数量: {{unitList.length}}</text>
|
||
</view>
|
||
<view class="unit-grid">
|
||
<view v-for="unit in unitList" :key="unit.value" class="unit-item li-mr-15 li-mb-15"
|
||
:class="activeUnitId === String(unit.value) ? 'unit-item-active' : ''"
|
||
@click="handleUnitSelect(unit.value)">
|
||
<text class="unit-text"
|
||
:class="activeUnitId === String(unit.value) ? 'unit-text-active' : ''">{{unit.label}}</text>
|
||
<view v-if="getUnitUnpaidCount(unit.value) > 0" class="unit-badge">
|
||
{{getUnitUnpaidCount(unit.value)}}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 楼层和房屋数据 -->
|
||
<view class="house-list li-px-30 li-pb-40">
|
||
<view v-for="(floor, floorIndex) in floorHouseList" :key="floorIndex"
|
||
class="floor-item li-mb-20">
|
||
<view class="floor-header">
|
||
<text class="floor-title">{{floor.floor}}层</text>
|
||
</view>
|
||
<view class="house-grid">
|
||
<view v-for="(house, houseIndex) in floor.houses" :key="houseIndex"
|
||
class="house-card" @click="showHouseDetailFunc(house)">
|
||
<view class="house-card-inner" :class="{'house-has-unpaid': house.unpaid > 0}">
|
||
<view class="house-number-box">
|
||
<text class="house-number">{{house.number}}</text>
|
||
</view>
|
||
<view class="house-content">
|
||
<view v-if="house.unpaid > 0" class="house-unpaid">
|
||
<text class="unpaid-count">{{house.unpaid}}</text>
|
||
<text class="unpaid-text">笔欠费</text>
|
||
</view>
|
||
<view v-else class="house-paid">
|
||
<text class="paid-text">无欠费</text>
|
||
</view>
|
||
</view>
|
||
<view v-if="house.unpaid > 0" class="house-badge">{{house.unpaid}}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view v-if="floorHouseList.length === 0"
|
||
class="empty-state li-py-100 li-flex-center li-flex-col">
|
||
<wd-status-tip :image="uni.$globalData?.RESOURCE_URL+'tip/search.png'" tip="该单元暂无数据" />
|
||
</view>
|
||
|
||
<!-- 添加底部安全间距 -->
|
||
<view class="bottom-safe-area"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 房屋详情弹窗 -->
|
||
<wd-popup v-model="showHouseDetail" position="bottom" custom-class="custom-class-popup">
|
||
<view class="house-detail" :style="{ height: '60vh' }">
|
||
<view class="detail-header li-p-30 li-bottom-border">
|
||
<view class="li-flex li-justify-between li-items-center">
|
||
<text class="li-text-36 li-font-bold">{{selectedHouse.number}}</text>
|
||
<text class="ri-close-line li-text-50 li-text-#999" @click="showHouseDetail = false"></text>
|
||
</view>
|
||
<view class="li-flex li-items-center li-mt-10">
|
||
<text class="li-text-28 li-text-#666">{{activeCommunity.name}} {{getActiveBuilding().name}}
|
||
{{activeUnit}}</text>
|
||
</view>
|
||
</view>
|
||
<scroll-view scroll-y class="detail-content-scroll">
|
||
<view class="detail-content li-p-30">
|
||
<view v-if="selectedHouse.unpaid > 0">
|
||
<view class="li-flex li-justify-between li-items-center li-mb-20">
|
||
<text class="li-text-32 li-font-bold">欠费账单</text>
|
||
<text class="li-text-28 li-text-#3e9bff">共 {{selectedHouse.unpaid}} 笔</text>
|
||
</view>
|
||
<view v-for="(bill, index) in selectedHouse.bills" :key="index"
|
||
class="bill-item li-p-30 li-mb-20">
|
||
<view class="li-flex li-items-center li-justify-between">
|
||
<text class="li-text-30 li-font-bold">{{bill.type}}</text>
|
||
<text class="li-text-30 li-text-#ff4d4f">¥{{bill.amount}}</text>
|
||
</view>
|
||
<view class="li-flex li-items-center li-mt-15">
|
||
<text class="li-text-26 li-text-#999">创建时间:</text>
|
||
<text class="li-text-26 li-text-#666">{{bill.overdueTime}}</text>
|
||
</view>
|
||
<view class="li-flex li-items-center li-mt-10">
|
||
<text class="li-text-26 li-text-#999">缴费时间段:</text>
|
||
<text class="li-text-26 li-text-#666">{{bill.period}}</text>
|
||
</view>
|
||
<view class="li-flex li-justify-between li-items-center li-flex-row-reverse li-mt-20">
|
||
<wd-button size="small" type="primary"
|
||
@click="prepareCaller(selectedHouse)">电话催缴</wd-button>
|
||
<!-- <wd-button size="small" type="primary"
|
||
@click="sendReminder(selectedHouse)">发送通知</wd-button> -->
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-else class="empty-bills li-flex-center li-flex-col">
|
||
<text class="ri-check-double-line li-text-100 li-text-#52c41a"></text>
|
||
<text class="li-text-30 li-text-#666 li-mt-20">该住户暂无欠费</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</wd-popup>
|
||
<community-selector :communities="formattedCommunityList" v-model="selectedCommunity"
|
||
v-model:visible="showCommunityPicker" @confirm="handleCommunityConfirm" />
|
||
|
||
<!-- 电话催缴动作面板 -->
|
||
<wd-action-sheet v-model="showPhoneActionSheet" :actions="phoneActions" title="选择拨打业主电话" cancel-text="取消"
|
||
@select="handlePhoneSelect" :close-on-click-action="true" :close-on-click-modal="true"
|
||
custom-style="border-radius: 16px; max-height: 70vh;" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
reactive,
|
||
computed,
|
||
onMounted,
|
||
watch
|
||
} from 'vue';
|
||
import {
|
||
onLoad
|
||
} from '@dcloudio/uni-app';
|
||
import {
|
||
useNavigation
|
||
} from '@/hooks/useNavigation';
|
||
import {
|
||
useToast
|
||
} from '@/uni_modules/wot-design-uni';
|
||
import CommunitySelector from '@/components/community-selector/community-selector.vue';
|
||
import {
|
||
feeInfo
|
||
} from '/api/fee'
|
||
|
||
const Toast = useToast();
|
||
|
||
// 使用导航composable
|
||
const {
|
||
hasMultiplePages,
|
||
isTabBarPage,
|
||
checkRouteStack
|
||
} = useNavigation();
|
||
|
||
// 小区信息
|
||
const village = ref({});
|
||
// 楼栋列表
|
||
const buildingList = ref([]);
|
||
// 当前选中的楼栋
|
||
const activeBuilding = ref('');
|
||
// 单元列表
|
||
const unitList = ref([]);
|
||
// 当前选中的单元 (使用字符串类型)
|
||
const activeUnitId = ref('');
|
||
// 兼容原有的数字类型单元ID
|
||
const activeUnit = computed(() => {
|
||
// 确保返回的是当前选中的单元名称,用于显示
|
||
if (!activeUnitId.value || !unitList.value.length) return '';
|
||
const unit = unitList.value.find(u => String(u.value) === String(activeUnitId.value));
|
||
return unit ? unit.label : '';
|
||
});
|
||
|
||
// 当前选中的小区数据
|
||
const activeCommunity = computed(() => {
|
||
return {
|
||
id: '1',
|
||
name: village.value.village_name || '暂无数据',
|
||
address: `${village.value.province_name || ''}-${village.value.city_name || ''}-${village.value.area_name || ''}`,
|
||
managementAddress: village.value.address || '暂无地址',
|
||
totalUnpaid: calculateTotalUnpaid()
|
||
};
|
||
});
|
||
|
||
// 计算总欠费数量
|
||
const calculateTotalUnpaid = () => {
|
||
let total = 0;
|
||
if (!village.value.regions) return total;
|
||
|
||
village.value.regions.forEach(region => {
|
||
region.cells.forEach(cell => {
|
||
cell.houses.forEach(house => {
|
||
// 计算房屋的欠费金额总和
|
||
if (house.estate && house.estate.length > 0) {
|
||
house.estate.forEach(item => {
|
||
// 将字符串金额转换为数字并累加
|
||
total += parseFloat(item.total_money || 0);
|
||
});
|
||
}
|
||
|
||
if (house.meter && house.meter.length > 0) {
|
||
house.meter.forEach(item => {
|
||
// 将字符串金额转换为数字并累加
|
||
total += parseFloat(item.total_money || 0);
|
||
});
|
||
}
|
||
});
|
||
});
|
||
});
|
||
// 格式化为两位小数
|
||
return total.toFixed(2);
|
||
};
|
||
|
||
// 房屋数据结构处理
|
||
const floorHouseList = computed(() => {
|
||
if (!activeBuilding.value || !activeUnitId.value) return [];
|
||
|
||
// 找到当前选中的楼栋和单元
|
||
const region = village.value.regions?.find(r => String(r.region_id) === activeBuilding.value);
|
||
if (!region) return [];
|
||
|
||
const cell = region.cells?.find(c => String(c.cell_id) === activeUnitId.value);
|
||
if (!cell) return [];
|
||
|
||
// 按楼层分组
|
||
const floorGroups = {};
|
||
cell.houses.forEach(house => {
|
||
const floor = house.floor;
|
||
if (!floorGroups[floor]) {
|
||
floorGroups[floor] = {
|
||
floor: `${floor}`,
|
||
houses: []
|
||
};
|
||
}
|
||
|
||
floorGroups[floor].houses.push({
|
||
id: String(house.house_id), // 使用字符串ID
|
||
number: house.house_name,
|
||
unpaid: (house.estate || []).length + (house.meter || []).length,
|
||
owner: house.owner && house.owner.length > 0 ? house.owner[0].owner_name : '未知',
|
||
phone: house.owner && house.owner.length > 0 ? house.owner[0].mobile : '',
|
||
bills: formatBills(house)
|
||
});
|
||
});
|
||
|
||
// 转换为数组并按楼层排序
|
||
return Object.values(floorGroups).sort((a, b) => parseInt(a.floor) - parseInt(b.floor));
|
||
});
|
||
|
||
// 格式化账单数据
|
||
const formatBills = (house) => {
|
||
const bills = [];
|
||
|
||
// 处理物业费账单
|
||
if (house.estate && house.estate.length > 0) {
|
||
house.estate.forEach(item => {
|
||
bills.push({
|
||
type: item.fee_name || '物业费',
|
||
amount: item.total_money,
|
||
period: `${item.begin_date} 至 ${item.end_date}`,
|
||
overdueTime: item.create_time
|
||
});
|
||
});
|
||
}
|
||
|
||
// 处理抄表账单
|
||
if (house.meter && house.meter.length > 0) {
|
||
house.meter.forEach(item => {
|
||
bills.push({
|
||
type: item.fee_name || '抄表费',
|
||
amount: item.total_money,
|
||
period: `${item.create_time}`,
|
||
overdueTime: item.create_time
|
||
});
|
||
});
|
||
}
|
||
|
||
return bills;
|
||
};
|
||
|
||
// 获取当前选中的楼栋对象
|
||
const getActiveBuilding = () => {
|
||
if (!activeBuilding.value || !village.value.regions) return {
|
||
id: '',
|
||
name: ''
|
||
};
|
||
const region = village.value.regions.find(r => String(r.region_id) === activeBuilding.value);
|
||
return region ? {
|
||
id: String(region.region_id),
|
||
name: region.region_name
|
||
} : {
|
||
id: '',
|
||
name: ''
|
||
};
|
||
};
|
||
|
||
// 获取楼栋的欠费住户数量
|
||
const getBuildingUnpaidCount = (buildingId) => {
|
||
if (!village.value.regions) return '';
|
||
|
||
const region = village.value.regions.find(r => String(r.region_id) === buildingId);
|
||
if (!region) return '';
|
||
|
||
let count = 0;
|
||
region.cells.forEach(cell => {
|
||
cell.houses.forEach(house => {
|
||
const unpaidCount = (house.estate || []).length + (house.meter || []).length;
|
||
if (unpaidCount > 0) count++;
|
||
});
|
||
});
|
||
|
||
return count > 0 ? count : '';
|
||
};
|
||
|
||
// 获取单元欠费数量
|
||
const getUnitUnpaidCount = (unitId) => {
|
||
if (!village.value.regions || !activeBuilding.value) return 0;
|
||
|
||
const region = village.value.regions.find(r => String(r.region_id) === activeBuilding.value);
|
||
if (!region) return 0;
|
||
|
||
const cell = region.cells.find(c => String(c.cell_id) === unitId);
|
||
if (!cell) return 0;
|
||
|
||
let count = 0;
|
||
cell.houses.forEach(house => {
|
||
const unpaidCount = (house.estate || []).length + (house.meter || []).length;
|
||
if (unpaidCount > 0) count++;
|
||
});
|
||
|
||
return count;
|
||
};
|
||
|
||
// 房屋详情弹窗
|
||
const showHouseDetail = ref(false);
|
||
const selectedHouse = ref({
|
||
id: '',
|
||
number: '',
|
||
unpaid: 0,
|
||
owner: [],
|
||
phone: '',
|
||
bills: []
|
||
});
|
||
|
||
// 电话催缴动作面板
|
||
const showPhoneActionSheet = ref(false);
|
||
const phoneActions = ref([]);
|
||
|
||
// 显示房屋详情
|
||
const showHouseDetailFunc = (house) => {
|
||
selectedHouse.value = house;
|
||
showHouseDetail.value = true;
|
||
};
|
||
|
||
// 准备并显示电话催缴动作面板
|
||
const prepareCaller = (house) => {
|
||
// 显示加载状态
|
||
uni.showLoading({
|
||
title: '获取联系人...',
|
||
mask: true
|
||
});
|
||
|
||
// 找到原始房屋数据
|
||
if (!activeBuilding.value || !activeUnitId.value) {
|
||
uni.hideLoading();
|
||
Toast.fail('无法获取房屋信息');
|
||
return;
|
||
}
|
||
|
||
const region = village.value.regions?.find(r => String(r.region_id) === activeBuilding.value);
|
||
if (!region) {
|
||
uni.hideLoading();
|
||
Toast.fail('未找到楼栋信息');
|
||
return;
|
||
}
|
||
|
||
const cell = region.cells?.find(c => String(c.cell_id) === activeUnitId.value);
|
||
if (!cell) {
|
||
uni.hideLoading();
|
||
Toast.fail('未找到单元信息');
|
||
return;
|
||
}
|
||
|
||
// 找到对应的房屋
|
||
const originalHouse = cell.houses.find(h => String(h.house_id) === String(house.id));
|
||
if (!originalHouse || !originalHouse.owner) {
|
||
uni.hideLoading();
|
||
Toast.fail('未找到该房屋业主信息');
|
||
return;
|
||
}
|
||
|
||
// 生成业主电话列表
|
||
if (originalHouse.owner.length === 0) {
|
||
uni.hideLoading();
|
||
Toast.fail('该房屋暂无业主联系方式');
|
||
return;
|
||
}
|
||
|
||
// 更新selectedHouse的owner字段
|
||
selectedHouse.value.owner = originalHouse.owner;
|
||
|
||
// 生成动作列表
|
||
phoneActions.value = originalHouse.owner.map(owner => ({
|
||
name: `${owner.owner_name || '未知'}${owner.sex === 0 ? '(先生)' : owner.sex === 1 ? '(女士)' : ''}`,
|
||
subname: owner.mobile || '无电话',
|
||
disabled: !owner.mobile,
|
||
color: !owner.mobile ? '#999999' : ''
|
||
}));
|
||
|
||
// 隐藏加载状态
|
||
setTimeout(() => {
|
||
uni.hideLoading();
|
||
// 显示动作面板
|
||
showPhoneActionSheet.value = true;
|
||
}, 300);
|
||
};
|
||
|
||
// 电话催缴 - 选择后执行
|
||
const handlePhoneSelect = (item, index) => {
|
||
const owner = item.item.subname;
|
||
if (!owner) {
|
||
Toast.error('无效的联系电话');
|
||
return;
|
||
}
|
||
uni.makePhoneCall({
|
||
phoneNumber: owner,
|
||
});
|
||
};
|
||
|
||
// 刷新按钮点击处理
|
||
let isRefreshing = false;
|
||
const handleRefreshClick = async () => {
|
||
getFeeInfo();
|
||
};
|
||
|
||
// 页面跳转
|
||
const toPages = (item) => {
|
||
switch (item.type) {
|
||
case 'nav':
|
||
uni.navigateBack({
|
||
delta: 1
|
||
});
|
||
break;
|
||
case 'home':
|
||
uni.switchTab({
|
||
url: '/pages/index/index'
|
||
});
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
};
|
||
|
||
// 获取接口数据
|
||
const getFeeInfo = async () => {
|
||
try {
|
||
uni.showLoading({
|
||
title: '加载中...'
|
||
});
|
||
|
||
const res = await feeInfo();
|
||
console.log('API返回数据:', res);
|
||
|
||
if (res.code === 200 && res.data) {
|
||
// 设置小区信息
|
||
village.value = res.data.village || {};
|
||
console.log('小区信息:', village.value);
|
||
|
||
// 处理楼栋列表
|
||
if (village.value.regions && village.value.regions.length > 0) {
|
||
buildingList.value = village.value.regions.map(region => ({
|
||
id: String(region.region_id), // 确保ID是字符串类型
|
||
name: region.region_name
|
||
}));
|
||
console.log('楼栋列表:', buildingList.value);
|
||
|
||
// 默认选中第一个楼栋
|
||
if (buildingList.value.length > 0) {
|
||
// 直接设置为第一个楼栋的ID(已确保是字符串类型)
|
||
activeBuilding.value = buildingList.value[0].id;
|
||
console.log('默认选中楼栋:', activeBuilding.value);
|
||
// 立即更新单元列表
|
||
updateUnitList();
|
||
}
|
||
} else {
|
||
console.log('没有找到楼栋数据');
|
||
}
|
||
} else {
|
||
Toast.fail('获取数据失败');
|
||
console.error('API返回错误:', res);
|
||
}
|
||
} catch (error) {
|
||
console.error('获取数据出错:', error);
|
||
Toast.fail('网络异常,请重试');
|
||
} finally {
|
||
uni.hideLoading();
|
||
}
|
||
};
|
||
|
||
// 更新单元列表
|
||
const updateUnitList = () => {
|
||
unitList.value = [];
|
||
if (!activeBuilding.value || !village.value.regions) return;
|
||
|
||
const region = village.value.regions.find(r => String(r.region_id) === activeBuilding.value);
|
||
if (region && region.cells) {
|
||
unitList.value = region.cells.map(cell => ({
|
||
value: String(cell.cell_id), // 确保单元ID也是字符串类型
|
||
label: cell.cell_name
|
||
}));
|
||
|
||
// 默认选中第一个单元
|
||
if (unitList.value.length > 0) {
|
||
activeUnitId.value = unitList.value[0].value;
|
||
console.log('默认选中单元:', activeUnitId.value);
|
||
}
|
||
} else {
|
||
console.log('未找到对应楼栋或单元数据');
|
||
}
|
||
};
|
||
|
||
// 监听楼栋变化
|
||
watch(activeBuilding, () => {
|
||
updateUnitList();
|
||
});
|
||
|
||
// 初始化数据
|
||
const initData = async () => {
|
||
await getFeeInfo();
|
||
|
||
// 强制刷新视图
|
||
if (unitList.value.length > 0 && !activeUnitId.value) {
|
||
// 如果单元列表已加载但没有选中值,再次尝试设置
|
||
activeUnitId.value = unitList.value[0].value;
|
||
console.log('强制设置默认单元:', activeUnitId.value);
|
||
}
|
||
|
||
// 给视图一点时间渲染
|
||
setTimeout(() => {
|
||
console.log('当前状态 - 楼栋:', activeBuilding.value, '单元:', activeUnitId.value);
|
||
|
||
// 最终检查,确保楼栋和单元都有选中值
|
||
if (buildingList.value.length > 0 && !activeBuilding.value) {
|
||
console.log('检测到楼栋未选中,强制选中第一个楼栋');
|
||
activeBuilding.value = buildingList.value[0].id;
|
||
updateUnitList();
|
||
}
|
||
}, 500);
|
||
};
|
||
|
||
// 生命周期钩子
|
||
onLoad(() => {
|
||
checkRouteStack();
|
||
initData();
|
||
});
|
||
|
||
// 小区选择确认
|
||
const handleCommunityConfirm = (selected) => {
|
||
console.log(selected, 'selected');
|
||
// 此处逻辑可以根据实际需求修改
|
||
};
|
||
|
||
// 获取小区列表,扁平化处理
|
||
const formattedCommunityList = computed(() => {
|
||
// 原始数据是二维数组,需要提取第一层
|
||
const flatList = buildingList.value || [];
|
||
return flatList.map(item => ({
|
||
value: item.id,
|
||
label: item.name
|
||
}));
|
||
});
|
||
|
||
// 新增的showCommunityPicker
|
||
const showCommunityPicker = ref(false);
|
||
|
||
// 新增的handleUnitSelect
|
||
const handleUnitSelect = (value) => {
|
||
activeUnitId.value = String(value);
|
||
console.log('选中单元:', activeUnitId.value);
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
page {
|
||
background-color: #F7F8FA;
|
||
}
|
||
|
||
.call-page {
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.nav-bg-layer {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: calc(var(--status-bar-height) + 88rpx);
|
||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.9) 100%);
|
||
z-index: 10;
|
||
backdrop-filter: blur(10px);
|
||
-webkit-backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.content {
|
||
padding-bottom: 0;
|
||
}
|
||
|
||
.community-switch {
|
||
height: 60rpx;
|
||
padding: 0 25rpx;
|
||
border-radius: 30rpx;
|
||
background-color: rgba(62, 155, 255, 0.1);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 2rpx 8rpx rgba(62, 155, 255, 0.15);
|
||
transition: all 0.2s;
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
.community-switcher {
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: rgba(246, 248, 250, 0.8);
|
||
height: 60rpx;
|
||
border-radius: 30rpx;
|
||
padding: 0 20rpx 0 24rpx;
|
||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
|
||
border: 1px solid rgba(232, 234, 237, 0.8);
|
||
|
||
.community-name {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
max-width: 140rpx;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.arrow-icon {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-left: 6rpx;
|
||
}
|
||
|
||
&:active {
|
||
background-color: rgba(242, 244, 246, 0.8);
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
.selector-trigger {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 60rpx;
|
||
padding: 0 20rpx;
|
||
background-color: rgba(255, 255, 255, 0.9);
|
||
border-radius: 30rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
||
|
||
.location-icon {
|
||
font-size: 28rpx;
|
||
color: #3e9bff;
|
||
margin-right: 6rpx;
|
||
}
|
||
|
||
.trigger-text {
|
||
font-size: 28rpx;
|
||
color: #3e9bff;
|
||
font-weight: 400;
|
||
}
|
||
|
||
&:active {
|
||
opacity: 0.85;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
.community-card {
|
||
border-radius: 20rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||
|
||
.community-header {
|
||
background: linear-gradient(135deg, #3e9bff, #2374e1);
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
right: -20%;
|
||
width: 400rpx;
|
||
height: 400rpx;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
z-index: 0;
|
||
}
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: -30%;
|
||
left: -10%;
|
||
width: 300rpx;
|
||
height: 300rpx;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.05);
|
||
z-index: 0;
|
||
}
|
||
}
|
||
|
||
.community-footer {
|
||
border-radius: 0 0 20rpx 20rpx;
|
||
}
|
||
}
|
||
|
||
.building-selector-container {
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.building-sidebar-layout {
|
||
display: flex;
|
||
/* 移动端通用适配 */
|
||
height: calc(100vh - 400rpx - constant(safe-area-inset-bottom));
|
||
height: calc(100vh - 400rpx - env(safe-area-inset-bottom));
|
||
/* APP端特殊适配 */
|
||
/* #ifdef APP-PLUS */
|
||
height: calc(100vh - 400rpx - 50rpx);
|
||
margin-bottom: 50rpx;
|
||
/* #endif */
|
||
/* 非APP端 */
|
||
/* #ifndef APP-PLUS */
|
||
margin-bottom: calc(constant(safe-area-inset-bottom));
|
||
margin-bottom: calc(env(safe-area-inset-bottom));
|
||
/* #endif */
|
||
}
|
||
|
||
.sidebar-container {
|
||
width: 180rpx;
|
||
flex-shrink: 0;
|
||
height: 100%;
|
||
background-color: #f8f8f8;
|
||
overflow-y: auto;
|
||
/* APP端特殊适配 */
|
||
/* #ifdef APP-PLUS */
|
||
-webkit-overflow-scrolling: touch;
|
||
/* #endif */
|
||
}
|
||
|
||
.content-container {
|
||
flex: 1;
|
||
height: 100%;
|
||
overflow-y: auto;
|
||
padding-bottom: 40rpx;
|
||
/* APP端特殊适配 */
|
||
/* #ifdef APP-PLUS */
|
||
-webkit-overflow-scrolling: touch;
|
||
/* #endif */
|
||
}
|
||
|
||
.building-sidebar {
|
||
height: 100%;
|
||
}
|
||
|
||
::v-deep .wd-sidebar-item {
|
||
font-size: 28rpx;
|
||
padding: 30rpx 20rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
::v-deep .wd-sidebar-item--active {
|
||
background-color: #fff;
|
||
color: #3e9bff;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.unit-selector {
|
||
background-color: #fff;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.unit-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.unit-item {
|
||
position: relative;
|
||
width: 120rpx;
|
||
height: 65rpx;
|
||
background-color: #f7f8fa;
|
||
border-radius: 10rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 10rpx 10rpx 0;
|
||
|
||
&-active {
|
||
background: rgba(62, 155, 255, 0.1);
|
||
border: 1px solid rgba(62, 155, 255, 0.3);
|
||
}
|
||
|
||
.unit-text {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
font-weight: 500;
|
||
|
||
&-active {
|
||
color: #3e9bff;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
|
||
.unit-badge {
|
||
position: absolute;
|
||
top: -8rpx;
|
||
right: -8rpx;
|
||
min-width: 32rpx;
|
||
height: 32rpx;
|
||
border-radius: 16rpx;
|
||
background-color: #ff4d4f;
|
||
color: #fff;
|
||
font-size: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0 6rpx;
|
||
box-shadow: 0 2rpx 6rpx rgba(255, 77, 79, 0.3);
|
||
}
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
.house-list {
|
||
padding: 0 30rpx 50rpx;
|
||
}
|
||
|
||
.floor-item {
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
/* box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); */
|
||
margin-bottom: 25rpx;
|
||
}
|
||
|
||
.floor-header {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx 30rpx;
|
||
background: linear-gradient(to right, #f5f7fa, #f9fafc);
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.04);
|
||
}
|
||
|
||
.floor-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
position: relative;
|
||
padding-left: 16rpx;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 6rpx;
|
||
height: 28rpx;
|
||
background: #3e9bff;
|
||
border-radius: 3rpx;
|
||
}
|
||
}
|
||
|
||
.house-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
padding: 20rpx 10rpx;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.house-card {
|
||
width: 33.33%;
|
||
padding: 10rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.house-card-inner {
|
||
position: relative;
|
||
background-color: #fff;
|
||
border-radius: 12rpx;
|
||
padding: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
border: 1px solid #fdf9f9;
|
||
transition: all 0.25s;
|
||
overflow: hidden;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
|
||
|
||
&:active {
|
||
transform: translateY(2rpx);
|
||
box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.01);
|
||
}
|
||
}
|
||
|
||
.house-number-box {
|
||
background-color: #f7f9fc;
|
||
padding: 14rpx 10rpx;
|
||
text-align: center;
|
||
border-bottom: 1px solid #eaedf1;
|
||
}
|
||
|
||
.house-number {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.house-content {
|
||
padding: 14rpx 0;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 60rpx;
|
||
}
|
||
|
||
.house-unpaid {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.unpaid-count {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #ff4d4f;
|
||
margin-right: 4rpx;
|
||
}
|
||
|
||
.unpaid-text {
|
||
font-size: 24rpx;
|
||
color: #ff4d4f;
|
||
}
|
||
}
|
||
|
||
.house-paid {
|
||
.paid-text {
|
||
font-size: 24rpx;
|
||
color: #52c41a;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.house-badge {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
background-color: #ff4d4f;
|
||
color: #fff;
|
||
font-size: 20rpx;
|
||
min-width: 36rpx;
|
||
height: 36rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 0 12rpx 0 12rpx;
|
||
font-weight: bold;
|
||
z-index: 2;
|
||
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.house-has-unpaid {
|
||
/* border-color: rgba(255, 77, 79, 0.2); */
|
||
background-color: #fff;
|
||
}
|
||
|
||
.empty-state {
|
||
padding: 100rpx 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.bottom-safe-area {
|
||
height: 0;
|
||
width: 100%;
|
||
|
||
/* #ifdef MP-WEIXIN */
|
||
height: constant(safe-area-inset-bottom);
|
||
height: env(safe-area-inset-bottom);
|
||
/* #endif */
|
||
|
||
/* #ifdef APP-PLUS */
|
||
height: 100rpx;
|
||
/* #endif */
|
||
|
||
/* #ifdef H5 */
|
||
height: 80rpx;
|
||
/* #endif */
|
||
}
|
||
|
||
.empty-bills {
|
||
padding-top: 150rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.house-detail {
|
||
height: 100%;
|
||
border-radius: 24rpx 24rpx 0 0 !important;
|
||
overflow: hidden;
|
||
background-color: #fff;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
|
||
.detail-header {
|
||
// padding-top: constant(safe-area-inset-top);
|
||
// padding-top: env(safe-area-inset-top);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.detail-content-scroll {
|
||
flex: 1;
|
||
height: calc(100% - 120rpx);
|
||
}
|
||
|
||
.detail-content {
|
||
padding-bottom: constant(safe-area-inset-bottom);
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
|
||
.bill-item {
|
||
background-color: #f9f9f9;
|
||
border-radius: 16rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.li-bottom-border {
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||
}
|
||
}
|
||
|
||
::v-deep .wd-navbar__placeholder {
|
||
height: calc(var(--status-bar-height) + 88rpx) !important;
|
||
padding-top: constant(safe-area-inset-top) !important;
|
||
padding-top: env(safe-area-inset-top) !important;
|
||
}
|
||
|
||
.custom-class-popup {
|
||
border-radius: 24rpx 24rpx 0 0 !important;
|
||
}
|
||
|
||
.refresh-btn {
|
||
background-color: rgba(255, 255, 255, 0.3);
|
||
border-radius: 30rpx;
|
||
padding: 8rpx 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
z-index: 5;
|
||
/* 确保在最上层 */
|
||
transition: all 0.2s;
|
||
border: none;
|
||
margin: 0;
|
||
font-size: inherit;
|
||
color: #fff;
|
||
line-height: normal;
|
||
|
||
/* 清除按钮在小程序中的默认样式 */
|
||
&::after {
|
||
display: none;
|
||
border: none;
|
||
}
|
||
|
||
&:active {
|
||
opacity: 0.7;
|
||
transform: scale(0.95);
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<style scoped>
|
||
::-webkit-scrollbar {
|
||
width: 0 !important;
|
||
height: 0 !important;
|
||
display: none;
|
||
}
|
||
</style> |