fjrcloud-community-app/pages/index/index.vue

714 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!-- 智慧社区首页 - 严格还原蓝湖设计图 -->
<template>
<view class="home-page">
<!-- 状态栏占位 -->
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 顶部小区选择器 -->
<view class="community-selector">
<text class="community-name" @tap="showCommunityPicker">{{ communityName }}</text>
<text class="arrow-down"></text>
<!-- 未认证 -->
<text v-if="!isHouseAuth" class="not-auth" @tap="goCheck">未认证!请点击认证房屋!</text>
<!-- 已认证但未选择房屋 -->
<text v-else-if="!hasSelectedHouse" class="not-auth" @tap="goCheck">请选择房屋</text>
<!-- 已认证且已选择房屋 -->
<text v-else class="house-address" @tap="goCheck">{{ houseAddress }}</text>
</view>
<!-- Banner卡片 -->
<view class="banner-card">
<swiper
class="banner-swiper"
:indicator-dots="true"
:autoplay="true"
:interval="3000"
:circular="true"
indicator-color="rgba(255, 127, 105, 0.3)"
indicator-active-color="#FF7F69"
>
<swiper-item v-for="(item, index) in bannerList" :key="index" @tap="handleBannerTap(item)">
<view class="banner-content">
<view class="banner-text">
<text class="banner-title">{{ item.title }}</text>
<text class="banner-subtitle">{{ item.subtitle }}</text>
</view>
<image class="banner-icon" :src="item.icon" mode="aspectFit"></image>
</view>
</swiper-item>
</swiper>
</view>
<!-- 功能入口网格 4x2 -->
<view class="function-grid">
<view
class="grid-item"
v-for="(item, index) in functionList"
:key="index"
@tap="handleFunctionTap(item)"
>
<view class="icon-wrapper" :style="{ background: item.bgGradient }">
<image class="icon-img" :src="item.icon" mode="aspectFit"></image>
</view>
<text class="icon-label">{{ item.label }}</text>
</view>
</view>
<!-- 通知栏 -->
<view class="notice-bar" @tap="goNotice">
<view class="notice-tag">通知</view>
<text class="notice-text">{{ noticeTitle || '暂无通知' }}</text>
<image class="right-icon" src="/static/img/right-icon.png" mode="aspectFit" />
</view>
<!-- 法律小课堂 + 小区治理 -->
<view class="info-cards">
<view class="info-card card-purple" @tap="goLawClass">
<text class="card-title">法律小课堂</text>
<text class="card-desc">法律进小区平安伴邻里</text>
</view>
<view class="info-card card-orange" @tap="goCommunityGov">
<text class="card-title">小区治理</text>
<text class="card-desc">共治共管幸福家园</text>
</view>
</view>
<!-- 小区活动 -->
<view class="activity-section">
<view class="section-header" @tap="goActivityList">
<text class="section-title">小区活动</text>
<view class="more-icon-wrapper">
<image class="right-icon" src="/static/img/right-icon-black.png" mode="aspectFit" />
</view>
</view>
<!-- 活动卡片 -->
<view class="activity-card" @tap="goActivityDetail">
<image class="activity-cover" src="/static/img/guest.png" mode="aspectFill"></image>
<view class="activity-info">
<view class="activity-location">
<view class="location-tag">
<image class="location-img" src="/static/img/me-icon7.png" mode="aspectFit" />
</view>
<text class="location-text">福清市音西街道融侨馨苑1号楼下</text>
</view>
<view class="activity-date">
<text class="date-label">报名日期:</text>
<text class="date-value">2025/06/30-2025/07/01</text>
</view>
<view class="activity-date">
<text class="date-label">活动日期:</text>
<text class="date-value">2025/07/05 12:00-2025/07/05 18:00</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import MemberHouseApi from '@/sheep/api/community/memberHouse';
import NoticeApi from '@/sheep/api/community/notice';
import sheep from '@/sheep';
// 状态栏高度
const statusBarHeight = ref(0);
// 用户信息
const userStore = sheep.$store('user');
const userInfo = computed(() => userStore.userInfo || {});
// 小区房屋树
const communityTree = ref([]);
// 当前选中的小区名称(优先下拉选择,其次用户信息)
const selectedCommunityName = ref('');
// 当前小区名称
const communityName = computed(() => {
return selectedCommunityName.value || userInfo.value.currentCommunityName || '请选择小区';
});
// 是否已完成房屋认证(根据 currentHouseId 判断)
const isHouseAuth = computed(() => {
return !!userInfo.value.currentHouseId;
});
// 是否已选择房屋currentHouseId 不为空)
const hasSelectedHouse = computed(() => {
return !!userInfo.value.currentHouseId;
});
// 当前房屋地址
const houseAddress = computed(() => {
return userInfo.value.currentHouseAddress || '';
});
// 通知数据
const noticeTitle = ref('');
// 查询最新通知
const fetchNotice = async () => {
const { code, data } = await NoticeApi.getPage({ pageNo: 1, pageSize: 1 });
if (code === 0 && data && data.list && data.list.length > 0) {
noticeTitle.value = data.list[0].title;
}
};
// Banner轮播数据
const bannerList = ref([]);
// 查询Banner列表
const fetchBannerList = async () => {
const { code, data } = await NoticeApi.getBannerList(1);
if (code === 0 && data && data.length > 0) {
bannerList.value = data.map((item) => ({
id: item.id,
title: item.name,
subtitle: '',
icon: item.picUrl,
url: item.url,
}));
}
};
// 功能入口列表(静态配置已注释,改为接口动态获取)
/*
const functionListStatic = [
{ label: '工作计划', icon: '/static/img/Group_1.png', bgGradient: 'linear-gradient(35deg, #FF7F69 0%, #FC5A5D 100%)', path: '/pages/sub/community/dynamics' },
{ label: '业主投票', icon: '/static/img/Group_2.png', bgGradient: 'linear-gradient(35deg, #52C41A 0%, #36AD1A 100%)', path: '/pages/sub/community/daily' },
{ label: '收益公示', icon: '/static/img/Group_3.png', bgGradient: 'linear-gradient(35deg, #FFA940 0%, #FA8C16 100%)', path: '/pages/sub/income/index' },
{ label: '在线缴费', icon: '/static/img/Group_4.png', bgGradient: 'linear-gradient(35deg, #4096FF 0%, #1890FF 100%)', path: '' },
{ label: '报事报修', icon: '/static/img/Group_5.png', bgGradient: 'linear-gradient(35deg, #69B1FF 0%, #4096FF 100%)', path: '' },
{ label: '物业人员', icon: '/static/img/Group_6.png', bgGradient: 'linear-gradient(35deg, #9254DE 0%, #722ED1 100%)', path: '/pages/sub/staff/index' },
{ label: '业委会组织', icon: '/static/img/Group_7.png', bgGradient: 'linear-gradient(35deg, #7B61FF 0%, #597EF7 100%)', path: '' },
{ label: '更多服务', icon: '/static/img/Group_8.png', bgGradient: 'linear-gradient(35deg, #36CFC9 0%, #13C2C2 100%)', path: '' },
];
*/
const functionList = ref([]);
// 功能入口背景色预设(按索引循环使用)
const bgGradientPresets = [
'linear-gradient(35deg, #FF7F69 0%, #FC5A5D 100%)',
'linear-gradient(35deg, #52C41A 0%, #36AD1A 100%)',
'linear-gradient(35deg, #FFA940 0%, #FA8C16 100%)',
'linear-gradient(35deg, #4096FF 0%, #1890FF 100%)',
'linear-gradient(35deg, #69B1FF 0%, #4096FF 100%)',
'linear-gradient(35deg, #9254DE 0%, #722ED1 100%)',
'linear-gradient(35deg, #7B61FF 0%, #597EF7 100%)',
'linear-gradient(35deg, #36CFC9 0%, #13C2C2 100%)',
];
// 查询功能入口列表
const fetchFunctionList = async () => {
const { code, data } = await NoticeApi.getMiniAppConfigList(1);
if (code === 0 && data && data.length > 0) {
functionList.value = data.map((item, index) => ({
label: item.name,
icon: item.icon,
path: item.url,
bgGradient: bgGradientPresets[index % bgGradientPresets.length],
}));
}
};
onLoad(() => {
// 获取状态栏高度
const systemInfo = uni.getSystemInfoSync();
statusBarHeight.value = systemInfo.statusBarHeight || 0;
// 加载小区房屋树
fetchCommunityTree();
// 加载最新通知
fetchNotice();
// 加载Banner
fetchBannerList();
// 加载功能入口
fetchFunctionList();
});
onShow(() => {
// 每次进入首页刷新用户信息,确保小区房屋信息最新
if (userStore.isLogin) {
userStore.getInfo();
}
});
// 获取小区房屋树
const fetchCommunityTree = async () => {
const { code, data } = await MemberHouseApi.getCommunityTree();
if (code === 0 && data && data.length > 0) {
communityTree.value = data;
// 默认展示第一个小区名称
selectedCommunityName.value = data[0].communityName;
}
};
// 级联选择小区和房屋
const showCommunityPicker = () => {
if (!communityTree.value.length) {
uni.showToast({ title: '暂无小区数据', icon: 'none' });
return;
}
// 第一级:选择小区
const communityNames = communityTree.value.map((item) => item.communityName);
uni.showActionSheet({
itemList: communityNames,
success: (communityRes) => {
const selectedCommunity = communityTree.value[communityRes.tapIndex];
selectedCommunityName.value = selectedCommunity.communityName;
// 第二级:选择房屋
const houses = selectedCommunity.houses || [];
if (houses.length > 0) {
const houseNames = houses.map((item) => item.fullAddress || `${item.buildingNo}${item.unitNo}${item.roomNo}`);
uni.showActionSheet({
itemList: houseNames,
success: async (houseRes) => {
const selectedHouse = houses[houseRes.tapIndex];
// 调用切换房屋接口
const switchRes = await MemberHouseApi.switchHouse({
memberHouseId: selectedHouse.memberHouseId,
houseId: selectedHouse.houseId,
communityId: selectedCommunity.communityId,
});
if (switchRes.code === 0) {
// 切换成功后刷新用户信息
await userStore.getInfo();
uni.showToast({ title: '切换成功', icon: 'success' });
}
}
});
}
}
});
};
// 功能入口点击
const handleFunctionTap = (item) => {
const path = item.path ? item.path.trim() : '';
if (path && path !== '/') {
uni.navigateTo({ url: path });
} else {
uni.showToast({ title: `${item.label}功能开发中`, icon: 'none' });
}
};
// 通知
const goNotice = () => {
uni.navigateTo({ url: '/pages/sub/notice/list' });
};
// 跳转登录页
const goLogin = () => {
uni.navigateTo({ url: '/pages/index/login-page' });
};
const goCheck = () => {
if (!isHouseAuth.value) {
// 未认证/未选择房屋,跳转认证表单
uni.navigateTo({ url: '/pages/index/check-page' });
} else {
// 已认证且已选择房屋,跳转切换房号页面
uni.navigateTo({ url: '/pages/index/switch-house' });
}
};
// 法律小课堂
const goLawClass = () => {
uni.navigateTo({ url: '/pages/sub/knowledge/classroom?tab=0', fail: (err) => { console.error('跳转失败', err); } });
};
// 小区治理
const goCommunityGov = () => {
uni.navigateTo({ url: '/pages/sub/knowledge/classroom?tab=1', fail: (err) => { console.error('跳转失败', err); } });
};
// 活动列表
const goActivityList = () => {
uni.navigateTo({ url: '/pages/sub/activity/list' });
};
// 活动详情
const goActivityDetail = () => {
uni.navigateTo({ url: '/pages/sub/activity/detail?id=1' });
};
// Banner点击跳转
const handleBannerTap = (item) => {
if (!item.url) return;
// 外部链接用webview打开
if (item.url.startsWith('http')) {
uni.navigateTo({ url: `/pages/public/webview?url=${encodeURIComponent(item.url)}` });
} else {
// 内部页面直接跳转
uni.navigateTo({ url: item.url });
}
};
</script>
<style lang="less">
//
.home-page {
min-height: 100vh;
background-color: #F5F7FA;
padding-bottom: 120rpx;
}
//
.status-bar {
width: 100%;
background-color: #F5F7FA;
}
//
.community-selector {
display: flex;
align-items: center;
padding: 20rpx 32rpx;
.community-name {
font-size: 36rpx;
font-weight: 600;
color: #333333;
font-family: 'PingFang SC', sans-serif;
}
.arrow-down {
font-size: 20rpx;
color: #999999;
margin-left: 8rpx;
}
.not-auth{
margin-left:30rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 24rpx;
font-family: 'PingFang SC', sans-serif;
}
.house-address {
margin-left: 30rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 24rpx;
color: #666666;
font-family: 'PingFang SC', sans-serif;
}
}
// Banner
.banner-card {
height: 298rpx;
margin: 0 24rpx 24rpx;
padding: 45.8rpx 38rpx;
background-color: #FFFFFF;
border-radius: 38rpx;
overflow: hidden;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
.banner-swiper {
width: 100%;
height: 100%;
.banner-content {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
height: 100%;
.banner-text {
flex: 1;
.banner-title {
display: block;
font-size: 44rpx;
font-weight: 600;
color: #333333;
margin-bottom: 12rpx;
font-family: 'PingFang SC', sans-serif;
}
.banner-subtitle {
display: block;
font-size: 24rpx;
color: #999999;
font-family: 'PingFang SC', sans-serif;
}
}
.banner-icon {
width: 160rpx;
height: 160rpx;
}
}
}
}
//
.function-grid {
margin: 0 24rpx 24rpx;
padding: 24rpx 16rpx;
//background-color: #FFFFFF;
border-radius: 24rpx;
display: flex;
flex-wrap: wrap;
// box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.grid-item {
width: 25%;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 24rpx;
.icon-wrapper {
width: 88rpx;
height: 88rpx;
border-radius: 34rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12rpx;
.icon-img {
// width: 48rpx;
// height: 48rpx;
width: 100%;
height: 100%;
}
}
.icon-label {
font-size: 24rpx;
color: #333333;
font-family: 'PingFang SC', sans-serif;
}
}
}
//
.notice-bar {
margin: 0 24rpx 50rpx;
padding: 20rpx 24rpx;
background-color: #FFF0E5;
border-radius: 19rpx;
display: flex;
align-items: center;
.notice-tag {
height: 40rpx;
font-family: YouSheBiaoTiYuan, YouSheBiaoTiYuan;
font-weight: 400;
font-size: 31rpx;
color: #333333;
text-align: center;
font-style: normal;
text-transform: none;
flex-shrink: 0;
margin-right: 16rpx;
}
.notice-text {
flex: 1;
min-width: 0;
font-size: 26rpx;
color: #666666;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: 'PingFang SC', sans-serif;
}
.right-icon {
width: 24rpx;
height: 24rpx;
margin-left: 8rpx;
flex-shrink: 0;
}
}
// +
.info-cards {
margin: 0 24rpx 38rpx;
display: flex;
gap: 16rpx;
.info-card {
flex: 1;
padding: 22rpx;
border-radius: 16rpx;
min-height: 140rpx;
display: flex;
flex-direction: column;
// justify-content: center;
.card-title {
// font-size: 30rpx;
// font-weight: 600;
// color: #FFFFFF;
// margin-bottom: 8rpx;
// font-family: 'PingFang SC', sans-serif;
height: 36rpx;
font-family: Source Han Sans SC, Source Han Sans SC;
font-weight: 500;
font-size: 31rpx;
color: #FFFFFF;
text-align: left;
font-style: normal;
text-transform: none;
margin-bottom: 13rpx;
}
.card-desc {
// font-size: 22rpx;
// color: rgba(255, 255, 255, 0.85);
// font-family: 'PingFang SC', sans-serif;
height: 61rpx;
font-family: Source Han Sans SC, Source Han Sans SC;
font-weight: 400;
font-size: 23rpx;
color: #E8E6FF;
line-height: 34rpx;
text-align: left;
font-style: normal;
text-transform: none;
}
&.card-purple {
background: url('/static/img/home-left.png') no-repeat right bottom, linear-gradient(135deg, #9254DE 0%, #722ED1 100%);
background-size: 180rpx auto, 100% 100%;
}
&.card-orange {
background: url('/static/img/home-right.png') no-repeat right bottom, linear-gradient(135deg, #FFA940 0%, #FA8C16 100%);
background-size: 180rpx auto, 100% 100%;
}
}
}
//
.activity-section {
margin: 0 24rpx;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
font-family: 'PingFang SC', sans-serif;
}
.more-icon-wrapper {
width: 42rpx;
height: 42rpx;
background: rgba(208, 199, 194, 0.27);
border-radius: 71rpx;
display: flex;
align-items: center;
justify-content: center;
.right-icon {
width: 30rpx;
height: 30rpx;
}
}
}
.activity-card {
background-color: #FFFFFF;
border-radius: 31rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
padding: 19rpx;
.activity-cover {
width: 100%;
height: 320rpx;
border-radius: 31rpx;
}
.activity-info {
padding: 24rpx;
.activity-title {
display: block;
font-size: 30rpx;
font-weight: 600;
color: #333333;
margin-bottom: 16rpx;
font-family: 'PingFang SC', sans-serif;
}
.activity-location {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.location-tag {
width: 53rpx;
height: 59rpx;
background: #FF9F7F;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
flex-shrink: 0;
transform: skewX(-15deg);
.location-img {
width: 25rpx;
height: 25rpx;
transform: skewX(15deg);
filter: brightness(0) invert(1);
}
}
.location-text {
flex: 1;
min-width: 0;
font-size: 30rpx;
color: #666666;
font-family: 'PingFang SC', sans-serif;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.activity-date {
display: flex;
align-items: center;
margin-bottom: 12rpx;
.date-label {
font-size: 24rpx;
color: #999999;
font-family: 'PingFang SC', sans-serif;
}
.date-value {
font-size: 24rpx;
color: #666666;
font-family: 'PingFang SC', sans-serif;
}
}
}
}
}
</style>