首页、登录页、引导页等页面

This commit is contained in:
zhouyanli
2026-04-23 17:30:38 +08:00
parent ebf9558713
commit 1252708a8e
50 changed files with 3375 additions and 194 deletions

View File

@@ -0,0 +1,219 @@
<template>
<view class="guide-page">
<view class="status_bar"></view>
<!-- 引导内容滑动容器 -->
<swiper
class="guide-swiper"
:current="currentStep"
@change="handleSwiperChange"
:disable-scroll="false"
:indicator-dots="false"
:mode="mode"
>
<!-- 便捷服务 -->
<swiper-item>
<view class="guide-item">
<view class="guide-img">
<image src="/static/imgs/guide1.png" style="width: 100%;height: 100%;"></image>
</view>
<view style="height: 200rpx;width: 100%;"></view>
<view class="guide-title">精准服务</view>
<view class="guide-subtitle">需求深度洞察,资源优化配置</view>
</view>
</swiper-item>
<!-- 高效管理 -->
<swiper-item>
<view class="guide-item">
<view class="guide-img">
<image src="/static/imgs/guide2.png" style="width: 100%;height: 100%;"></image>
</view>
<view style="height: 200rpx;width: 100%;"></view>
<view class="guide-title">高效沟通</view>
<view class="guide-subtitle">反馈即时响应,流程无缝衔接</view>
</view>
</swiper-item>
<!--安全保障 -->
<swiper-item>
<view class="guide-item">
<view class="guide-img">
<image src="/static/imgs/guide3.png" style="width: 100%;height: 100%;"></image>
</view>
<view style="height: 200rpx;width: 100%;"></view>
<view class="guide-title">智能管理</view>
<view class="guide-subtitle">数据驱动决策,风险提前预警</view>
</view>
</swiper-item>
</swiper>
<!-- 进度点 -->
<view class="progress-dots">
<view class="dot" :class="{ active: currentStep === 0 }"></view>
<view class="dot" :class="{ active: currentStep === 1 }"></view>
<view class="dot" :class="{ active: currentStep === 2 }"></view>
</view>
<!-- 操作按钮 -->
<view class="guide-btn-wrap">
<button
class="guide-btn"
:class="{ primary: currentStep === 2 }"
@click="handleBtnClick"
>
{{ currentStep === 2 ? '立即体验' : '下一步' }}
</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
// ========== 响应式数据 ==========
// 当前引导步骤(0/1/2)
const currentStep = ref(0);
const mode = ref("default")
// ========== 方法 ==========
// 滑动swiper切换步骤
const handleSwiperChange = (e) => {
currentStep.value = e.detail.current;
};
// 点击按钮操作
const handleBtnClick = () => {
// 前两步:点击下一步切换
if (currentStep.value < 2) {
currentStep.value++;
} else {
// 立即体验,跳转首页
// 1. 记录引导已完成
uni.setStorageSync('hasShowGuide', true);
// 2. 跳转首页
uni.navigateTo({
url:"/pageSubPack/loginSub/pwd-login"
})
// uni.switchTab({
// url: '/pages/index/index'
// });
}
};
</script>
<style scoped>
/* 页面容器 */
.guide-page {
width: 100%;
height: 100vh;
/* background-color: #f8f9ff; */
background: linear-gradient(to bottom, #E2F0FF, #F6FAFF);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 120rpx 40rpx 80rpx;
box-sizing: border-box;
}
.status_bar {
height: 115px;
width: 100%;
}
/* 滑动容器 */
.guide-swiper {
flex: 1;
width: 100%;
}
/* 单个引导项 */
.guide-item {
width: 360px;
height: 480px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.guide-img {
width: 77%;
height: 50%;
margin-bottom: 80rpx;
}
/* 引导标题 */
.guide-title {
font-size: 56rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
}
/* 引导副标题 */
.guide-subtitle {
font-size: 28rpx;
color: #666;
}
/* 进度点容器 */
.progress-dots {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 60rpx;
}
/* 单个进度点 */
.dot {
width: 16rpx;
height: 16rpx;
border-radius: 8rpx;
background-color: #ddd;
margin: 0 8rpx;
}
/* 激活的进度点 */
.dot.active {
background-color: #4D5DFA;
width: 40rpx;
}
/* 按钮容器 */
.guide-btn-wrap {
width: 80%;
}
/* 引导按钮 */
.guide-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
padding: 0;
margin: 0;
box-sizing: border-box;
}
/* 默认按钮样式(下一步) */
.guide-btn:not(.primary) {
background-color: #fff;
color: #4D5DFA;
border: 2rpx solid #4D5DFA;
}
/* 主按钮样式(立即体验) */
.guide-btn.primary {
background-color: #4D5DFA;
color: #fff;
border: none;
}
/* 按钮禁用态 */
.guide-btn:disabled {
background-color: #ccc;
color: #fff;
border: none;
}
</style>

View File

@@ -1,26 +1,438 @@
<template>
<view class="container">
<view class="content-class">
<view class="nar-top">
<!-- 这里是状态栏占位 -->
<view class="status_bar"></view>
<!-- 导航栏 -->
<view class="nav-bar">
<text class="nav-title">首页</text>
</view>
<view class="tabbarClass">
<view class="header-container" @click="navigateToPage">
<!-- 左侧小区选择区域 -->
<view class="location-area">
<!-- 定位图标 -->
<view class="location-icon">
<image src="/static/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="/static/imgs/三角形.png" style="height: 100%;width: 100%;"></image>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- banner -->
<view class="bannarClass">
<uni-swiper-dot class="uni-swiper-dot-box" @clickItem=clickItem :info="bannerList" :current="current"
:mode="mode">
<swiper class="swiper-box" :current="swiperDotIndex" :autoplay="true">
<swiper-item v-for="(item, index) in bannerList" :key="index">
<view class="swiper-item">
<image :src="item.img" style="width: 100%;height: 100%;"></image>
</view>
</swiper-item>
</swiper>
</uni-swiper-dot>
</view>
<!-- 巡检 -->
<view class="inspectionClass">
<!-- 消息通知栏 -->
<view class="noticeClass">
<!-- 左侧消息通知文字 -->
<view class="notice-left">
<image src="/static/imgs/notice.png" class="notice-icon"></image>
</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">
<text class="num">25</text>
<text class="label">巡检点</text>
</view>
<view class="statistics-item">
<text class="num">5</text>
<text class="label">巡检点</text>
</view>
<view class="statistics-item">
<text class="num">25</text>
<text class="label">巡检点</text>
</view>
</view>
</view>
<!-- 网格布局功能 -->
<view class="function-container">
<!-- 第一行功能:故障上报、一键开门、工单大厅、我的工单 -->
<view class="function-row">
<view class="function-item">
<image class="item-icon" src="/static/imgs/fault-report.png" mode="aspectFit"></image>
<text class="item-text">故障上报</text>
</view>
<view class="function-item" @click="goToOneOpenDoor">
<image class="item-icon" src="/static/imgs/open.png" mode="aspectFit"></image>
<text class="item-text">一键开门</text>
</view>
<view class="function-item">
<image class="item-icon" src="/static/imgs/work-order.png" mode="aspectFit"></image>
<text class="item-text">工单大厅</text>
</view>
<view class="function-item">
<image class="item-icon" src="/static/imgs/my-work.png" mode="aspectFit"></image>
<text class="item-text">我的工单</text>
</view>
</view>
<!-- 2 -->
<view class="function-row" style="margin-top: 2px;">
<view class="function-item">
<image class="item-icon" src="/static/imgs/complaint.png" mode="aspectFit"></image>
<text class="item-text">投诉处理</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import {ref} from "vue"
import {
ref
} from 'vue'
import {
onLoad,
onUnload
} from '@dcloudio/uni-app'
// 列表数据状态
const current = ref(0)
const currentCommunity = ref('碧桂园小区')
const bannerList = ref([{
img: '/static/bannar1.png'
},
{
img: '/static/bannar2.png'
},
])
const noticeText = ref('公告公告本小区今天安装了摄像头')
// =======方法=======
// 跳转小区
const navigateToPage =() =>{
uni.navigateTo({
url:"/pageSubPack/notice-list/select-community"
})
}
// 跳转公告
const getNoticeMore = () => {
uni.navigateTo({
url: "/pageSubPack/notice-list/notice-list"
})
}
// 跳转一键开门
const goToOneOpenDoor = ()=>{
uni.navigateTo({
url: "/pageSubPack/oneClickOpen/oneClickOpen"
})
}
// 接收选择小区返回的参数
const slelctHomeData = (data) => {
// console.log('收到B页面返回的数据:', data)
currentCommunity.value = data.name
}
onLoad(() => {
// 绑定全局事件监听
uni.$on('confirm', slelctHomeData)
})
// 页面销毁时移除监听(防止内存泄漏)
onUnload(() => {
uni.$off('confirm', slelctHomeData)
})
// 方法逻辑
</script>
<style scoped lang="scss">
.container{
padding: 0 12px;
// background-color: #f5f5f5;
min-height: 100vh;
}
<style lang="scss" scoped>
.content-class {
// margin: 0 20rpx;
// padding: 2rpx;
background-color: #f9f9f9;
padding-bottom: 100rpx;
box-sizing: border-box;
/* 保证padding不会撑大容器 */
min-height: 110vh;
overflow-y: auto;
.nar-top {
height: 400rpx;
width: 100%;
// background-color: #C0E4FB;
background: repeating-linear-gradient(to right, #EDF4FA, #C0E4FB);
position: absolute;
}
.status_bar {
height: 46px;
width: 100%;
}
/* 导航栏 */
.nav-bar {
height: 44px;
line-height: 44px;
text-align: center;
}
.nav-title {
font-size: 14px;
font-weight: 400;
}
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4rpx 30rpx;
box-sizing: border-box;
// border-bottom: 1rpx solid #f0f0f0;
position: absolute;
top: 85px;
left: 0;
z-index: 9999;
}
// 左侧区域
.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;
}
}
}
.bannarClass {
position: relative;
top: 110px;
left: 0;
padding: 40rpx;
}
.swiper-box {
height: 300rpx;
border-radius: 16rpx;
}
.swiper-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 300rpx;
color: #fff;
}
.inspectionClass {
position: relative;
top: 107px;
left: 0;
margin: 0 40rpx;
// height: 236rpx;
background: linear-gradient(to right, #7B87FA, #4D5DFA);
border-radius: 25rpx;
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 {
margin-right: 20rpx;
}
.notice-icon {
width: 48rpx;
height: 48rpx;
}
// 右侧公告滚动区
.notice-right {
flex: 1; // 占满剩余宽度
}
// 深度修改uni-notice-bar样式
::v-deep .uni-noticebar {
background-color: transparent !important;
height: 100% !important;
line-height: 80rpx !important;
padding: 0 !important;
margin: 0 !important;
}
// 公告文字样式
::v-deep .uni-noticebar__content {
font-size: 26rpx !important;
color: #fff !important;
padding: 0 !important;
}
// 右侧箭头按钮
::v-deep .uni-noticebar__more {
color: #fff !important;
font-size: 24rpx !important;
padding: 0 0 0 10rpx !important;
}
// 隐藏默认滚动动画的多余样式
::v-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: 8rpx;
padding: 30rpx 20rpx;
box-sizing: border-box;
position: relative;
top: 123px;
left: 0;
margin: 0 40rpx;
}
// 功能行布局
.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;
width: 25%; // 一行4个,每个占25%
padding: 10rpx 0;
// 第二行只有1个,单独调整宽度
.function-row:nth-child(2) & {
width: 25%; // 和第一行图标宽度保持一致
}
}
// 功能图标样式
.item-icon {
width: 88rpx;
height: 88rpx;
margin-bottom: 10rpx;
}
// 功能文字样式
.item-text {
font-size: 24rpx;
color: #333333;
font-weight: 400;
text-align: center;
}
}
</style>

View File

@@ -1,22 +1,217 @@
<template>
<view class="container">
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="我的" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<view class="topBox">
<view class="name">
<image src="/static/imgs/touxiang.png" class="img" />
<span style="padding: 5rpx 5rpx;display: flex;flex-direction: column;line-height: 60rpx;">
<span style="font-size: 36rpx;font-weight: 600;">
{{loginName}}
</span>
<span style="font-size: 26rpx;color:#666666;">
{{phoneNum}}
</span>
</span>
</view>
</view>
<view class="contentBox">
<div class="page-container">
<view class="list-container">
<view class="list-item" v-for="(item, index) in list" :key="index" @click="onItemClick(item)">
<image :src="item.icon" class="item-icon" />
<view class="item-content">
<text class="item-title">{{ item.title }}</text>
<span class="item-desc" v-if="item.desc"><span
style="align-self: flex-end;font-size: 24rpx;vertical-align: baseline;">{{ item.desc }}</span></span>
</view>
<view class="item-arrow"></view>
<!-- <image src="http://47.104.199.163:9090/images/propertyImgs/arrow-right.png" class="arrow-icon" /> -->
</view>
</view>
</div>
</view>
</scroll-view>
</view>
</template>
</template>
<script setup>
import {
ref
} from 'vue';
const list = ref([{
icon: "/static/imgs/shangbao.png",
title: "上报记录"
},
{
icon: "/static/imgs/xunjian.png",
title: "巡检记录",
// desc: "您可以在这里添加家人、朋友、租客~"
},
{
icon: "http://47.104.199.163:9090/images/propertyImgs/setting.png",
title: "设置"
}
])
const loginName = ref("wangh")
const phoneNum = ref('156*****1212')
// --方法----
const goToDetail = () => {
uni.navigateTo({
url: '/pageSubPack/notice-list/noticeListDetails'
})
}
// 返回
const onItemClick = (item) => {
if (item.title == '上报记录') {
uni.redirectTo({
url: '',
});
} else if (item.title == '巡检记录') {
uni.redirectTo({
url: '',
});
} else if (item.title == '设置') {
uni.redirectTo({
url: '',
});
}
}
</script>
<style lang="scss">
.container{
// padding: 0 12px;
// background-color: #f5f5f5;
<style lang="scss" scoped>
.contentStyle {
display: flex;
flex-direction: column;
min-height: 100vh;
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%)
}
.topBox {
width: 100%;
padding: 20rpx 0px;
position: relative;
// background-color: #1A75FF;
}
.titleBox {
width: 100%;
position: relative;
.title {
font-size: 36rpx;
line-height: 80rpx;
text-align: center;
font-weight: 500;
}
.settingImg {
width: 48rpx;
height: 48rpx;
top: 16rpx;
right: 0px;
}
}
.img {
width: 60px;
height: 60px;
margin-right: 10rpx;
border-radius: 50%;
}
.name {
bottom: 30rpx;
left: 30rpx;
display: flex;
align-items: center;
}
.contentBox {
position: relative;
}
.status-bar {
width: 100vw;
height: 46px;
}
.list-item {
display: flex;
align-items: center;
/* background: #fff; */
padding: 20rpx 6rpx;
border-radius: 8rpx;
/* border-bottom:2rpx solid #f2f2f2; */
/* margin-bottom: 10rpx; */
}
.item-icon {
width: 84rpx;
height: 84rpx;
/* margin-right: 20rpx; */
}
.item-content {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
}
.item-title {
font-size: 32rpx;
/* color: #333; */
font-weight: 400;
margin: 10rpx 20rpx;
/* margin-bottom: 6rpx; */
}
.item-desc {
font-size: 24rpx;
color: #b5b5b5;
white-space: nowrap;
/* 强制不换行 */
overflow: hidden;
/* 超出隐藏 */
text-overflow: ellipsis;
}
.item-arrow {
width: 16rpx;
height: 16rpx;
border-right: 3rpx solid #999999;
border-bottom: 3rpx solid #999999;
transform: rotate(-45deg);
margin-right: 10rpx;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 40rpx;
box-sizing: border-box;
}
</style>

View File

@@ -1,153 +0,0 @@
<template>
<view class="order-notice-page">
<!-- 列表 -->
<scroll-view class="order-list" scroll-y>
<!-- 单个工单项 -->
<view
class="order-item"
v-for="(item, index) in orderList"
:key="index"
@click="goToDetail(item.id)"
>
<!-- 工单标题栏(含未读红点) -->
<view class="order-header">
<text class="order-title">你有新的维修工单</text>
<view class="unread-dot" v-if="item.unread"></view>
</view>
<!-- 工单内容 -->
<view class="order-content">
<text class="content-title">{{ item.title }}</text>
<text class="content-desc">{{ item.desc }}</text>
</view>
<view class="line-class"></view>
<!-- 详情入口 -->
<view class="order-detail">
<text class="detail-text">详情</text>
<uni-icons type="right" size="14" color="#999"></uni-icons>
</view>
</view>
<!-- 空数据占位 -->
<view class="empty-tip" v-if="orderList.length === 0">
<text>暂无新的维修工单</text>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref } from 'vue'
// 模拟工单数据(可替换为接口请求)
const orderList = ref([
{
id: 1,
unread: true, // 是否未读(显示红点)
title: '标题巴拉巴拉',
desc: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。'
},
{
id: 2,
unread: false, // 已读(无红点)
title: '标题巴拉巴拉',
desc: '1栋楼下道路路灯不亮,并且有的亮度不够,请物业公司及时更换,防止发生意外事故。'
}
])
// 点击跳转工单详情页
const goToDetail = (orderId) => {
uni.navigateTo({
url: `/pages/repair-detail/repair-detail?id=${orderId}` // 替换为你的详情页路径
})
}
</script>
<style scoped lang="scss">
.order-notice-page {
min-height: 100vh;
background-color: #f5f5f5;
}
// 工单列表
.order-list {
padding: 10px 0px;
height: calc(100vh - 44px);
// 单个工单项
.order-item {
background-color: #fff;
border-radius: 8px;
padding: 15px;
margin-bottom: 10px;
position: relative;
// 工单标题栏(含未读红点)
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
.order-title {
font-size: 15px;
color: #333;
font-weight: 500;
}
// 未读红点
.unread-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #ff3b30;
}
}
// 工单内容
.order-content {
margin-bottom: 12px;
.content-title {
font-size: 14px;
color: #666;
display: block;
margin-bottom: 6px;
}
.content-desc {
font-size: 13px;
color: #999;
line-height: 1.5;
display: block;
}
}
.line-class{
border: 1px solid #d4d4d4;
}
// 详情入口
.order-detail {
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 20rpx;
.detail-text {
font-size: 13px;
color: #999;
margin-right: 4px;
}
}
}
// 空数据提示
.empty-tip {
text-align: center;
font-size: 14px;
color: #999;
margin-top: 50px;
}
}
</style>

View File

@@ -1,530 +0,0 @@
<template>
<view class="container">
<!-- 搜索区域 -->
<view class="search-container">
<view class="search-input-wrapper">
<uni-icons class="search-icon" type="search" size="20" color="#999"></uni-icons>
<input
class="search-input"
placeholder="请输入关键字进行搜索"
placeholder-class="placeholder"
v-model="searchText"
@input="handleSearchInput"
/>
<view v-if="searchText" class="clear-icon" @tap="clearSearch">
<uni-icons type="clear" size="18" color="#999"></uni-icons>
</view>
</view>
<button class="search-btn" @tap="handleSearch">搜索</button>
</view>
<!-- 小区列表 -->
<scroll-view
class="community-list"
scroll-y
:scroll-into-view="scrollIntoViewId"
@scrolltolower="loadMoreData"
:lower-threshold="100"
>
<view
v-for="item in displayedCommunities"
:key="item.id"
class="community-item"
:id="'item-' + item.id"
@tap="selectCommunity(item)"
>
<view class="community-item-left">
<uni-icons class="house-icon" type="home" size="24" color="#666"></uni-icons>
<view class="community-info">
<text class="community-name">{{ item.name }}</text>
<!-- <text class="community-address">{{ item.address }}</text> -->
<!-- <text class="community-id">ID: {{ item.id }}</text> -->
</view>
</view>
<view v-if="selectedItem && selectedItem.id === item.id" class="selected-icon">
<uni-icons type="checkbox-filled" size="24" color="#007AFF"></uni-icons>
</view>
</view>
<!-- 加载更多 -->
<view v-if="hasMoreData" class="load-more-container">
<view v-if="!isLoading" class="load-more-btn" @tap="loadMoreData">
<text>加载更多</text>
<uni-icons type="arrow-down" size="16" color="#007AFF"></uni-icons>
</view>
<view v-else class="loading-container">
<uni-icons type="spinner-cycle" size="20" color="#007AFF" class="loading-icon"></uni-icons>
<text>正在加载...</text>
</view>
<!-- <text class="page-info">第 {{ currentPage }} 页 / 共 {{ totalPages }} 页</text> -->
</view>
<!-- 没有更多数据 -->
<view v-if="!hasMoreData && displayedCommunities.length > 0" class="no-more-data">
<text>没有更多数据了</text>
</view>
<!-- 空状态 -->
<view v-if="displayedCommunities.length === 0 && !isSearching" class="empty-state">
<uni-icons type="info" size="60" color="#ccc"></uni-icons>
<text class="empty-text">暂无小区数据</text>
</view>
<!-- 搜索无结果 -->
<view v-if="displayedCommunities.length === 0 && isSearching" class="empty-state">
<uni-icons type="search" size="60" color="#ccc"></uni-icons>
<text class="empty-text">未找到相关小区</text>
<text class="empty-tip">请尝试其他关键词</text>
</view>
</scroll-view>
<!-- 确认按钮 -->
<view class="confirm-area">
<button
class="confirm-btn"
:disabled="!selectedItem"
:class="{ 'disabled-btn': !selectedItem }"
@tap="handleConfirm"
>
确认选择
</button>
<view v-if="selectedItem" class="selected-info">
已选: {{ selectedItem.name }}
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
// 响应式数据
const searchText = ref('')
const selectedItem = ref(null)
const scrollIntoViewId = ref('')
// 分页相关
const currentPage = ref(1)
const pageSize = 5
const totalCommunities = ref([])
const displayedCommunities = ref([])
const hasMoreData = ref(true)
const isLoading = ref(false)
// 搜索状态
const isSearching = ref(false)
let searchTimeout = null
// 所有小区数据
const allCommunities = ref([
{ id: 1, name: '北境碧桂园小区', address: '北境市光明区碧桂路1号' },
{ id: 2, name: '南湖花园小区', address: '南湖市滨湖区湖滨路28号' },
{ id: 3, name: '东城国际社区', address: '东城市新城区人民路156号' },
{ id: 4, name: '西湖雅苑', address: '西湖市风景区梅灵路88号' },
{ id: 5, name: '北境锦绣家园', address: '北境市朝阳区朝阳路56号' },
{ id: 6, name: '阳光100小区', address: '阳光市高新区创业路100号' },
{ id: 7, name: '绿城玫瑰园', address: '绿城市西湖区学院路188号' },
{ id: 8, name: '金地自在城', address: '金城市江干区金沙大道200号' },
{ id: 9, name: '万科城市花园', address: '万城市余杭区文一西路299号' },
{ id: 10, name: '保利中央公园', address: '保利市拱墅区莫干山路500号' },
{ id: 11, name: '龙湖天街小区', address: '龙湖市滨江区江南大道100号' },
])
// 计算属性
const totalPages = computed(() => {
return Math.ceil(totalCommunities.value.length / pageSize)
})
// 方法
// 加载分页数据
const loadPageData = (pageNum) => {
isLoading.value = true
// 模拟网络请求延迟
setTimeout(() => {
const startIndex = (pageNum - 1) * pageSize
const endIndex = startIndex + pageSize
const pageData = totalCommunities.value.slice(startIndex, endIndex)
if (pageNum === 1) {
displayedCommunities.value = pageData
} else {
displayedCommunities.value = [...displayedCommunities.value, ...pageData]
}
currentPage.value = pageNum
hasMoreData.value = displayedCommunities.value.length < totalCommunities.value.length
isLoading.value = false
// 滚动到新加载的内容
if (pageNum > 1 && pageData.length > 0) {
setTimeout(() => {
scrollIntoViewId.value = 'item-' + pageData[0].id
}, 100)
}
}, 500)
}
// 加载更多数据
const loadMoreData = () => {
if (isLoading.value || !hasMoreData.value) return
loadPageData(currentPage.value + 1)
}
// 处理搜索输入
const handleSearchInput = () => {
// 防抖处理
if (searchTimeout) {
clearTimeout(searchTimeout)
}
searchTimeout = setTimeout(() => {
handleSearch()
}, 300)
}
// 处理搜索
const handleSearch = () => {
const keyword = searchText.value.trim().toLowerCase()
if (!keyword) {
isSearching.value = false
totalCommunities.value = [...allCommunities.value]
loadPageData(1)
return
}
isSearching.value = true
totalCommunities.value = allCommunities.value.filter(community =>
community.name.toLowerCase().includes(keyword) ||
community.address.toLowerCase().includes(keyword)
)
loadPageData(1)
}
// 清空搜索
const clearSearch = () => {
searchText.value = ''
isSearching.value = false
totalCommunities.value = [...allCommunities.value]
loadPageData(1)
}
// 选择小区
const selectCommunity = (item) => {
selectedItem.value = item
}
// 确认选择
const handleConfirm = () => {
if (!selectedItem.value) {
uni.showToast({
title: '请先选择小区',
icon: 'none',
duration: 2000
})
return
}
uni.showModal({
title: '确认选择',
content: `您已选择: ${selectedItem.value.name}\n地址: ${selectedItem.value.address}`,
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '选择成功',
icon: 'success',
duration: 1500
})
// 在实际应用中,这里可以触发回调或页面跳转
// uni.navigateBack() 或 emit('confirm', selectedItem.value)
uni.$emit('confirm', selectedItem.value)
// 模拟2秒后返回上一页
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
}
})
}
// 监听搜索文本变化
watch(searchText, (newVal) => {
if (!newVal.trim()) {
clearSearch()
}
})
// 初始化
onMounted(() => {
// 初始化数据
totalCommunities.value = [...allCommunities.value]
loadPageData(1)
// 默认选中第一项
if (displayedCommunities.value.length > 0) {
selectedItem.value = displayedCommunities.value[0]
}
})
</script>
<style scoped>
/* 页面容器 */
.container {
min-height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
/* padding: 20rpx; */
}
/* 搜索区域 */
.search-container {
padding: 20rpx 30rpx;
background-color: #ffffff;
display: flex;
align-items: center;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
z-index: 10;
position: sticky;
top: 0;
}
.search-input-wrapper {
flex: 1;
background-color: #f8f8f8;
border-radius: 10rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
height: 80rpx;
margin-right: 20rpx;
}
.search-icon {
margin-right: 15rpx;
}
.search-input {
flex: 1;
height: 100%;
font-size: 28rpx;
color: #333;
}
.placeholder {
color: #999;
font-size: 28rpx;
}
.clear-icon {
padding: 10rpx;
display: flex;
align-items: center;
justify-content: center;
}
.search-btn {
background-color: #007AFF;
color: #fff;
font-size: 28rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 10rpx;
padding: 0 30rpx;
margin: 0;
white-space: nowrap;
}
/* 小区列表 */
.community-list {
flex: 1;
padding: 0 30rpx;
margin-top: 20rpx;
margin-bottom: 180rpx;
}
.community-item {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
transition: all 0.2s;
}
.community-item:active {
background-color: #f9f9f9;
transform: translateY(2rpx);
}
.community-item-left {
display: flex;
align-items: center;
flex: 1;
}
.house-icon {
margin-right: 20rpx;
}
.community-info {
display: flex;
flex-direction: column;
flex: 1;
}
.community-name {
font-size: 32rpx;
color: #333;
font-weight: 500;
margin-bottom: 8rpx;
}
.community-address {
font-size: 24rpx;
color: #999;
margin-bottom: 5rpx;
}
.community-id {
font-size: 22rpx;
color: #ccc;
}
.selected-icon {
margin-left: 20rpx;
}
/* 加载更多容器 */
.load-more-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
color: #999;
}
.load-more-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 40rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.load-more-btn text {
margin-right: 10rpx;
}
.loading-container {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 40rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.loading-icon {
margin-right: 10rpx;
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.page-info {
font-size: 24rpx;
color: #999;
}
/* 没有更多数据 */
.no-more-data {
text-align: center;
padding: 40rpx 0;
color: #999;
font-size: 28rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
.empty-tip {
font-size: 24rpx;
color: #ccc;
}
/* 确认区域 */
.confirm-area {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 30rpx 40rpx;
background-color: #fff;
box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.05);
}
.confirm-btn {
background-color: #007AFF;
color: #fff;
height: 100rpx;
line-height: 100rpx;
border-radius: 12rpx;
font-size: 34rpx;
width: 100%;
margin: 0;
}
.disabled-btn {
background-color: #ccc;
color: #fff;
}
.selected-info {
text-align: center;
font-size: 24rpx;
color: #666;
margin-top: 20rpx;
padding: 10rpx;
background-color: #f8f8f8;
border-radius: 8rpx;
}
/* 适配不同屏幕 */
@media (min-width: 768px) {
.container {
max-width: 500px;
margin: 0 auto;
}
}
</style>