Files
property-manager-side/wuye-manager-uniapp/pages/index/index.vue
2026-08-29 14:31:54 +08:00

793 lines
19 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="content-class">
<template v-if="isLoggedIn">
<view class="bg-gradient"></view>
<!-- 页面内容区 -->
<view class="page-content">
<view class="sticky-header">
<!-- 状态栏占位 -->
<!-- <view class="status_bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
<!-- 导航栏 -->
<view class="nav-bar">
<text class="nav-title"></text>
</view>
<!-- 小区选择 -->
<view class="header-container" @click="navigateToPage">
<view class="location-area">
<view class="location-icon">
<image
src="https://wuyeadmin.bugtc.com/images/imgs/adress.png"
style="height: 100%; width: 100%"
></image>
</view>
<view class="community-selector">
<text class="community-name">{{ currentCommunity }}</text>
<view class="community-icon">
<image
src="https://wuyeadmin.bugtc.com/images/imgs/三角形.png"
style="height: 100%; width: 100%"
></image>
</view>
</view>
</view>
</view>
</view>
<!-- 可滚动内容区 -->
<view class="scroll-content">
<!-- banner -->
<view class="bannarClass" v-if="bannerList && bannerList.length > 0">
<up-swiper
:list="bannerList"
indicator
indicatorMode="dot"
radius="15"
height="180"
keyName="url"
circular
>
<template #default="{ item, index }">
<image :src="item.url || item" mode="aspectFill" class="banner-img"></image>
</template>
</up-swiper>
</view>
<!-- banner 占位 -->
<view class="bannarClass" v-else>
<view class="banner-placeholder">
<text>暂无轮播图</text>
</view>
</view>
<!-- 巡检 -->
<view class="inspectionClass">
<!-- 消息通知栏 -->
<view class="noticeClass">
<view class="notice-left">
<image
src="https://wuyeadmin.bugtc.com/images/imgs/notice.png"
class="notice-icon"
></image>
<view class="notice-line"></view>
</view>
<view class="notice-right" @click="getNoticeMore">
<uni-notice-bar
:text="noticeText"
type="line"
:showIcon="false"
show-get-more
scrollable
color="#fff"
background-color="transparent"
moreColor="#fff"
no-border
>
</uni-notice-bar>
</view>
</view>
<!-- 巡检数据统计区 -->
<view class="statistics-wrap">
<view class="statistics-item" @click="goToInspectionRecord">
<text class="num">{{ allCount }}</text>
<text class="label">全部巡检</text>
</view>
<view class="statistics-item" @click="goToDaysInspection(0)">
<text class="num">{{ pendingCount }}</text>
<text class="label">待巡检</text>
</view>
<view class="statistics-item" @click="goToDaysInspection(1)">
<text class="num">{{ InspectedCount }}</text>
<text class="label">已巡检</text>
</view>
</view>
</view>
<!-- 网格布局功能 -->
<view class="function-container">
<template v-for="item in functionList" :key="item.action">
<view
class="function-item"
@click="handleFunctionClick(item)"
v-if="permStore.hasPermi(item.permKey)"
>
<image class="item-icon" :src="item.icon" mode="aspectFit"></image>
<text class="item-text">{{ item.text }}</text>
<view
v-if="item.permKey === 'butler:workOrder:mine' && MyOrderNum > 0"
class="todoNum"
>{{ MyOrderNum > 99 ? '99' : MyOrderNum }}</view
>
</view>
</template>
</view>
</view>
</view>
</template>
<view v-else class="launch-placeholder"></view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad, onUnload, onShow, onReady, onPullDownRefresh } from '@dcloudio/uni-app'
import {
getCurrentVillage,
getTodayInspection,
getNewNoticeData,
getBannarList,
myOrderCount
} from '../../api/api.js'
import { usePermStore } from '@/stores/usePermStore'
const permStore = usePermStore()
// 登录状态:未登录时启动跳引导页
const isLoggedIn = ref(!!uni.getStorageSync('token'))
// 首次加载标记
const hasInit = ref(false)
// banner 缓存 key 和缓存时长(30分钟)
const BANNER_CACHE_KEY = 'home_banner_cache'
const BANNER_CACHE_TIME = 30 * 60 * 1000
// 列表数据状态
const currentCommunity = ref('请先绑定房屋')
const bannerList = ref([])
const noticeText = ref('暂无公告')
const allCount = ref(0) // 全部巡检
const pendingCount = ref(0) //待巡检
const InspectedCount = ref(0) //已巡检
const statusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight || 0)
// =======方法=======
// 跳转小区
const navigateToPage = () => {
const curVillageId = uni.getStorageSync('currentVillage')
let villageId = ''
if (curVillageId) {
try {
villageId = JSON.parse(curVillageId).villageId || JSON.parse(curVillageId).id || ''
} catch (e) {}
}
uni.navigateTo({
url: `/pageSubPack/notice-list/select-community?villageId=${villageId}`
})
}
// 跳转公告
const getNoticeMore = () => {
uni.navigateTo({
url: '/pageSubPack/notice-list/notice-list'
})
}
// 跳转一键开门
const goToOneOpenDoor = () => {
uni.navigateTo({
url: '/pageSubPack/oneClickOpen/oneClickOpen'
})
}
// 工单大厅
const goToTicketHall = () => {
uni.navigateTo({
url: '/pageSubPack/ticket/hall'
})
}
// 我的工单
const goToMyTicket = () => {
uni.navigateTo({
url: '/pageSubPack/ticket/myticket?MyOrderNum=' + MyOrderNum.value
})
}
// 跳转到 今日巡检
const goToDaysInspection = type => {
uni.navigateTo({
url: '/pageSubPack/inspection/toDays_inscpection?type=' + type
})
}
// 跳转到 巡检记录
const goToInspectionRecord = () => {
uni.navigateTo({
url: '/pageSubPack/inspection/inspection_record'
})
}
// 跳转到 投诉处理
const goToComplaint = () => {
uni.navigateTo({
url: '/pageSubPack/complaint/Details'
})
}
// 故障上报
const goToReport = () => {
uni.navigateTo({
url: '/pageSubPack/fault-reporting/fault-reporting'
})
}
// 跳转到仪表换新
const goToReplacement_record = () => {
uni.navigateTo({
url: '/pageSubPack/instrument/replacement_record'
})
}
// 跳转到实收统计
const goToRevenueStatistics = () => {
uni.navigateTo({
url: '/pageSubPack/instrument/revenue_statistics'
})
}
// 跳转到收费总览
const goToFeeOverview = () => {
uni.navigateTo({
url: '/pageSubPack/statistics/statistics'
})
}
// 功能列表数据
const functionList = [
{
permKey: 'butler:fault:report',
icon: 'https://wuyeadmin.bugtc.com/images/imgs/fault-report.png',
text: '故障上报',
action: 'goToReport'
},
{
permKey: 'butler:door:open',
icon: 'https://wuyeadmin.bugtc.com/images/imgs/open.png',
text: '一键开门',
action: 'goToOneOpenDoor'
},
{
permKey: 'butler:workOrder:hall',
icon: 'https://wuyeadmin.bugtc.com/images/imgs/work-order.png',
text: '工单大厅',
action: 'goToTicketHall'
},
{
permKey: 'butler:workOrder:mine',
icon: 'https://wuyeadmin.bugtc.com/images/imgs/my-work.png',
text: '我的工单',
action: 'goToMyTicket'
},
{
permKey: 'butler:complaint:handle',
icon: 'https://wuyeadmin.bugtc.com/images/imgs/complaint.png',
text: '投诉处理',
action: 'goToComplaint'
},
{
permKey: 'butler:meter:replace',
icon: 'https://wuye.ckdzkj.com/images/home/updateDevice.png',
text: '仪表换新',
action: 'goToReplacement_record'
},
{
permKey: 'butler:actualReceipt:stats',
icon: 'https://wuye.ckdzkj.com/images/imgs/revenue-statistics.png',
text: '实收统计',
action: 'goToRevenueStatistics'
},
{
permKey: 'butler:feeOverview:stats',
icon: 'https://wuye.ckdzkj.com/images/imgs/fee-overview.png',
text: '收费总览',
action: 'goToFeeOverview'
}
]
// 统一点击处理
const actionMap = {
goToReport,
goToOneOpenDoor,
goToTicketHall,
goToMyTicket,
goToComplaint,
goToReplacement_record,
goToRevenueStatistics,
goToFeeOverview
}
const handleFunctionClick = item => {
const handler = actionMap[item.action]
if (handler) handler()
}
// 获取当前小区
const currentVillageData = () => {
currentCommunity.value = '请先绑定房屋'
return getCurrentVillage()
.then(res => {
const data = res.data
if (data) {
currentCommunity.value = data.villageName
uni.setStorageSync('currentVillage', JSON.stringify(data))
getNoticeData()
}
})
.catch(() => {
currentCommunity.value = '请先绑定房屋'
})
}
// 获取今日巡检数据
const getInspectionData = () => {
return getTodayInspection()
.then(res => {
if (res.code === 200) {
if (res.data) {
allCount.value = res.data.totalTasks
pendingCount.value = res.data.unfinishedTasks
InspectedCount.value = res.data.completedTasks
} else {
allCount.value = 0
pendingCount.value = 0
InspectedCount.value = 0
}
} else {
uni.showToast({ title: res.msg || '获取失败', icon: 'none' })
}
})
.catch(err => {
uni.showToast({ title: err.msg || '获取失败', icon: 'none' })
})
}
// 获取消息通知
const getNoticeData = () => {
getNewNoticeData()
.then(res => {
const notice =
res.code === 200 && res.data
? [res.data.notice, res.data.item].filter(Boolean).join(' : ') || '暂无公告'
: '暂无公告'
// 仅在内容真正变化时才更新
if (noticeText.value !== notice) {
noticeText.value = notice
}
})
.catch(() => {})
}
// 获取bannar图
function getbannarData(forceRefresh = false) {
// 非强制刷新时,优先读缓存
if (!forceRefresh) {
try {
const cached = uni.getStorageSync(BANNER_CACHE_KEY)
if (cached) {
const { data, time } = JSON.parse(cached)
if (data && data.length > 0 && Date.now() - time < BANNER_CACHE_TIME) {
bannerList.value = data
return // 缓存有效,不发起请求
}
}
} catch (e) {}
}
return getBannarList()
.then(res => {
if (res.code === 200 && res.data) {
const newData = res.data
const oldData = bannerList.value
if (JSON.stringify(newData) !== JSON.stringify(oldData)) {
bannerList.value = newData
}
// 写入缓存
uni.setStorageSync(
BANNER_CACHE_KEY,
JSON.stringify({
data: newData,
time: Date.now()
})
)
} else {
bannerList.value = []
}
})
.catch(() => {
bannerList.value = []
})
}
const MyOrderNum = ref(0)
// 查询我的工单-待办数量
function getMyOrderNum() {
return myOrderCount()
.then(res => {
if (res.code === 200 && res.data) {
MyOrderNum.value = res.data
} else {
MyOrderNum.value = 0
}
})
.catch(() => {
MyOrderNum.value = 0
})
}
// 切换小区返回的参数
const slelctHomeData = data => {
currentCommunity.value = data.villageName
uni.setStorageSync('currentVillage', JSON.stringify(data))
getNoticeData()
getInspectionData()
getbannarData(true)
}
onShow(() => {
if (!isLoggedIn.value) return
if (!hasInit.value) {
hasInit.value = true
getbannarData()
getMyOrderNum()
permStore.initPerm() // 刷新权限缓存
return
}
getbannarData()
getMyOrderNum()
getNoticeData()
// 换账号登录后 currentVillage 缓存已被清除,需要重新获取小区
if (!uni.getStorageSync('currentVillage')) {
currentVillageData()
getInspectionData()
}
})
onLoad(() => {
// 未登录:登录过→登录页;首次下载→引导页
if (!isLoggedIn.value) {
const hasShowGuide = uni.getStorageSync('hasShowGuide')
uni.hideTabBar({ animation: false })
uni.reLaunch({
url: hasShowGuide ? '/pageSubPack/loginSub/pwd-login' : '/pages/guide/guide',
animationType: 'none',
animationDuration: 0
})
return
}
// 已登录:确保 tabBar 显示
uni.showTabBar({ animation: false })
uni.$on('confirm', slelctHomeData)
currentVillageData()
getInspectionData()
})
onReady(() => {
if (isLoggedIn.value) {
// #ifdef APP-PLUS
plus.navigator.closeSplashscreen()
// #endif
}
})
onUnload(() => {
uni.$off('confirm', slelctHomeData)
})
// 下拉刷新
onPullDownRefresh(async () => {
if (!isLoggedIn.value) {
uni.stopPullDownRefresh()
return
}
try {
await Promise.allSettled([
currentVillageData(),
getInspectionData(),
getbannarData(true),
getMyOrderNum()
])
} finally {
uni.stopPullDownRefresh()
}
})
</script>
<style lang="scss" scoped>
.todoNum {
position: absolute;
top: 0rpx;
right: 17%;
min-width: 30rpx;
height: 30rpx;
background: #f53f3f;
color: #fff;
text-align: center;
font-size: 20rpx;
line-height: 30rpx;
border-radius: 50%;
padding: 3rpx;
}
.content-class {
background-color: #f9f9f9;
min-height: 100vh;
position: relative;
}
/* 未登录启动占位:背景与引导页一致,过渡不闪烁 */
.launch-placeholder {
min-height: 100vh;
background: linear-gradient(to bottom, #e2f0ff, #f6faff);
}
/* 顶部渐变背景 - 增加高度确保覆盖粘性头部区域 */
.bg-gradient {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 600rpx;
background: repeating-linear-gradient(to right, #edf4fa, #c0e4fb);
z-index: 0;
}
/* 页面内容容器 */
.page-content {
position: relative;
z-index: 1;
padding: 0 40rpx 40rpx;
}
/* 粘性头部区域 - 滚动时固定在顶部 */
.sticky-header {
position: sticky;
top: 0;
z-index: 100;
/* 与顶部渐变背景一致,保证滚动时视觉效果连贯 */
background: repeating-linear-gradient(to right, #edf4fa, #c0e4fb);
margin: 0 -40rpx; /* 抵消父容器内边距,使背景完全延伸 */
padding: 0 40rpx;
/* 可选:添加轻微阴影增强层次感 */
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.02);
}
.status_bar {
width: 100%;
}
/* 导航栏 */
.nav-bar {
height: 88rpx;
line-height: 88rpx;
text-align: center;
}
.nav-title {
font-size: 32rpx;
font-weight: 400;
}
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4rpx 0;
}
/* 左侧区域 */
.location-area {
display: flex;
align-items: center;
position: relative;
flex: 1;
min-width: 0;
.location-icon {
width: 45rpx;
height: 45rpx;
margin-right: 12rpx;
flex-shrink: 0;
}
.community-selector {
display: flex;
align-items: center;
max-width: 300rpx;
cursor: pointer;
.community-name {
font-size: 26rpx;
font-weight: 500;
color: #000;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200rpx;
}
.community-icon {
width: 20rpx;
height: 20rpx;
margin-left: 20rpx;
margin-top: -22rpx;
}
}
}
/* 可滚动内容区域 */
.scroll-content {
/* 确保内部元素正常布局 */
width: 100%;
}
.bannarClass {
padding: 20rpx 0;
}
.banner-img {
width: 100%;
height: 100%;
border-radius: 15rpx;
}
.banner-placeholder {
height: 180px;
background: #e8f1fb;
border-radius: 15rpx;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 26rpx;
}
.inspectionClass {
background: linear-gradient(to right, #7b87fa, #4d5dfa);
border-radius: 16rpx;
box-sizing: border-box;
padding: 30rpx;
}
/* 消息通知栏 */
.noticeClass {
display: flex;
align-items: center;
height: 80rpx;
background: rgba(255, 255, 255, 0.1);
border-radius: 16rpx;
padding: 6rpx 20rpx;
margin-bottom: 30rpx;
border: 1rpx solid rgba(255, 255, 255, 0.1);
}
/* 左侧"消息通知"文字 */
.notice-left {
display: flex;
align-items: center;
margin-right: 16rpx;
.notice-icon {
width: 48rpx;
height: 48rpx;
margin-right: 15rpx;
}
.notice-line {
width: 1rpx;
height: 40rpx;
background-color: rgba(255, 255, 255, 0.4);
}
}
/* 右侧公告滚动区 */
.notice-right {
flex: 1;
}
/* 深度修改uni-notice-bar样式 */
:deep(.uni-noticebar) {
background-color: transparent !important;
height: 100% !important;
line-height: 80rpx !important;
padding: 0 !important;
margin: 0 !important;
}
/* 公告文字样式 */
:deep(.uni-noticebar__content) {
font-size: 26rpx !important;
color: #fff !important;
padding: 0 !important;
}
/* 右侧箭头按钮 */
:deep(.uni-noticebar__more) {
color: #fff !important;
font-size: 24rpx !important;
padding: 0 0 0 10rpx !important;
}
/* 隐藏默认滚动动画的多余样式 */
:deep(.uni-noticebar__content-wrapper--scrollable) {
left: 0 !important;
width: calc(100% - 30rpx) !important;
}
/* 统计区布局 */
.statistics-wrap {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20rpx;
}
/* 统计项样式 */
.statistics-item {
text-align: center;
}
.statistics-item .num {
font-size: 36rpx;
font-weight: bold;
color: #fff;
display: block;
line-height: 1;
margin-bottom: 10rpx;
}
.statistics-item .label {
font-size: 24rpx;
color: #fff;
opacity: 0.9;
}
.function-container {
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx 0;
box-sizing: border-box;
margin-top: 20rpx;
display: grid;
grid-template-columns: repeat(4, 1fr);
}
/* 功能行布局 */
.function-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
}
.function-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10rpx 0;
position: relative;
}
/* 功能图标样式 */
.item-icon {
width: 88rpx;
height: 88rpx;
margin-bottom: 10rpx;
}
/* 功能文字样式 */
.item-text {
font-size: 24rpx;
color: #333333;
font-weight: 400;
text-align: center;
}
</style>