Files
property-resident-side/property-uniapp-project/pages/index/index.vue
2026-07-29 17:47:10 +08:00

700 lines
17 KiB
Vue

<template>
<view class="content-class">
<!-- 首次加载遮罩 -->
<view class="loading-mask" v-if="!pageReady">
<view class="loading-bg"></view>
</view>
<!-- 固定头部 -->
<view class="header-fixed" v-if="pageReady">
<view class="nar-top"></view>
<view class="header-content">
<!-- 小区选择 -->
<view class="header-container" @click="navigateToPage">
<view class="location-area">
<view class="location-icon">
<image src="https://wuyeadmin.bugtc.com/images/home/adress.png"
style="height: 100%;width: 100%;"></image>
</view>
<view class="community-selector">
<text class="community-name">{{ currentCommunity }}</text>
<u-icon name="arrow-down-fill" size="16" :color="isDropdownOpen ? '#2979ff' : '#222222'"
:class="['dropdown-arrow', { 'rotate': isDropdownOpen }]"></u-icon>
</view>
</view>
</view>
</view>
</view>
<!-- 滚动内容区 -->
<scroll-view class="main-scroll" v-if="pageReady" scroll-y>
<!-- banner -->
<view class="bannarClass">
<up-swiper :key="bannerSwiperKey" :list="bannerList" indicator indicatorMode="dot" radius="10">
<template #default="{ item, index }">
<image :src="item" mode="aspectFill" class="banner-img" @tap="bannerClick(index)"></image>
</template>
</up-swiper>
</view>
<!-- 公告 -->
<view class="noticeClass" @click="getNoticeMore">
<uni-notice-bar show-icon :text="noticeText" type="line" :key="noticeKey" show-get-more scrollable
color="#000" background-color="#fff" moreColor="#000">
<template #noticebarIcon>
<image src="https://wuyeadmin.bugtc.com/images/home/notice.png"
style="width: 38px;height: 38px;"></image>
</template>
</uni-notice-bar>
</view>
<!-- 一键开门 -->
<view class="u-demo-block__content bg-card-open">
<up-row justify="space-between" gutter="10">
<up-col span="3">
<view class="demo-layout bg-purple openClass" @click="openDoorClick">
<view class="iconClass">
<image src="https://wuyeadmin.bugtc.com/images/home/openDoor.png" class="iconClass-img">
</image>
</view>
<view class="title-dome-calss">一键开门</view>
</view>
</up-col>
<up-col span="3">
<view class="demo-layout bg-purple-light openClass" @click="payMentClick">
<view class="iconClass">
<image src="https://wuyeadmin.bugtc.com/images/home/lifePay.png" class="iconClass-img">
</image>
</view>
<view class="title-dome-calss">生活缴费</view>
</view>
</up-col>
<up-col span="3">
<view class="demo-layout bg-purple openClass" @click="onlineRepairClick">
<view class="iconClass">
<image src="https://wuyeadmin.bugtc.com/images/home/repair.png" class="iconClass-img">
</image>
</view>
<view class="title-dome-calss">在线报修</view>
</view>
</up-col>
<up-col span="3">
<view class="demo-layout bg-purple-light openClass" @click="visitorClick">
<view class="iconClass">
<image src="https://wuyeadmin.bugtc.com/images/home/visitorInvite.png"
class="iconClass-img"></image>
</view>
<view class="title-dome-calss">访客邀请</view>
</view>
</up-col>
</up-row>
</view>
<!-- 广告卡片 -->
<view class="adCardClass">
<view class="u-demo-block__content">
<up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="3">
<view class="demo-layout bg-purple iconRightClass">
<image src="https://wuyeadmin.bugtc.com/images/home/ad1.png" mode=""
style="width: 100%;height: 100%;"></image>
</view>
</up-col>
<up-col span="8">
<view class="demo-layout bg-purple-light titleLeft">
<text style="color: #fff;font-size: 40rpx;font-weight: 700;">便民服务</text>
<view style="color: #fff;font-size: 28rpx;">邻里便民服务 即将上线</view>
</view>
</up-col>
</up-row>
</view>
</view>
<!-- 活动列表 -->
<uni-card class="activity-card">
<view class="eventListClass">
<view>
<text class="activity-name">活动列表</text>
</view>
<view class="nav-more">
<text class="look-more-title" @click="moreClick">查看更多</text>
</view>
</view>
<view class="activity-list">
<view class="eventListCard" v-for="(activity, index) in activityList" :key="activity.id"
@click="goToActivityDetail(activity)">
<view class="u-demo-block__content ">
<up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="4">
<view class="demo-layout bg-purple-light activity-img">
<view class="status-tag">
{{ activity.statusText }}
</view>
<image :src="activity.coverImage"
style="width: 100%;height: 100%;border-radius: 16rpx;" mode="aspectFill">
</image>
</view>
</up-col>
<up-col span="8">
<view class="demo-layout bg-purple activity-info">
<view class="activity-title">{{activity.title}}</view>
<view class="activity-time">
{{formatDate(activity.startTime)}}-{{formatDate(activity.endTime)}}</view>
<view class="activity-organizer">
<text>{{activity.createName}}</text>
</view>
<view class="activity-enrollment">
<text>
<text v-if="activity.quota !== 0">活动名额{{ activity.quota }},</text>
已报名{{activity.appliedCount}}
</text>
</view>
</view>
</up-col>
</up-row>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="activityList.length === 0" class="empty-state">
<uni-icons type="info" size="30" color="#ccc"></uni-icons>
<text class="empty-text">暂无数据</text>
</view>
</uni-card>
<!-- 底部占位 -->
<view class="scroll-bottom-space"></view>
</scroll-view>
</view>
</template>
<script setup>
import {
ref,
nextTick
} from 'vue'
import {
onLoad,
onUnload,
onShow
} from '@dcloudio/uni-app'
import moment from 'moment'
import {
getNoticeLatest,
getCurrentVillage,
getActivitylatest,
getBannarList,
getReloadUserInfo
} from "/pageSubPack/api/api.js"
// 列表数据状态
const isDropdownOpen = ref(false)
const activityList = ref([])
const currentCommunity = ref('请绑定房屋')
const currentVillageId = ref('')
const bannerList = ref([])
const bannerRawData = ref([])
const bannerSwiperKey = ref(0)
const noticeText = ref('暂无公告')
const noticeKey = ref(0)
const noticeId = ref('')
const lastUserId = ref('')
const pageReady = ref(false)
// =======工具方法=======
const requireLogin = () => {
const token = uni.getStorageSync('token')
if (!token) {
uni.showToast({ title: '请先登录', icon: 'none' })
setTimeout(() => {
uni.navigateTo({ url: '/pageSubPack/loginSub/login?mode=pwd' })
}, 1000)
return false
}
return true
}
// =======方法=======
const formatDate = (date) => {
if (!date) return ''
return moment(date).format('YYYY-MM-DD HH:mm')
}
const getNoticeMore = () => {
if (!requireLogin()) return
uni.setStorageSync('sourcePage', "index")
uni.navigateTo({ url: "/pageSubPack/notice-list/noticeList" })
}
const getbannarData = async () => {
try {
const res = await getBannarList({ villageId: uni.getStorageSync('currentVillageId') })
if (res.code === 200) {
bannerRawData.value = res.data || []
const newList = (res.data || []).map(item => item.url)
if (JSON.stringify(newList) !== JSON.stringify(bannerList.value)) {
bannerList.value = newList
await nextTick()
bannerSwiperKey.value++
}
}
} catch (err) {
// 静默失败
}
}
const bannerClick = (index) => {
if (!requireLogin()) return
const item = bannerRawData.value[index]
if (item && item.link) {
uni.navigateTo({ url: item.link })
}
}
const slelctHomeData = (data) => {
if (data.name) currentCommunity.value = data.name
if (data.villageId) currentVillageId.value = data.villageId
getCurrentCommunityData()
getNoticeData()
noticeKey.value++
getNewActivityList()
}
const getCurrentCommunityData = () => {
getCurrentVillage().then(res => {
if (res.code === 200 && res.data && res.data.villageId) {
currentCommunity.value = res.data.villageName || '未命名小区'
currentVillageId.value = res.data.villageId
uni.setStorageSync('currentVillageId', res.data.villageId)
} else {
currentCommunity.value = '请绑定房屋'
currentVillageId.value = ''
}
}).catch(err => {
uni.showToast({ title: err.msg || '网络异常', icon: 'error' })
})
}
const getNoticeData = async () => {
try {
const res = await getNoticeLatest()
if (res.code === 200) {
if (res.data) {
noticeText.value = res.data.noticeTitle || ''
noticeId.value = res.data.noticeId || ''
} else {
noticeText.value = '暂无公告'
noticeId.value = ''
}
}
} catch (err) {
noticeText.value = '暂无公告'
noticeId.value = ''
}
}
const refreshAllData = async () => {
const token = uni.getStorageSync('token') || ''
const needReload = uni.getStorageSync('needReloadUserInfo')
if (token && needReload) {
try {
const res = await getReloadUserInfo()
if (res.code === 200 && res.data) {
if (res.data.userId) uni.setStorageSync('userId', res.data.userId)
const reloadedVillageId = res.data.currentVillageId || 0
uni.setStorageSync('currentVillageId', reloadedVillageId)
}
} catch (err) {
console.error('重新加载用户信息失败', err)
} finally {
uni.removeStorageSync('needReloadUserInfo')
}
}
getCurrentCommunityData()
getNoticeData()
noticeKey.value++
getNewActivityList()
getbannarData()
pageReady.value = true
}
const resetPageData = () => {
currentCommunity.value = '请先绑定房屋'
currentVillageId.value = ''
activityList.value = []
bannerList.value = []
bannerRawData.value = []
noticeText.value = '暂无公告'
noticeId.value = ''
}
onShow(() => {
const currentUserId = uni.getStorageSync('userId') || ''
if (lastUserId.value && lastUserId.value !== currentUserId) {
resetPageData()
refreshAllData()
} else {
refreshAllData()
}
lastUserId.value = currentUserId
})
onLoad(() => {
uni.$on('confirm', slelctHomeData)
lastUserId.value = uni.getStorageSync('userId') || ''
})
onUnload(() => {
uni.$off('confirm', slelctHomeData)
})
const openDoorClick = () => {
if (!requireLogin()) return
uni.setStorageSync('sourcePage', "index")
uni.navigateTo({ url: "/pageSubPack/notice-list/oneClickOpen" })
}
const payMentClick = () => {
if (!requireLogin()) return
uni.setStorageSync('sourcePage', "index")
uni.navigateTo({ url: "/pageSubPack/payment-list/paymentIndex" })
}
const navigateToPage = () => {
const cachedVillageId = uni.getStorageSync('currentVillageId')
if (!cachedVillageId || cachedVillageId === '0' || cachedVillageId === 0) {
currentCommunity.value = '请先绑定房屋'
uni.setStorageSync('currentCommunity', '请先绑定房屋')
uni.showToast({ title: '请先绑定小区', icon: 'none', duration: 1500 })
setTimeout(() => { uni.switchTab({ url: '/pages/myIndex/myIndex' }) }, 1500)
} else {
uni.navigateTo({ url: `/pageSubPack/notice-list/selectHome?villageId=${cachedVillageId}` })
}
}
const getNewActivityList = async () => {
try {
const res = await getActivitylatest({ villageId: uni.getStorageSync('currentVillageId') })
if (res.code === 200) {
activityList.value = res.data || []
} else {
activityList.value = []
}
} catch (err) {
activityList.value = []
}
}
const onlineRepairClick = () => {
if (!requireLogin()) return
uni.setStorageSync('sourcePage', "index")
uni.navigateTo({ url: '/pageSubPack/online-repair/onlineRepair' })
}
const visitorClick = () => {
if (!requireLogin()) return
uni.setStorageSync('sourcePage', "index")
uni.navigateTo({ url: "/pageSubPack/visitor/visitor-list" })
}
const goToActivityDetail = (activity) => {
if (!requireLogin()) return
uni.navigateTo({
url: `/pageSubPack/activity-list/activityListDetails?id=${activity.id}&title=${activity.title}&applied=${activity.applied || ''}&statusText=${activity.statusText || ''}`
})
}
const moreClick = () => {
if (!requireLogin()) return
uni.setStorageSync('sourcePage', "index")
uni.navigateTo({ url: "/pageSubPack/activity-list/activityList" })
}
</script>
<style lang="scss" scoped>
.content-class {
background-color: #f9f9f9;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
/* 加载遮罩 */
.loading-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
}
.loading-bg {
width: 100%;
height: 100%;
background: repeating-linear-gradient(to right, #EDF4FA, #C0E4FB);
}
/* 固定头部 */
.header-fixed {
position: relative;
flex-shrink: 0;
.nar-top {
height: 400rpx;
width: 100%;
background: repeating-linear-gradient(to right, #EDF4FA, #C0E4FB);
position: absolute;
top: 0;
left: 0;
z-index: 0;
}
.header-content {
position: relative;
z-index: 1;
}
}
/* 滚动区域 */
.main-scroll {
flex: 1;
min-height: 0;
overflow: hidden;
position: relative;
z-index: 1;
}
.scroll-bottom-space {
height: 40rpx;
}
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 30rpx;
box-sizing: border-box;
position: relative;
z-index: 10;
}
// 左侧区域
.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;
}
}
}
.bannarClass {
position: relative;
padding: 20rpx 40rpx;
height: 300rpx;
::v-deep .u-swiper {
height: 300rpx !important;
}
::v-deep .u-swiper__wrapper {
height: 300rpx !important;
}
::v-deep .u-swiper__wrapper__item__wrapper__image {
height: 300rpx !important;
}
::v-deep .banner-img {
height: 300rpx !important;
width: 100%;
border-radius: 10rpx;
}
}
.noticeClass {
padding: 0rpx 36rpx;
margin-bottom: 20rpx;
}
::v-deep .uni-noticebar__content-wrapper--scrollable {
margin-right: 20px;
}
::v-deep .uni-noticebar {
padding: 4px 12px;
}
.bg-card-open {
padding: 20rpx;
background-color: #fff;
border-radius: 8rpx;
margin: 0 18px 20rpx;
}
.openClass {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20rpx;
.iconClass {
width: 88rpx;
height: 88rpx;
border-radius: 10rpx;
}
.iconClass-img {
width: 100%;
height: 100%;
border-radius: 8px
}
.title-dome-calss {
margin-top: 10rpx;
font-size: 28rpx;
color: #000;
}
}
.adCardClass {
height: 200rpx;
margin: 0 30rpx 20rpx;
background-color: #3E8EFF;
border-radius: 28rpx;
.titleLeft {
text-align: left;
line-height: 2;
}
.iconRightClass {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin: 36rpx;
}
}
.uni-card {
margin: 0 18px 20rpx !important;
}
.eventListClass {
margin: 10rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.activity-name {
font-size: 32rpx;
font-weight: 600;
color: #222;
}
.eventListCard {
position: relative;
margin: 30rpx 0rpx;
.activity-img {
width: 170rpx;
height: 230rpx;
position: relative;
}
.status-tag {
position: absolute;
top: 0;
left: 0;
padding: 0px 7px 10px 11px;
border-radius: 10px 0px 10px 0px;
font-size: 12px;
color: #fff;
font-weight: 500;
width: 80rpx;
height: 20rpx;
background: repeating-linear-gradient(to right, #FD7373, #FB3F3F);
z-index: 5;
}
.activity-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 12px;
line-height: 1.4;
}
.activity-time {
display: flex;
align-items: center;
font-size: 22rpx;
color: #999999;
margin-bottom: 8px;
}
.activity-organizer {
font-size: 28rpx;
display: flex;
align-items: center;
color: #999999;
margin-bottom: 8px;
}
.activity-enrollment {
display: flex;
align-items: center;
font-size: 28rpx;
margin-top: 20rpx;
color: #666666;
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
.look-more-title {
color: #3E8EFF;
font-size: 28rpx;
}
}
</style>