Files
property-resident-side/property-uniapp-project/pageSubPack/visitor/visitor-list.vue
zhouyanli 1710cba509 修复bug
2026-08-17 18:02:46 +08:00

342 lines
7.1 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="visitor-page">
<!-- 标签切换栏 -->
<view class="tab-bar">
<view
class="tab-item"
:class="{ active: activeTab === 'waiting' }"
@click="activeTab = 'waiting'"
>
等待来访
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'visited' }"
@click="activeTab = 'visited'"
>
已到访
</view>
</view>
<!-- 访客列表 -->
<scroll-view class="list-container" scroll-y>
<view
class="visitor-item"
v-for="(item, index) in filterdList"
:key="index"
>
<!-- 访客基本信息 -->
<view class="item-header">
<text class="visitor-name" :class="item.status">
来访者{{ item.visitorName }}
</text>
<text class="status-tag" :class="item.status">
{{ item.statusText }}
</text>
</view>
<!-- 有效期/到访时间 -->
<view class="item-row">
<text class="label" :class="item.status">
{{ item.status === "waiting" ? "有效期" : "到访时间" }}
</text>
<text class="value" :class="item.status">
{{ item.startTime }}{{ item.endTime }}
</text>
</view>
<!-- 联系方式 -->
<view class="item-row">
<text class="label" :class="item.status">联系方式</text>
<text class="value" :class="item.status">{{ item.phone }}</text>
</view>
<!-- 车牌号仅车辆来访显示 -->
<view class="item-row" v-if="item.type !== '1'">
<text class="label" :class="item.status">车牌号</text>
<text class="value" :class="item.status">{{ item.carNumber }}</text>
</view>
<!-- 时间+查看详情 -->
<view class="item-footer">
<text class="time-text" :class="item.status">
时间{{ item.createTime }}
</text>
<view class="detail-btn" @click="viewDetail(item)">
<text>查看详情</text>
<uni-icons type="arrowright" size="14"></uni-icons>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="filterdList.length === 0" class="empty-state">
<uni-icons type="info" size="30" color="#ccc"></uni-icons>
<text class="empty-text">
{{ activeTab === "waiting" ? "暂无等待到访数据" : "暂无已到访数据" }}
</text>
</view>
<!-- 底部占位防止最后一项贴底 -->
<view class="list-bottom-space"></view>
</scroll-view>
<!-- 新增邀请按钮 -->
<view class="add-btn-wrapper">
<view class="add-btn" @click="addInvitation">
<text class="add-text">新增邀请+</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from "vue";
import { onShow } from "@dcloudio/uni-app";
import { getVisitorList } from "../api/api.js";
const activeTab = ref("waiting");
const visitorList = ref([]);
const mapData = (list) => {
if (!Array.isArray(list)) return [];
return list.map((item) => {
const status =
item.status === 1 || item.status === "1" ? "visited" : "waiting";
return {
...item,
status,
statusText: status === "visited" ? "已到访" : "等待来访",
phone: item.visitorPhone,
carNumber: item.carNum,
createTime: item.createTime || "",
};
});
};
const fetchVisitorList = async () => {
try {
const params = {
userId: uni.getStorageSync("userId"),
villageId: uni.getStorageSync('currentVillageId')
}
const res = await getVisitorList(params);
visitorList.value = mapData(res.data || []);
} catch (err) {
uni.showToast({ title: err.msg, icon: "none" });
}
};
onShow(() => {
fetchVisitorList();
})
const filterdList = computed(() => {
return visitorList.value.filter((item) => item.status === activeTab.value);
});
const viewDetail = (item) => {
uni.navigateTo({
url: `/pageSubPack/visitor/visitor-detail?id=${item.id}`,
});
};
const addInvitation = () => {
uni.navigateTo({ url: "/pageSubPack/visitor/visitor-add" });
};
</script>
<style scoped>
/* 页面根容器 */
.visitor-page {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
box-sizing: border-box;
background: linear-gradient(135deg, #e2f0ff 0%, #f9f9f9 50%);
padding: 0 30rpx;
}
/* 标签切换栏 */
.tab-bar {
display: flex;
}
.tab-item {
position: relative;
padding: 24rpx 0;
margin-right: 48rpx;
font-size: 30rpx;
color: #666;
}
.tab-item.active {
color: #2f77fd;
font-weight: 500;
}
.tab-item.active::after {
content: "";
position: absolute;
bottom: 8rpx;
left: 50%;
transform: translateX(-50%);
width: 126rpx;
height: 8rpx;
border-radius: 32rpx;
background: linear-gradient(to bottom, #2F77FD, #CEDFFF);
}
/* 列表区域 */
.list-container {
flex: 1;
height: 0;
overflow: hidden;
}
/* 访客卡片 */
.visitor-item {
margin-top: 24rpx;
padding: 24rpx;
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.visitor-name {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.visitor-name.visited {
color: #999;
}
.status-tag {
font-size: 26rpx;
}
.status-tag.waiting {
color: #2f77fd;
}
.status-tag.visited {
color: #999;
}
/* 信息行 */
.item-row {
display: flex;
margin-bottom: 12rpx;
font-size: 26rpx;
line-height: 1.6;
}
.label {
min-width: 140rpx;
color: #666;
}
.value {
flex: 1;
color: #333;
}
.label.visited,
.value.visited {
color: #999;
}
/* 底部行 */
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16rpx;
padding-top: 16rpx;
border-top: 1rpx solid #f5f5f5;
}
.time-text {
font-size: 24rpx;
color: #999;
}
.time-text.visited {
color: #bbb;
}
.detail-btn {
display: flex;
align-items: center;
font-size: 26rpx;
color: #666;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
}
.empty-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #999;
}
/* 列表底部占位(为固定的按钮预留高度,保证最后一项能滚到按钮上方) */
.list-bottom-space {
height: 180rpx;
}
/* 新增邀请按钮 */
.add-btn-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 10;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
background-color: #f9f9f9;
}
.add-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #2f77fd;
color: #fff;
border-radius: 16rpx;
text-align: center;
font-size: 30rpx;
font-weight: 500;
box-shadow: 0 8rpx 24rpx rgba(47, 119, 253, 0.3);
}
.add-text {
font-size: 30rpx;
}
</style>
<style>
/* 页面容器撑满并禁止整体滚动,让内部 scroll-view 单独滚动 */
page {
height: 100%;
overflow: hidden;
}
</style>