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

392 lines
8.3 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="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 顶部导航栏 -->
<view class="nav-bar">
<view class="nav-left" @tap="goBack">
<image
src="https://wuyeadmin.bugtc.com/images/home/arr-left.png"
class="back-icon"
/>
</view>
<view class="nav-title">访客邀请</view>
<view class="nav-right"></view>
</view>
<!-- 标签切换栏 -->
<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 :style="{ height: scrollHeight + 'px' }">
<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 { onLoad } from "@dcloudio/uni-app";
import { getVisitorList } from "../api/api.js";
const activeTab = ref("waiting");
const statusBarHeight = ref(0);
const scrollHeight = ref(0);
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 () => {
const userId = uni.getStorageSync("userId");
try {
const res = await getVisitorList({ userId });
visitorList.value = mapData(res.data || []);
} catch (err) {
uni.showToast({ title: err.msg, icon: "none" });
}
};
onLoad(() => {
const info = uni.getSystemInfoSync();
statusBarHeight.value = info.statusBarHeight || 0;
// rpx 转 px 比例
const scale = info.windowWidth / 750;
// 各固定区域高度rpx 转 px
const navPx = 90 * scale; // 导航栏
const tabPx = 88 * scale; // tab栏
const btnPx = 148 * scale; // 底部按钮区域
// 列表区域可用高度 = 窗口高度 - 状态栏 - 导航栏 - tab栏 - 底部按钮区
scrollHeight.value = info.windowHeight - statusBarHeight.value - navPx - tabPx - btnPx;
fetchVisitorList();
});
const filterdList = computed(() => {
return visitorList.value.filter((item) => item.status === activeTab.value);
});
const goBack = () => {
const source = uni.getStorageSync("sourcePage");
if (source === "index") {
uni.switchTab({ url: "/pages/index/index" });
} else if (source === "serviceIndex") {
uni.switchTab({ url: "/pages/serviceIndex/serviceIndex" });
}
uni.removeStorageSync("sourcePage");
};
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 {
background: linear-gradient(135deg, #e2f0ff 0%, #f9f9f9 50%);
padding: 0 30rpx;
}
.status-bar {
width: 100%;
}
/* 顶部导航栏 */
.nav-bar {
display: flex;
align-items: center;
height: 90rpx;
}
.nav-left {
width: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.back-icon {
width: 36rpx;
height: 36rpx;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: 500;
color: #000;
}
.nav-right {
width: 60rpx;
}
/* 标签切换栏 */
.tab-bar {
display: flex;
/* border-bottom: 1rpx solid rgba(0, 0, 0, 0.04); */
}
.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-color: #2f77fd; */
background: linear-gradient(to bottom, #2F77FD, #CEDFFF);
}
/* 访客卡片 */
.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 {
height: 148rpx;
display: flex;
align-items: center;
}
.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>