staff/pagesB/call/index.vue

729 lines
21 KiB
Vue
Raw 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>
<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>
<template #right>
<view class="li-mr-30 li-flex li-items-center">
<view class="community-switch li-flex li-items-center" @click="showCommunityPicker = true">
<text class="li-text-30 li-text-primary li-mr-10">{{activeCommunity.name}}</text>
<text class="ri-arrow-down-s-line li-text-30"></text>
</view>
</view>
</template>
</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>
<view class="li-px-20 li-py-10 li-bg-rgba(255,255,255,0.3) li-rd-30" @click="handleRefresh">
<text class="li-text-28 li-text-white">刷新数据</text>
</view>
</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="li-text-28 li-text-#666">物业管理中心:</text>
<text class="li-text-28 li-text-#333">{{activeCommunity.managementAddress}}</text>
</view>
</view>
</view>
<!-- 楼栋选择 (使用wd-tabs组件) -->
<view class="building-tabs-container li-mt-30">
<wd-tabs v-model="activeBuilding" :slidable="'always'" sticky bg-color="#fff" line-height="3px" inactive-color="#666" color="#3e9bff">
<wd-tab v-for="item in buildingList" :key="item.id" :name="item.id" :title="item.name">
<!-- 单元选择 (使用wd-tabs组件) -->
<view class="unit-selector li-mt-20 li-mx-30">
<view class="li-mb-20">
<text class="li-text-30 li-font-bold">选择单元</text>
</view>
<view class="unit-buttons li-flex li-flex-wrap">
<view v-for="unit in unitList" :key="unit.value"
class="unit-button li-flex-center li-mr-20 li-mb-20"
:class="activeUnitId === String(unit.value) ? 'unit-button-active' : ''"
@click="activeUnitId = String(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 class="house-list li-mt-30 li-pb-40">
<view v-for="(floor, floorIndex) in floorHouseList" :key="floorIndex" class="floor-item li-mb-20">
<view class="floor-header li-py-15 li-px-30">
<text class="li-text-30 li-font-bold li-text-#333">{{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">
<view class="house-icon li-flex-center">
<text class="ri-building-2-line li-text-60 li-text-#3e9bff"></text>
<!-- 欠费标记 -->
<view v-if="house.unpaid > 0" class="unpaid-badge">{{house.unpaid}}</view>
</view>
<view class="house-info li-mt-15">
<text class="li-text-30 li-font-bold li-text-#333 li-text-center">{{house.number}}</text>
<view class="li-flex li-items-center li-justify-center li-mt-5">
<text class="li-text-24" :class="house.unpaid > 0 ? 'li-text-#ff4d4f' : 'li-text-#52c41a'">
{{house.unpaid > 0 ? house.unpaid + '笔欠费' : '无欠费'}}
</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="floorHouseList.length === 0" class="empty-state li-py-100 li-flex-center li-flex-col">
<text class="ri-file-list-3-line li-text-100 li-text-#ccc"></text>
<text class="li-text-30 li-text-#999 li-mt-20">该单元暂无数据</text>
</view>
</view>
</view>
</wd-tab>
</wd-tabs>
</view>
</view>
<!-- 小区选择器 -->
<wd-picker v-model="showCommunityPicker" :columns="communityList" title="选择小区"
v-model:value="selectedCommunity" @confirm="handleCommunityConfirm" label-key="name" value-key="id" />
<!-- 房屋详情弹窗 -->
<wd-popup v-model="showHouseDetail" position="bottom" >
<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.period}}</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.overdueTime}}</text>
</view>
<view class="li-flex li-justify-between li-items-center li-mt-20">
<wd-button size="small" type="info" plain @click="callOwner(selectedHouse)">电话催缴</wd-button>
<wd-button size="small" type="primary" @click="sendReminder(selectedHouse)">发送通知</wd-button>
</view>
</view>
</view>
<view v-else class="empty-bills li-py-60 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>
</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';
const Toast = useToast();
// 使用导航composable
const {
hasMultiplePages,
isTabBarPage,
checkRouteStack
} = useNavigation();
// 小区列表
const communityList = ref([
[
{ id: '1', name: '九仙花苑' },
{ id: '2', name: '阳光花园小区' },
{ id: '3', name: '翠湖庭院' },
{ id: '4', name: '金色家园' }
]
]);
// 当前选中的小区
const selectedCommunity = ref('1');
const showCommunityPicker = ref(false);
// 获取当前选中的小区数据
const activeCommunity = computed(() => {
const communityData = {
'1': {
id: '1',
name: '九仙花苑',
address: '福建省-福州市-鼓楼区',
managementAddress: '东街道33号社区中心',
totalUnpaid: 3624
},
'2': {
id: '2',
name: '阳光花园小区',
address: '福建省-福州市-台江区',
managementAddress: '茶亭街道15号物业中心',
totalUnpaid: 1856
},
'3': {
id: '3',
name: '翠湖庭院',
address: '福建省-福州市-晋安区',
managementAddress: '王庄街道8号物业中心',
totalUnpaid: 2145
},
'4': {
id: '4',
name: '金色家园',
address: '福建省-福州市-仓山区',
managementAddress: '城门镇1号物业中心',
totalUnpaid: 1023
}
};
return communityData[selectedCommunity.value];
});
// 楼栋列表
const buildingList = ref([
{ id: '1', name: '1号楼' },
{ id: '2', name: '2号楼' },
{ id: '3', name: '3号楼' },
{ id: '5', name: '5号楼' },
{ id: '6', name: '6号楼' },
{ id: '7', name: '7号楼' },
{ id: '8', name: '8号楼' },
{ id: '9', name: '9号楼' },
{ id: '10', name: '10号楼' },
{ id: '11', name: '11号楼' },
{ id: '12', name: '12号楼' }
]);
// 单元列表
const unitList = ref([
{ value: 1, label: '1单元' },
{ value: 2, label: '2单元' },
{ value: 3, label: '3单元' },
{ value: 4, label: '4单元' },
{ value: 5, label: '5单元' },
{ value: 6, label: '6单元' }
]);
// 当前选中的楼栋
const activeBuilding = ref('1');
// 当前选中的单元 (使用字符串类型)
const activeUnitId = ref('1');
// 兼容原有的数字类型单元ID
const activeUnit = computed(() => parseInt(activeUnitId.value));
// 监听activeUnit变化更新floorHouseList
watch(activeUnit, (newVal) => {
console.log('activeUnit changed to', newVal);
});
// 房屋数据
const houseData = reactive({
// 1号楼 1单元数据
'1-1': [
{
floor: '1',
houses: [
{ id: '1-1-101', number: '1-1001', unpaid: 0, owner: '张先生', phone: '13812345678', bills: [] },
{ id: '1-1-102', number: '1-1002', unpaid: 3, owner: '刘女士', phone: '13812345679', bills: [
{ type: '物业费', amount: '568.00', period: '2023-07-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '水费', amount: '124.50', period: '2023-12-01 至 2023-12-31', overdueTime: '2024-01-15' }
] },
{ id: '1-1-103', number: '1-1003', unpaid: 2, owner: '陈先生', phone: '13812345680', bills: [
{ type: '电费', amount: '356.80', period: '2023-12-01 至 2023-12-31', overdueTime: '2024-01-15' }
] },
{ id: '1-1-104', number: '1-1004', unpaid: 0, owner: '林女士', phone: '13812345681', bills: [] },
{ id: '1-1-105', number: '1-1005', unpaid: 4, owner: '黄先生', phone: '13812345682', bills: [
{ type: '物业费', amount: '568.00', period: '2023-07-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '水费', amount: '124.50', period: '2023-12-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '电费', amount: '356.80', period: '2023-12-01 至 2023-12-31', overdueTime: '2024-01-15' }
] },
{ id: '1-1-106', number: '1-1006', unpaid: 1, owner: '张先生', phone: '13812345683', bills: [
{ type: '物业费', amount: '568.00', period: '2023-07-01 至 2023-12-31', overdueTime: '2024-01-15' }
] }
]
},
{
floor: '2',
houses: [
{ id: '1-1-201', number: '2-1002', unpaid: 0, owner: '李女士', phone: '13812345679', bills: [] }
]
},
{
floor: '3',
houses: [
{ id: '1-1-301', number: '3-1003', unpaid: 0, owner: '王先生', phone: '13812345680', bills: [] }
]
},
{
floor: '4',
houses: [
{ id: '1-1-401', number: '4-1004', unpaid: 6, owner: '赵女士', phone: '13812345681', bills: [
{ type: '物业费', amount: '568.00', period: '2023-07-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '水费', amount: '124.50', period: '2023-12-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '电费', amount: '356.80', period: '2023-12-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '停车费', amount: '300.00', period: '2023-11-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '垃圾处理费', amount: '45.00', period: '2023-10-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '维修基金', amount: '120.00', period: '2023-07-01 至 2023-12-31', overdueTime: '2024-01-15' }
] }
]
},
{
floor: '5',
houses: [
{ id: '1-1-501', number: '5-1005', unpaid: 0, owner: '钱先生', phone: '13812345682', bills: [] }
]
},
{
floor: '6',
houses: [
{ id: '1-1-601', number: '6-1006', unpaid: 6, owner: '孙女士', phone: '13812345683', bills: [
{ type: '物业费', amount: '568.00', period: '2023-07-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '水费', amount: '124.50', period: '2023-12-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '电费', amount: '356.80', period: '2023-12-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '停车费', amount: '300.00', period: '2023-11-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '垃圾处理费', amount: '45.00', period: '2023-10-01 至 2023-12-31', overdueTime: '2024-01-15' },
{ type: '维修基金', amount: '120.00', period: '2023-07-01 至 2023-12-31', overdueTime: '2024-01-15' }
] }
]
}
],
// 1号楼 2单元数据
'1-2': [
{
floor: '1',
houses: [
{ id: '1-2-101', number: '1-2001', unpaid: 3, owner: '周先生', phone: '13812345684', bills: [
{ type: '物业费', amount: '568.00', period: '2023-07-01 至 2023-12-31', overdueTime: '2024-01-15' }
] }
]
},
{
floor: '2',
houses: [
{ id: '1-2-201', number: '2-2002', unpaid: 0, owner: '吴女士', phone: '13812345685', bills: [] }
]
}
],
// 添加5单元和6单元的空数据
'1-4': [],
'1-5': [],
'1-6': []
});
// 获取当前楼栋单元的房屋列表
const floorHouseList = computed(() => {
const key = `${activeBuilding.value}-${activeUnit.value}`;
return houseData[key] || [];
});
// 获取当前选中的楼栋对象
const getActiveBuilding = () => {
return buildingList.value.find(item => item.id === activeBuilding.value) || { id: '', name: '' };
};
// 获取单元欠费数量
const getUnitUnpaidCount = (unit) => {
const key = `${activeBuilding.value}-${unit}`;
if (!houseData[key]) return 0;
return houseData[key].reduce((total, floor) => {
return total + floor.houses.filter(house => house.unpaid > 0).length;
}, 0);
};
// 房屋详情弹窗
const showHouseDetail = ref(false);
const selectedHouse = ref({
id: '',
number: '',
unpaid: 0,
owner: '',
phone: '',
bills: []
});
// 显示房屋详情
const showHouseDetailFunc = (house) => {
selectedHouse.value = house;
showHouseDetail.value = true;
};
// 电话催缴
const callOwner = (house) => {
uni.makePhoneCall({
phoneNumber: house.phone,
success: () => {
console.log('拨打电话成功');
},
fail: () => {
Toast.fail('拨打电话失败');
}
});
};
// 发送通知
const sendReminder = (house) => {
Toast.success('已发送催缴通知');
setTimeout(() => {
showHouseDetail.value = false;
}, 1500);
};
// 小区选择确认
const handleCommunityConfirm = (selected) => {
selectedCommunity.value = selected[0].id;
};
// 刷新数据
const handleRefresh = () => {
Toast.success('数据已刷新');
};
// 页面跳转
const toPages = (item) => {
switch (item.type) {
case 'nav':
uni.navigateBack({
delta: 1
});
break;
case 'home':
uni.switchTab({
url: '/pages/index/index'
});
break;
default:
break;
}
};
// 生命周期钩子
onLoad(() => {
checkRouteStack();
});
</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: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.community-switch {
height: 60rpx;
padding: 0 20rpx;
border-radius: 30rpx;
background-color: rgba(62, 155, 255, 0.1);
}
.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-tabs-container {
background-color: #fff;
}
.unit-selector {
background-color: #fff;
padding: 30rpx;
border-radius: 16rpx;
}
.unit-buttons {
flex-wrap: wrap;
}
.unit-button {
position: relative;
width: 140rpx;
height: 80rpx;
background-color: #f7f8fa;
border-radius: 12rpx;
transition: all 0.2s;
&-active {
background: rgba(62, 155, 255, 0.1);
border: 1px solid rgba(62, 155, 255, 0.3);
}
.unit-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
&-active {
color: #3e9bff;
font-weight: bold;
}
}
.unit-badge {
position: absolute;
top: -10rpx;
right: -10rpx;
min-width: 36rpx;
height: 36rpx;
border-radius: 18rpx;
background-color: #ff4d4f;
color: #fff;
font-size: 20rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 8rpx;
box-shadow: 0 2rpx 6rpx rgba(255, 77, 79, 0.3);
}
&:active {
opacity: 0.8;
transform: scale(0.98);
}
}
.house-list {
padding: 0 2rpx;
}
.floor-item {
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
margin-bottom: 30rpx;
.floor-header {
background-color: #f5f7fa;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.house-grid {
display: flex;
flex-wrap: wrap;
padding: 20rpx;
}
}
.house-card {
width: 25%;
padding: 8rpx;
box-sizing: border-box;
@media screen and (max-width: 768rpx) {
width: 33.33%;
}
@media screen and (max-width: 580rpx) {
width: 50%;
}
&-inner {
background-color: #fff;
border-radius: 12rpx;
padding: 15rpx;
display: flex;
flex-direction: column;
align-items: center;
border: 1px solid rgba(0, 0, 0, 0.03);
transition: all 0.2s;
height: 180rpx;
justify-content: center;
&:active {
background-color: #f9f9f9;
transform: scale(0.98);
}
}
.house-icon {
position: relative;
width: 80rpx;
height: 80rpx;
background-color: #eef6ff;
border-radius: 40rpx;
margin-bottom: 10rpx;
}
.unpaid-badge {
position: absolute;
top: -10rpx;
right: -10rpx;
min-width: 40rpx;
height: 40rpx;
border-radius: 20rpx;
background-color: #ff4d4f;
color: #fff;
font-size: 22rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10rpx;
box-shadow: 0 2rpx 8rpx rgba(255, 77, 79, 0.3);
}
.house-info {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
.li-text-30 {
font-size: 28rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
}
}
.house-detail {
height: 100%;
border-radius: 24rpx 24rpx 0 0;
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);
}
}
.empty-state {
min-height: 300rpx;
}
::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;
}
</style>