first commit

This commit is contained in:
zhouyanli
2026-04-18 08:31:55 +08:00
commit eeb09abc04
989 changed files with 137562 additions and 0 deletions

View File

@@ -0,0 +1,356 @@
<template>
<!-- 通知列表 -->
<scroll-view
class="notice-list"
scroll-y
:refresher-enabled="true"
:refresher-triggered="refreshing"
:style="{ height: `calc(100vh - ${statusBarHeight + navBarHeight}px)` }"
>
<!-- 下拉刷新 -->
<view class="refresh-container" v-if="refreshing">
<uni-icons type="spinner-cycle" size="20" color="#999" class="loading"></uni-icons>
<text class="refresh-text">刷新中...</text>
</view>
<!-- 通知列表项 -->
<view
v-for="(notice, index) in noticeList"
:key="notice.id"
class="notice-item"
@click="goToDetail(notice)"
>
<!-- 通知标题 -->
<view class="notice-header">
<view class="notice-title">
{{notice.title}}
</view>
<view class="unread-dot" v-if="notice.hasRead"></view>
</view>
<!-- 红点 -->
<!-- 发布者和时间 -->
<view class="notice-meta">
<view class="notice-author">
<text class="author-name">{{notice.author}}</text>
<text class="separator">·</text>
<text class="publish-time">{{notice.publishTime}}</text>
</view>
</view>
<!-- 通知摘要 -->
<view class="notice-summary">
{{ notice.summary }}
</view>
<!-- 详情 -->
<view class="notice-actions" @click.stop="goToDetail(notice)">
<!-- <view class="read-status" :class="{ unread: !notice.hasRead }">
{{ notice.hasRead ? '已读' : '未读' }}
</view> -->
<view class="" >
<text style="color: #2F77FD;font-size:14px">详情 </text>
</view>
<!-- <button class="detail-btn" @click.stop="goToDetail(notice)"> -->
<!-- <text>详情 </text> -->
<uni-icons type="right" size="13" color="#007AFF"></uni-icons>
<!-- </button> -->
</view>
</view>
<!-- 加载更多 -->
<view v-if="loadingMore" class="load-more">
<uni-icons type="spinner-cycle" size="18" color="#999" class="loading"></uni-icons>
<text>加载中...</text>
</view>
<!-- 没有更多数据 -->
<view v-if="noMoreData && noticeList.length > 0" class="no-more">
没有更多通知了
</view>
<!-- 空状态 -->
<view v-if="noticeList.length === 0 && !loading" class="empty-state">
<image src="/static/empty-notice.png" mode="aspectFit" class="empty-image"></image>
<text class="empty-text">暂无通知</text>
<button class="btn-refresh" @click="loadNotices">刷新试试</button>
</view>
</scroll-view>
</template>
<script setup>
import { ref } from 'vue';
const noticeList = ref([
{
id:'1',
title:'我那我是咖啡牛奶附件说的都是vv的v给v梵蒂冈v辅导报告发布',
author:'张三',
publishTime:'2026.3.5',
summary:'为保障社区居民生命财产安全,维护社区和谐稳定,根据上级部门要求,结合我社区实际情况,现就进一步加强社区安全管理工作通知如下',
hasRead:true
},
{
id:'2',
title:'今天停水了',
author:'张三',
publishTime:'2026.3.5',
summary:'为保障社区居民生命财产安全,维护社区和谐稳定,根据上级部门要求,结合我社区实际情况,现就进一步加强社区安全管理工作通知如下',
hasRead:true
}
])
const refreshing = ref(false)
const loadingMore = ref(false)
const noMoreData = ref(false)
const pageSize = 10
const statusBarHeight = ref(0)
const navBarHeight = 44
// --方法----
const goToDetail = () =>{
uni.navigateTo({
url:'/pages/notice-list/noticeListDetails'
})
}
</script>
<style lang="scss">
/* 通知列表容器 */
.notice-list {
padding: 12px 12px 20px;
box-sizing: border-box;
background-color: #f9f9f9;
}
/* 下拉刷新 */
.refresh-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px 0;
color: #999;
font-size: 14px;
.loading {
animation: rotate 1s linear infinite;
margin-bottom: 8px;
}
.refresh-text {
font-size: 12px;
}
}
/* 通知项样式 */
.notice-item {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
position: relative;
&:active {
background-color: #f9f9f9;
}
/* 重要通知标记 */
&.important::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: #ff6b6b;
border-radius: 2px 0 0 2px;
}
}
.notice-header{
display: flex;
justify-content: space-between;
align-items: center;
}
/* 通知标题 */
.notice-title {
font-size: 16px;
font-weight: bold;
color: #333;
line-height: 1.4;
margin-bottom: 12px;
// display: -webkit-box;
// -webkit-box-orient: vertical;
// -webkit-line-clamp: 2;
width: 290px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 未读红点
.unread-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #ff3b30;
}
/* 元信息 */
.notice-meta {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
color: #666;
font-size: 12px;
.notice-author {
display: flex;
align-items: center;
.author-name {
color: #666;
}
.separator {
margin: 0 6px;
color: #ccc;
}
.publish-time {
color: #999;
}
}
.notice-time {
display: flex;
align-items: center;
.date-text {
margin-left: 4px;
color: #999;
}
}
}
/* 通知摘要 */
.notice-summary {
font-size: 14px;
color: #666;
background-color: #f9f9f9;
line-height: 1.5;
margin-bottom: 16px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
}
/* 操作区域 */
.notice-actions {
display: flex;
align-items: center;
justify-content: space-between;
.read-status {
font-size: 12px;
padding: 2px 8px;
border-radius: 10px;
background-color: #f0f0f0;
color: #999;
&.unread {
background-color: #e6f7ff;
color: #1890ff;
}
}
.detail-btn {
display: flex;
align-items: flex-end;
background: transparent;
border: none;
color: #007AFF;
font-size: 14px;
padding: 0;
margin-right: 10rpx;
&::after {
border: none;
}
text {
margin-right: 4px;
}
}
}
/* 分割线 */
// .divider {
// height: 1px;
// background-color: #f0f0f0;
// margin-top: 16px;
// }
/* 加载更多 */
.load-more {
display: flex;
align-items: center;
justify-content: center;
padding: 20px 0;
color: #999;
font-size: 14px;
.loading {
animation: rotate 1s linear infinite;
margin-right: 8px;
}
}
.no-more {
text-align: center;
padding: 20px 0;
color: #999;
font-size: 14px;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
.empty-image {
width: 200px;
height: 150px;
margin-bottom: 20px;
}
.empty-text {
font-size: 16px;
color: #999;
margin-bottom: 20px;
}
.btn-refresh {
background-color: #007AFF;
color: #fff;
padding: 8px 20px;
border-radius: 20px;
font-size: 14px;
&::after {
border: none;
}
}
}
</style>

View File

@@ -0,0 +1,494 @@
<template>
<view class="container">
<!-- 内容区域 -->
<!-- <scroll-view class="content-scroll" scroll-y> -->
<!-- 文章标题 -->
<view class="article-header">
<view class="article-title">
以实干担当不断开创中国特色大国外交新局面
</view>
</view>
<!-- 作者和日期 -->
<view class="notice-meta">
<view class="notice-author">
<text class="author-name">张三</text>
<text class="separator">·</text>
<text class="publish-time">2026.3.6</text>
</view>
</view>
<!-- 文章内容 -->
<view class="article-content">
<view class="paragraph">
学习贯彻习近平新时代中国特色社会主义思想主题教育开展以来外交外事战线认真学习贯彻习近平总书记重要讲话精神和党中央部署要求
全面准确把握目标要求紧密联系实际精心组织周密部署紧密结合外交中心工作开展主题教育
坚持学思用贯通知信行统一把习近平新时代中国特色社会主义思想转化为坚定理想锤炼党性和指导实践
推动工作的强大力量以实干担当不断开创中国特色大国外交新局面
</view>
<view class="articleimg">
<image src="/static/bannar1.png" mode="" style="width: 100%;height: 100%;border-radius: 20rpx;"></image>
<image src="/static/bannar1.png" mode="" style="width: 100%;height: 100%;border-radius: 20rpx;"></image>
</view>
</view>
<!-- 附件区域 -->
<!-- <view class="attachments-section">
<view class="section-title">
<view class="title-text">相关附件</view>
<view class="title-line"></view>
</view>
<view class="attachments-list"> -->
<!-- DOC文件 -->
<!-- <view class="attachment-item" @click="downloadFile('filename.DOC')">
<view class="attachment-left">
<view class="file-icon doc-icon">📄</view>
<view class="file-info">
<view class="file-name">文件名.DOC</view>
<view class="file-size">299kb</view>
</view>
</view>
<view class="download-btn">
<view class="download-icon"></view>
</view>
</view> -->
<!-- PDF文件 -->
<!-- <view class="attachment-item" @click="downloadFile('filename.PDF')">
<view class="attachment-left">
<view class="file-icon pdf-icon">📄</view>
<view class="file-info">
<view class="file-name">文件名.PDF</view>
<view class="file-size">299kb</view>
</view>
</view>
<view class="download-btn">
<view class="download-icon"></view>
</view>
</view>
</view>
</view> -->
<!-- </scroll-view> -->
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// import { onLoad, onShow } from '@dcloudio/uni-app'
const isLoading = ref(false)
// 下载文件
const downloadFile = (fileName) => {
uni.showModal({
title: '下载确认',
content: `确定要下载 "${fileName}" 吗?`,
success: (res) => {
if (res.confirm) {
isLoading.value = true
// 显示下载进度
uni.showLoading({
title: '下载中...',
mask: true
})
// 模拟下载过程
setTimeout(() => {
uni.hideLoading()
isLoading.value = false
// 根据文件类型打开文件
if (fileName.endsWith('.PDF')) {
uni.showToast({
title: 'PDF文件已保存到下载目录',
icon: 'success',
duration: 2000
})
// 在真实项目中这里应该调用下载API
// uni.downloadFile({
// url: 'https://example.com/files/filename.PDF',
// success: (res) => {
// if (res.statusCode === 200) {
// uni.saveFile({
// tempFilePath: res.tempFilePath,
// success: (saveRes) => {
// uni.showToast({ title: '下载成功', icon: 'success' })
// }
// })
// }
// }
// })
} else if (fileName.endsWith('.DOC')) {
uni.showToast({
title: 'DOC文件已保存到下载目录',
icon: 'success',
duration: 2000
})
}
}, 1500)
}
}
})
}
</script>
<style lang="scss" scoped>
.container {
min-height: 100vh;
background-color: #ffffff;
// font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
/* 内容滚动区域 */
.content-scroll {
padding-top: 15px;
box-sizing: border-box;
background-color: #ffffff;
}
/* 文章头部 */
.article-header {
// padding: 24px 20px 20px;
// border-bottom: 1px solid #f0f0f0;
.article-title {
font-size: 18px;
font-weight: bold;
color: #333333;
line-height: 1.4;
margin-bottom: 16px;
text-align: center;
}
.article-meta {
display: flex;
align-items: center;
justify-content: center;
color: #666666;
font-size: 14px;
.meta-item {
display: flex;
align-items: center;
.meta-icon {
font-size: 14px;
margin-right: 4px;
}
.meta-text {
font-size: 14px;
}
}
.meta-divider {
margin: 0 12px;
color: #cccccc;
}
}
}
/* 元信息 */
.notice-meta {
display: flex;
align-items: center;
justify-content: center;
// margin-bottom: 12px;
color: #666;
font-size: 16px;
margin: 0 30rpx;
.notice-author {
display: flex;
align-items: center;
.author-name {
color: #666;
}
.separator {
margin: 0 6px;
color: #ccc;
}
.publish-time {
color: #999;
}
}
.notice-time {
display: flex;
align-items: center;
.date-text {
margin-left: 4px;
color: #999;
}
}
}
/* 文章内容 */
.article-content {
padding: 20px;
color: #333333;
line-height: 1.8;
font-size: 16px;
.articleimg{
width: 100%;
height: 249rpx;
}
.paragraph {
margin-bottom: 20px;
text-align: justify;
text-indent: 2em;
}
.bullet-list {
margin: 20px 0 20px 2em;
.bullet-item {
display: flex;
margin-bottom: 12px;
.bullet-dot {
color: #666666;
margin-right: 8px;
flex-shrink: 0;
}
.bullet-text {
flex: 1;
color: #333333;
}
}
}
}
/* 附件区域 */
.attachments-section {
padding: 20px;
background-color: #f9f9f9;
border-top: 1px solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
margin-top: 20px;
.section-title {
display: flex;
align-items: center;
margin-bottom: 20px;
.title-text {
font-size: 18px;
font-weight: 600;
color: #333333;
white-space: nowrap;
margin-right: 12px;
}
.title-line {
flex: 1;
height: 1px;
background-color: #e0e0e0;
}
}
.attachments-list {
.attachment-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
background-color: #ffffff;
border-radius: 8px;
margin-bottom: 12px;
border: 1px solid #e8e8e8;
&:active {
background-color: #f5f5f5;
}
&:last-child {
margin-bottom: 0;
}
.attachment-left {
display: flex;
align-items: center;
flex: 1;
.file-icon {
font-size: 24px;
margin-right: 12px;
&.doc-icon {
color: #2b579a; /* Word文档蓝色 */
}
&.pdf-icon {
color: #e74c3c; /* PDF红色 */
}
}
.file-info {
.file-name {
font-size: 16px;
color: #333333;
margin-bottom: 4px;
font-weight: 500;
}
.file-size {
font-size: 13px;
color: #666666;
}
}
}
.download-btn {
width: 40px;
height: 40px;
border-radius: 20px;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
.download-icon {
font-size: 18px;
color: #333333;
}
}
}
}
}
/* 页面底部 */
.page-footer {
padding: 30px 20px 40px;
display: flex;
flex-direction: column;
align-items: center;
.footer-text {
font-size: 16px;
color: #999999;
margin-bottom: 20px;
}
.footer-info {
display: flex;
align-items: center;
font-size: 13px;
color: #999999;
.info-item {
padding: 0 8px;
}
.info-divider {
color: #dddddd;
}
}
}
/* 响应式调整 */
@media (min-width: 768px) {
.container {
max-width: 768px;
margin: 0 auto;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.article-header,
.article-content,
.attachments-section,
.page-footer {
padding-left: 40px;
padding-right: 40px;
}
}
/* 暗色模式适配 */
@media (prefers-color-scheme: dark) {
.container {
background-color: #1a1a1a;
}
.custom-navbar {
background-color: #1a1a1a;
border-bottom-color: #333333;
.nav-back,
.nav-title {
color: #ffffff;
}
.nav-actions .action-icon {
color: #ffffff;
}
}
.content-scroll {
background-color: #1a1a1a;
}
.article-header {
border-bottom-color: #333333;
.article-title {
color: #ffffff;
}
.article-meta {
color: #999999;
}
}
.article-content {
color: #e0e0e0;
.bullet-item .bullet-text {
color: #e0e0e0;
}
}
.attachments-section {
background-color: #2a2a2a;
border-top-color: #333333;
border-bottom-color: #333333;
.section-title .title-text {
color: #ffffff;
}
.attachment-item {
background-color: #2a2a2a;
border-color: #333333;
&:active {
background-color: #333333;
}
.file-info .file-name {
color: #ffffff;
}
.file-info .file-size {
color: #999999;
}
}
}
.page-footer {
.footer-text,
.footer-info {
color: #666666;
}
}
}
</style>

View File

@@ -0,0 +1,241 @@
<template>
<!-- 页面主容器 -->
<view class="door-open-page">
<view class="status_bar"></view>
<!-- 顶部标题栏 -->
<view class="top-bar">
<uni-nav-bar left-icon="left" title="远程开门" backgroundColor ="none" :border="false" @clickLeft="narBack" />
</view>
<view class="top-img">
<image src="/static/一键开门/远程开门.png" mode="" style="width: 100%;height: 100%;"></image>
</view>
<!-- 提示文案 -->
<view class="tips-text">
远程开门
<text class="tips-desc">请选择要开的门并保持距离在10米以内单次仅开一门</text>
</view>
<!-- 开门成功提示默认隐藏 -->
<view v-if="showSuccess" class="success-toast">开门成功</view>
<!-- 开门授权区域 -->
<view class="auth-container">
<view class="auth-title">开门授权</view>
<!-- 网格布局2行2列 -->
<view class="grid-container">
<!-- 每个门选项 -->
<view
v-for="(item, index) in doorList"
:key="index"
:class="['door-item', { active: selectedDoor === index }]"
@click="selectDoor(index)"
>
<!-- 图标占位可替换为真实图标/图片 -->
<view class="door-icon">
<image class="icon-text" :src="selectedDoor === index ? item.iconActive:item.iconText"></image>
</view>
<!-- 门名称 -->
<text class="door-name">{{ item.name }}</text>
</view>
</view>
</view>
<!-- 确认开门按钮 -->
<button class="confirm-btn" @click="openDoor" :disabled="selectedDoor === -1">确认开门</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
// 响应式数据:选中的门索引、开门成功提示
const selectedDoor = ref(-1) // -1表示未选中
const showSuccess = ref(false)
// 门列表数据(和设计图一致)
const doorList = ref([
{ iconText: '/static/一键开门/西入门.png',iconActive:"/static/一键开门/西入门-白.png", name: '西入门闸机' },
{ iconText: '/static/一键开门/东入门.png',iconActive:"/static/一键开门/东入门-白.png", name: '东入门闸机' },
{ iconText: '/static/一键开门/单元门.png',iconActive:"/static/一键开门/单元门-白.png", name: '1号楼1单元门' },
{ iconText: '/static/一键开门/单元门.png',iconActive:"/static/一键开门/单元门-白.png", name: '1号楼2单元门' }
])
// 选择门的方法
const selectDoor = (index) => {
selectedDoor.value = index
showSuccess.value = false // 选择新门时隐藏成功提示
}
// 返回
const narBack = () =>{
// uni.navigateBack({
// delta: 1
// })
uni.switchTab({
url:"/pages/index/index"
})
}
// 确认开门方法
const openDoor = () => {
// 校验:未选择门时提示
// if (selectedDoor.value === -1) {
// uni.showToast({
// title: '请先选择要开的门',
// icon: 'none'
// })
// return
// }
// 模拟开门接口请求(实际项目替换为真实接口)
setTimeout(() => {
showSuccess.value = true
// 可选:添加成功提示后自动隐藏
// setTimeout(() => showSuccess.value = false, 2000)
}, 500)
}
</script>
<style scoped>
/* 页面全局样式 */
.door-open-page {
background: linear-gradient(180deg, #4a90e2, #F6FAFF);
min-height: 100vh;
padding: 30rpx 26rpx;
box-sizing: border-box;
}
.status_bar {
height: 32px;
width: 100%;
}
/* 顶部标题 */
.top-bar {
text-align: center;
margin-bottom: 20rpx;
}
.title-text {
font-size: 48rpx;
color: #ffffff;
font-weight: bold;
}
.top-img{
width: 460rpx;
height: 290rpx;
float: right;
}
/* 提示文案 */
.tips-text {
color: #000;
margin-bottom: 30rpx;
font-size: 68rpx;
line-height: 1.5;
font-weight: 700;
font-style: italic;
clear: both;
position: relative;
top: -86px;
left: 0;
}
.tips-desc {
display: block;
font-size: 24rpx;
opacity: 0.9;
color: #666666;
}
/* 开门成功提示 */
.success-toast {
background: rgba(0, 0, 0, 0.7);
color: #ffffff;
padding: 10rpx 20rpx;
border-radius: 30rpx;
font-size: 24rpx;
position: absolute;
top: 200rpx;
left: 50%;
transform: translateX(-50%);
z-index: 99;
}
/* 开门授权容器 */
.auth-container {
background: #ffffff;
border-radius: 20rpx;
padding: 30rpx 20rpx;
margin-bottom: 60rpx;
position: relative;
top: -86px;
left: 0;
}
.auth-title {
text-align: center;
font-size: 32rpx;
color: #333333;
padding-bottom: 20rpx;
border-bottom: 1px solid #e5e5e5;
margin-bottom: 30rpx;
}
/* 网格布局 */
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30rpx;
}
/* 门选项样式 */
.door-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 30rpx 20rpx;
border-radius: 10rpx;
border: 2px solid #e5e5e5;
transition: all 0.3s;
}
/* 选中状态 */
.door-item.active {
/* border-color: #2c6bc7; */
background-color: #2F77FD;
color: #ffffff;
}
/* 门图标 */
.door-icon {
width: 80rpx;
height: 80rpx;
/* background-color: #f5f5f5; */
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15rpx;
}
.icon-text {
/* font-size: 40rpx; */
width: 100%;
height: 100%;
}
/* 门名称 */
.door-name {
font-size: 26rpx;
color: #333333;
}
/* 确认按钮 */
.confirm-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #ffffff;
color: #2c6bc7;
font-size: 32rpx;
border-radius: 20rpx;
border: none;
}
.confirm-btn::after {
border: none; /* 去除uni-app默认按钮边框 */
}
</style>

View File

@@ -0,0 +1,545 @@
<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">
<image src="/static/首页/小区.png" style="width: 60rpx;height: 60rpx;"></image>
<!-- <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号' },
// { id: 12, name: '华润幸福里', address: '华润市萧山区市心北路200号' },
// { id: 13, name: '融创壹号院', address: '融创市余杭区文二西路300号' },
// { id: 14, name: '中海国际社区', address: '中海市西湖区天目山路400号' },
// { id: 15, name: '世茂江滨花园', address: '世茂市江干区钱江路500号' },
// { id: 16, name: '恒大御景湾', address: '恒大市拱墅区湖墅南路600号' },
// { id: 17, name: '碧桂园凤凰城', address: '碧桂园市滨江区滨康路700号' },
// { id: 18, name: '万科金色家园', address: '万科市萧山区金城路800号' },
// { id: 19, name: '绿城桂花城', address: '绿城市西湖区文三路900号' },
// { id: 20, name: '滨江金色海岸', address: '滨江市江干区下沙路1000号' },
// { id: 21, name: '远洋公馆', address: '远洋市拱墅区莫干山路1100号' },
// { id: 22, name: '龙湖春江彼岸', address: '龙湖市滨江区闻涛路1200号' },
// { id: 23, name: '融信蓝孔雀', address: '融信市西湖区学院路1300号' },
// { id: 24, name: '德信早安', address: '德信市余杭区良睦路1400号' },
// { id: 25, name: '宋都阳光国际', address: '宋都市江干区下沙路1500号' }
])
// 计算属性
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>