SmartParks_uniapp/pages/sys/user/myVisitor/myVisitor.vue

430 lines
9.4 KiB
Vue
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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-container">
<!-- tab栏 -->
<view class="visitor-tabs">
<view v-for="(tab, idx) in tabs" :key="idx" :class="['visitor-tab', {active: idx === activeTab}]"
@click="changeTab(idx)">
{{ tab }}
<view v-if="idx === activeTab" class="tab-underline"></view>
</view>
</view>
<!-- 列表区 -->
<view class="visitor-list-container">
<scroll-view
v-for="(tab, idx) in tabs"
:key="idx"
v-show="idx === activeTab"
class="visitor-list"
scroll-y
:refresher-enabled="false">
<view v-for="(item, index) in tabData[idx]" :key="index" class="visitor-card" @click="showVisitorDetail(item)">
<view class="card-row">
<view class="card-type">{{ item.visitorUnit }}</view>
<view class="card-status" :class="getStatusColor(item.serveStatus)">{{ getStatusLabel(item.serveStatus) }}</view>
</view>
<view class="card-info">{{ item.visitorName }}
<text class="card-phone">{{ item.visitorPhone }}</text>
<text class="card-divider"></text>
<text class="card-address">{{ item.visitorUnit }}</text>
</view>
<view class="card-time">{{ item.createTime }}</view>
</view>
</scroll-view>
</view>
<!-- 底部按钮 -->
<button class="visitor-btn-fixed" @click="goCreateVisitor">访客邀约</button>
<view v-if="showDetail" class="visitor-detail-mask" @click.self="closeVisitorDetail">
<view class="visitor-detail-dialog">
<view class="visitor-detail-title-row">
<text class="visitor-detail-title">访客详情</text>
<image src="/static/ic_close_01.png" class="visitor-detail-close" @click="closeVisitorDetail" />
</view>
<view class="visitor-detail-info">
<view>{{ detailData.visitorName }} {{ detailData.visitorPhone }}</view>
<view v-if="detailData.idCard">{{ detailData.idCard}}</view>
<view v-if="detailData.carNumber">车牌号:{{ detailData.carNumber}}</view>
<view>来访时间:{{ detailData.visitingBeginTime }}</view>
<view>访问地址:{{ detailData.visitorUnit}}</view>
<image
:src="detailData.statusClass === 'orange' ? '/static/ic_my_visitor_01.png' : '/static/ic_my_visitor_02.png'"
class="visitor-detail-status-img" />
</view>
<button class="visitor-detail-btn">修改</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
tabs: ['我的预约', '我的邀约', '全部记录'],
activeTab: 0,
tabData: [
[],
[],
[]
], // 每个tab的数据
tabLoaded: [false, false, false], // 每个tab是否已加载
loading: false,
showDetail: false,
detailData: {},
}
},
computed: {
list() {
return this.tabData[this.activeTab];
}
},
created() {
this.loadAllTabsData(); // 预加载所有标签页数据
},
methods: {
goBack() {
uni.navigateBack();
},
goCreateVisitor() {
uni.navigateTo({
url: '/pages/sys/user/myVisitor/creatVisitor'
});
},
async changeTab(idx) {
this.activeTab = idx;
// 移除切换标签时的加载逻辑,避免重复调用接口
/*
if (!this.tabLoaded[idx]) {
await this.loadTabData(idx);
}
*/
},
async loadTabData(idx) {
this.loading = true;
// 模拟接口请求不同tab返回不同mock数据
let params = {}
if (idx === 0) {
params = {
"interviewedPerson": this.vuex_user.userId
}
}
let data = [];
let res = await this.$u.api.getMyVisitor(params);
if (res.code == '200') {
data = res.rows
}
this.$set(this.tabData, idx, data);
this.$set(this.tabLoaded, idx, true);
this.loading = false;
},
async loadAllTabsData() {
for (let i = 0; i < this.tabs.length; i++) {
if (!this.tabLoaded[i]) {
await this.loadTabData(i);
}
}
},
showVisitorDetail(item) {
this.detailData = item;
this.showDetail = true;
},
closeVisitorDetail() {
this.showDetail = false;
},
getStatusLabel(status) {
const statusMap = {
0: '待确认',
1: '已确认',
2: '已取消',
3: '已完成'
};
return statusMap[status] || '';
},
getStatusColor(status){
const statusMap = {
0: '待确认',
1: 'orange',
2: '已取消',
3: '已完成'
};
return statusMap[status] || '';
},
// 添加预加载所有标签页数据的方法
async loadAllTabsData() {
// 并行加载所有标签页数据,提高加载速度
const loadPromises = [0, 1, 2].map((index) => {
return this.loadTabData(index);
});
await Promise.all(loadPromises);
// 标记所有标签页已加载
this.tabLoaded = [true, true, true];
}
}
}
</script>
<style scoped>
.visitor-container {
height: 100vh;
background: #f7f7f7;
display: flex;
flex-direction: column;
overflow: hidden;
}
.visitor-tabs {
display: flex;
align-items: center;
justify-content: space-around;
background: #fff;
height: 80rpx;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
/* 防止被压缩 */
}
.visitor-list-container {
flex: 1;
position: relative;
margin: 25rpx 0 0 0;
padding: 0 35rpx;
padding-bottom: 200rpx;
/* 为底部按钮留出空间 */
}
.visitor-tab {
flex: 1;
text-align: center;
font-size: 30rpx;
color: #888;
position: relative;
font-weight: 500;
padding: 0 0 10rpx 0;
/* tab点击事件 */
cursor: pointer;
}
.visitor-tab.active {
color: #2186FF;
font-weight: bold;
}
.tab-underline {
width: 60rpx;
height: 6rpx;
background: #2186FF;
border-radius: 3rpx;
margin: 0 auto;
margin-top: 8rpx;
}
.visitor-list {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 0 35rpx;
overflow-y: auto;
padding-bottom: 200rpx;
/* 为底部按钮留出空间 */
height: calc(100vh - 80rpx - 32rpx - 80rpx); /* 减去顶部tab区域和底部按钮高度 */
box-sizing: border-box;
}
.visitor-card {
background: #fff;
border-radius: 16rpx;
margin-bottom: 24rpx;
padding: 20rpx 40rpx 70rpx 12rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
}
.card-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 28rpx;
}
.card-type {
font-weight: 600;
font-size: 26rpx;
padding: 10rpx 45rpx 10rpx 12rpx;
border-radius: 15rpx;
color: #fff;
background: linear-gradient(90deg, #007CFF 0%, #FFFFFF 100%);
display: inline-block;
}
.card-status {
font-size: 28rpx;
}
.card-status.orange {
color: #F3831F;
}
.card-status.green {
color: #07C78E;
}
.card-status.gray {
color: #8F8F8F;
}
.card-info {
font-size: 26rpx;
color: #333;
margin-bottom: 8rpx;
margin-left: 8rpx;
display: flex;
align-items: center;
}
.card-phone {
margin: 0 8rpx 0 8rpx;
}
.card-divider {
width: 1rpx;
height: 40rpx;
background: #DCDCDC;
display: inline-block;
margin: 0 12rpx;
vertical-align: middle;
content: '';
}
.card-address {
margin-left: 0;
}
.card-time {
font-size: 28rpx;
color: #626262;
margin-left: 8rpx;
}
.visitor-btn {
width: 90vw;
height: 80rpx;
background: #0090FF;
color: #fff;
font-size: 32rpx;
border: none;
border-radius: 40rpx;
margin: 60rpx auto 0 auto;
display: block;
font-weight: bold;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.18);
}
.visitor-btn-fixed {
position: fixed;
left: 0;
right: 0;
bottom: 100rpx;
margin: 0 auto;
width: 90vw;
height: 80rpx;
background: #0090FF;
color: #fff;
font-size: 32rpx;
border: none;
border-radius: 40rpx;
display: block;
font-weight: bold;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.18);
z-index: 99;
}
.visitor-detail-mask {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}
.visitor-detail-dialog {
width: 80vw;
background: #fff;
border-radius: 36rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
padding: 48rpx 36rpx 36rpx 36rpx;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
.visitor-detail-title-row {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 52rpx;
}
.visitor-detail-title {
font-size: 36rpx;
color: #000;
}
.visitor-detail-close {
position: absolute;
right: 0;
top: 0;
width: 22rpx;
height: 22rpx;
}
.visitor-detail-info {
width: 100%;
font-size: 28rpx;
color: #222;
margin-bottom: 80rpx;
display: flex;
flex-direction: column;
gap: 28rpx;
position: relative;
}
.visitor-detail-status-img {
position: absolute;
right: 0;
bottom: -40rpx;
width: 160rpx;
height: 160rpx;
z-index: 1;
pointer-events: none;
}
.visitor-detail-btn {
width: 320rpx;
height: 80rpx;
background: linear-gradient(90deg, #0090FF 0%, #2E9FFF 100%);
color: #fff;
font-size: 32rpx;
border: none;
border-radius: 40rpx;
margin-bottom: 30rpx;
display: block;
font-weight: bold;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.18);
}
</style>