2026-04-20 06:23:11 +00:00
|
|
|
<template>
|
|
|
|
|
<view class="renters-page">
|
|
|
|
|
<view class="custom-nav safe-area-top">
|
|
|
|
|
<view class="nav-content">
|
|
|
|
|
<view class="nav-back" @click="goBack">
|
|
|
|
|
<uni-icons type="left" size="20" color="#1E293B"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
<text class="nav-title">租客管理</text>
|
|
|
|
|
<view class="nav-actions">
|
|
|
|
|
<view class="nav-btn" @click="addRenter">
|
|
|
|
|
<uni-icons type="plus" size="20" color="#2563EB"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<scroll-view scroll-y class="page-content" @scrolltolower="loadMore">
|
|
|
|
|
<view class="search-section">
|
|
|
|
|
<view class="search-box">
|
|
|
|
|
<uni-icons type="search" size="18" color="#94A3B8"></uni-icons>
|
2026-04-22 06:47:04 +00:00
|
|
|
<input type="text" v-model="searchForm.keyword" placeholder="搜索姓名/电话/身份证" @confirm="search"/>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="filter-box">
|
|
|
|
|
<picker mode="selector" :range="statusOptions" range-key="label" :value="statusIndex" @change="onStatusChange">
|
|
|
|
|
<view class="filter-item">
|
|
|
|
|
<text>{{statusOptions[statusIndex].label}}</text>
|
|
|
|
|
<uni-icons type="down" size="14" color="#64748B"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</picker>
|
2026-04-20 06:23:11 +00:00
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="renter-list">
|
|
|
|
|
<view v-for="(item, index) in renterList" :key="index" class="renter-card" @click="viewDetail(item)">
|
|
|
|
|
<view class="renter-avatar">
|
|
|
|
|
<text class="avatar-text">{{item.name ? item.name.charAt(0) : '租'}}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="renter-info">
|
|
|
|
|
<view class="info-header">
|
|
|
|
|
<text class="renter-name">{{item.name || '未知'}}</text>
|
|
|
|
|
<view class="renter-status" :class="item.status">
|
|
|
|
|
<text>{{getStatusText(item.status)}}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-detail">
|
|
|
|
|
<text class="detail-item">{{item.phone || '暂无电话'}}</text>
|
2026-04-22 06:47:04 +00:00
|
|
|
<text class="detail-item" v-if="item.idCard">身份证: {{item.idCard || '-'}}</text>
|
2026-04-20 06:23:11 +00:00
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<uni-icons type="right" size="16" color="#94A3B8"></uni-icons>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view v-if="renterList.length === 0 && !isLoading" class="empty-state">
|
|
|
|
|
<text class="empty-text">暂无租客数据</text>
|
|
|
|
|
<button class="add-btn" @click="addRenter">添加租客</button>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="safe-area-bottom" style="height: 40rpx;"></view>
|
|
|
|
|
</scroll-view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { renterApi } from '../../api/index.js'
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
2026-04-22 06:47:04 +00:00
|
|
|
searchForm: {
|
|
|
|
|
keyword: '',
|
|
|
|
|
status: ''
|
|
|
|
|
},
|
|
|
|
|
statusOptions: [
|
|
|
|
|
{ label: '全部', value: '' },
|
|
|
|
|
{ label: '正常', value: 'active' },
|
|
|
|
|
{ label: '停用', value: 'inactive' }
|
|
|
|
|
],
|
|
|
|
|
statusIndex: 0,
|
2026-04-20 06:23:11 +00:00
|
|
|
renterList: [],
|
2026-04-22 06:47:04 +00:00
|
|
|
total: 0,
|
2026-04-20 06:23:11 +00:00
|
|
|
isLoading: false,
|
|
|
|
|
page: 1,
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
hasMore: true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onLoad() {
|
|
|
|
|
this.loadData()
|
|
|
|
|
},
|
|
|
|
|
onShow() {
|
|
|
|
|
this.loadData()
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
async loadData() {
|
|
|
|
|
if (this.isLoading) return
|
|
|
|
|
this.isLoading = true
|
|
|
|
|
try {
|
2026-04-22 06:47:04 +00:00
|
|
|
const params = {
|
|
|
|
|
keyword: this.searchForm.keyword,
|
|
|
|
|
status: this.searchForm.status,
|
2026-04-20 06:23:11 +00:00
|
|
|
page: this.page,
|
|
|
|
|
pageSize: this.pageSize
|
2026-04-22 06:47:04 +00:00
|
|
|
}
|
|
|
|
|
const res = await renterApi.getAll(params)
|
|
|
|
|
const list = res.data.list
|
|
|
|
|
const total = res.data.total
|
2026-04-20 06:23:11 +00:00
|
|
|
if (this.page === 1) {
|
|
|
|
|
this.renterList = list
|
|
|
|
|
} else {
|
|
|
|
|
this.renterList = [...this.renterList, ...list]
|
|
|
|
|
}
|
2026-04-22 06:47:04 +00:00
|
|
|
this.total = total
|
|
|
|
|
this.hasMore = this.renterList.length < total
|
2026-04-20 06:23:11 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
|
|
|
|
} finally {
|
|
|
|
|
this.isLoading = false
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
loadMore() {
|
|
|
|
|
if (this.hasMore && !this.isLoading) {
|
|
|
|
|
this.page++
|
|
|
|
|
this.loadData()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
search() {
|
|
|
|
|
this.page = 1
|
|
|
|
|
this.loadData()
|
|
|
|
|
},
|
2026-04-22 06:47:04 +00:00
|
|
|
onStatusChange(e) {
|
|
|
|
|
this.statusIndex = e.detail.value
|
|
|
|
|
this.searchForm.status = this.statusOptions[this.statusIndex].value
|
|
|
|
|
this.page = 1
|
|
|
|
|
this.loadData()
|
|
|
|
|
},
|
2026-04-20 06:23:11 +00:00
|
|
|
addRenter() {
|
|
|
|
|
uni.navigateTo({ url: '/pages/renter-add/renter-add' })
|
|
|
|
|
},
|
|
|
|
|
viewDetail(item) {
|
|
|
|
|
uni.navigateTo({ url: `/pages/renter-detail/renter-detail?id=${item.id}` })
|
|
|
|
|
},
|
|
|
|
|
goBack() {
|
|
|
|
|
uni.navigateBack()
|
|
|
|
|
},
|
|
|
|
|
getStatusText(status) {
|
2026-04-22 06:47:04 +00:00
|
|
|
const texts = { 'active': '正常', 'inactive': '停用' }
|
2026-04-20 06:23:11 +00:00
|
|
|
return texts[status] || '未知'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.renters-page { min-height: 100vh; background: #F8FAFC; display: flex; flex-direction: column; }
|
|
|
|
|
.custom-nav { background: #FFFFFF; border-bottom: 2rpx solid #F1F5F9; }
|
|
|
|
|
.nav-content { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 32rpx; }
|
|
|
|
|
.nav-back { width: 60rpx; height: 60rpx; display: flex; align-items: center; justify-content: center; }
|
|
|
|
|
.nav-title { font-size: 36rpx; font-weight: 700; color: #1E293B; }
|
|
|
|
|
.nav-actions { width: 60rpx; height: 60rpx; display: flex; align-items: center; justify-content: center; }
|
|
|
|
|
.page-content { flex: 1; padding: 24rpx 32rpx; }
|
2026-04-22 06:47:04 +00:00
|
|
|
.search-section { margin-bottom: 24rpx; display: flex; gap: 16rpx; }
|
|
|
|
|
.search-box { flex: 1; display: flex; align-items: center; background: #FFFFFF; border-radius: 16rpx; padding: 20rpx 24rpx; gap: 16rpx; }
|
2026-04-20 06:23:11 +00:00
|
|
|
.search-box input { flex: 1; font-size: 28rpx; color: #1E293B; }
|
2026-04-22 06:47:04 +00:00
|
|
|
.filter-box { background: #FFFFFF; border-radius: 16rpx; padding: 0 24rpx; display: flex; align-items: center; }
|
|
|
|
|
.filter-item { display: flex; align-items: center; gap: 8rpx; height: 72rpx; }
|
|
|
|
|
.filter-item text { font-size: 28rpx; color: #64748B; }
|
2026-04-20 06:23:11 +00:00
|
|
|
.renter-list { display: flex; flex-direction: column; gap: 24rpx; }
|
|
|
|
|
.renter-card { background: #FFFFFF; border-radius: 24rpx; padding: 32rpx; display: flex; align-items: center; gap: 24rpx; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04); }
|
|
|
|
|
.renter-avatar { width: 88rpx; height: 88rpx; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; }
|
|
|
|
|
.avatar-text { font-size: 36rpx; font-weight: 700; color: #FFFFFF; }
|
|
|
|
|
.renter-info { flex: 1; }
|
|
|
|
|
.info-header { display: flex; align-items: center; gap: 16rpx; margin-bottom: 12rpx; }
|
|
|
|
|
.renter-name { font-size: 32rpx; font-weight: 700; color: #1E293B; }
|
|
|
|
|
.renter-status { padding: 6rpx 16rpx; border-radius: 8rpx; font-size: 22rpx; }
|
|
|
|
|
.renter-status.active { background: #DCFCE7; color: #16A34A; }
|
|
|
|
|
.renter-status.inactive { background: #F1F5F9; color: #64748B; }
|
|
|
|
|
.info-detail { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
|
|
|
|
.detail-item { font-size: 26rpx; color: #64748B; }
|
|
|
|
|
.empty-state { display: flex; flex-direction: column; align-items: center; padding: 120rpx 0; }
|
|
|
|
|
.empty-text { font-size: 28rpx; color: #94A3B8; margin-bottom: 32rpx; }
|
|
|
|
|
.add-btn { background: linear-gradient(135deg, #2563EB 0%, #1D4ED8 100%); color: #FFFFFF; font-size: 28rpx; padding: 24rpx 64rpx; border-radius: 16rpx; border: none; }
|
|
|
|
|
</style>
|