Files
property-resident-side/property-uniapp-project/pageSubPack/notice-list/selectHome.vue

510 lines
11 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="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.villageId"
class="community-item"
:class="{ 'active-item': selectedItem && selectedItem.villageId === item.villageId }"
:id="'item-' + item.villageId"
@tap="selectCommunity(item)"
>
<view class="community-item-left">
<image src="https://wuyeadmin.bugtc.com/images/home/community.png" style="width: 60rpx;height: 60rpx;"></image>
<view class="community-info">
<text class="community-name">{{ item.villageName }}</text>
</view>
</view>
<view v-if="selectedItem && selectedItem.villageId === item.villageId" 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>
</view>
<!-- 没有更多数据 -->
<view v-if="!hasMoreData && displayedCommunities.length > 0" class="no-more-data">
<text>没有更多数据了</text>
</view>
<!-- 空状态 -->
<view v-if="displayedCommunities.length === 0 && !isSearching && !isLoading" class="empty-state">
<uni-icons type="info" size="30" color="#ccc"></uni-icons>
<text class="empty-text">暂无小区数据</text>
</view>
<!-- 搜索无结果 -->
<view v-if="displayedCommunities.length === 0 && isSearching && !isLoading" class="empty-state">
<uni-icons type="search" size="30" 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>
</view>
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getVillageList, getVillageSwitch } from "../api/api.js"
// 响应式数据
const searchText = ref('')
const selectedItem = ref(null)
const scrollIntoViewId = ref('')
const currentVillageIdFromHome = ref('') // 首页传入的当前选中小区ID
// 分页相关
const currentPage = ref(1)
const pageSize = 5
const totalCount = ref(0)
const displayedCommunities = ref([])
const hasMoreData = ref(true)
const isLoading = ref(false)
// 搜索状态
const isSearching = ref(false)
let searchTimeout = null
// =====方法======
// 查询已认证小区列表
const getCommunityList = async (pageNum = 1, keyword = '') => {
isLoading.value = true
try {
const params = {
pageNum,
pageSize,
keyword: keyword.trim()
}
const res = await getVillageList(params)
if (res.code === 200) {
const listData = res.data || []
const total = res.data?.total || listData.length
if (pageNum === 1) {
displayedCommunities.value = listData
} else {
displayedCommunities.value = [...displayedCommunities.value, ...listData]
}
totalCount.value = total
hasMoreData.value = displayedCommunities.value.length < totalCount.value
// 默认选中:优先匹配传入的 villageId否则选第一条仅第一页时
if (pageNum === 1 && displayedCommunities.value.length > 0) {
const targetId = currentVillageIdFromHome.value || uni.getStorageSync('currentVillageId') || ''
if (targetId) {
const matched = displayedCommunities.value.find(
item => String(item.villageId) === String(targetId)
)
selectedItem.value = matched || displayedCommunities.value[0]
} else {
selectedItem.value = displayedCommunities.value[0]
}
}
} else {
displayedCommunities.value = []
hasMoreData.value = false
uni.showToast({
title: res.msg || "查询失败",
icon: 'error'
})
}
} catch (err) {
// console.error('获取小区列表失败:', err)
displayedCommunities.value = []
hasMoreData.value = false
uni.showToast({
title: err.msg || "网络异常",
icon: 'error'
})
} finally {
isLoading.value = false
}
}
// 页面加载时初始化
onLoad((options) => {
// 接收首页传来得选择的小区的id
currentVillageIdFromHome.value = options?.villageId || ''
getCommunityList(1)
})
// 加载更多数据
const loadMoreData = () => {
if (isLoading.value || !hasMoreData.value) return
getCommunityList(currentPage.value + 1, searchText.value)
currentPage.value += 1
}
// 处理搜索输入(防抖)
const handleSearchInput = () => {
if (searchTimeout) {
clearTimeout(searchTimeout)
}
searchTimeout = setTimeout(() => {
handleSearch()
}, 300)
}
// 处理搜索
const handleSearch = () => {
const keyword = searchText.value.trim()
isSearching.value = !!keyword
currentPage.value = 1
getCommunityList(1, keyword)
}
// 清空搜索
const clearSearch = () => {
searchText.value = ''
isSearching.value = false
currentPage.value = 1
getCommunityList(1)
}
// 选择小区
const selectCommunity = (item) => {
selectedItem.value = item
}
// 确认选择
const handleConfirm = async () => {
if (!selectedItem.value) {
uni.showToast({
title: '请先选择小区',
icon: 'none',
duration: 2000
})
return
}
try {
uni.showLoading({
title: '切换小区中...',
mask: true
})
const params = {
villageId: selectedItem.value.villageId,
}
const res = await getVillageSwitch(params)
uni.hideLoading()
if (res.code === 200) {
if (res.data.villageId) {
selectedItem.value.villageId = res.data.villageId
}
uni.showToast({
title: '选择成功',
icon: 'success',
duration: 1500
})
// 更新缓存并通知首页
uni.setStorageSync('currentVillageId', selectedItem.value.villageId)
uni.$emit('confirm', {
name: selectedItem.value.villageName,
villageId: selectedItem.value.villageId
})
setTimeout(() => {
uni.navigateBack()
}, 1000)
} else {
uni.showToast({
title: res.msg || '切换小区失败',
icon: 'error',
duration: 2000
})
}
} catch (err) {
uni.hideLoading()
uni.showToast({
title: err.msg || '网络异常,请重试',
icon: 'error',
duration: 2000
})
}
}
// 监听搜索文本变化
watch(searchText, (newVal) => {
if (!newVal.trim()) {
clearSearch()
}
})
// 初始化
onMounted(() => {
if (displayedCommunities.value.length === 0 && !isLoading.value) {
getCommunityList(1)
}
})
</script>
<style scoped>
.container {
min-height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
padding: 40rpx;
}
.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;
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,
.community-item.active-item {
/* background-color: #e6f2ff; */
}
.community-item-left {
display: flex;
align-items: center;
flex: 1;
}
.community-info {
display: flex;
flex-direction: column;
flex: 1;
margin-left: 20rpx;
}
.community-name {
font-size: 32rpx;
color: #333;
font-weight: 500;
}
.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); }
}
.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: 28rpx;
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;
}
@media (min-width: 768px) {
.container {
max-width: 500px;
margin: 0 auto;
}
}
</style>