Files
property-resident-side/property-uniapp-project/pageSubPack/visitor/visitor-list.vue
2026-07-30 15:35:02 +08:00

328 lines
6.7 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">
<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: 100%;
overflow: hidden;
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: 24rpx;
}
/* 新增邀请按钮 */
.add-btn-wrapper {
flex-shrink: 0;
padding: 20rpx 0;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.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>