SmartParks_uniapp/pages/sys/user/myVisitor/myVisitor.vue
2025-07-27 15:52:39 +08:00

411 lines
8.7 KiB
Vue
Raw 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">
<view v-for="(item, idx) in list" :key="idx" 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>
</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: 1,
tabData: [
[],
[],
[]
], // 每个tab的数据
tabLoaded: [false, false, false], // 每个tab是否已加载
loading: false,
showDetail: false,
detailData: {},
}
},
computed: {
list() {
return this.tabData[this.activeTab];
}
},
created() {
this.loadTabData(this.activeTab); // 初始化加载当前tab数据
},
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;
},
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] || '';
}
}
}
</script>
<style scoped>
.visitor-container {
height: 100vh;
background: #f7f7f7;
display: flex;
flex-direction: column;
}
.visitor-navbar {
width: 100%;
height: 120rpx;
padding-top: 40rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
background: #fff;
flex-shrink: 0;
/* 防止被压缩 */
}
.visitor-back {
position: absolute;
left: 37rpx;
width: 15rpx;
height: 33rpx;
}
.visitor-title {
font-size: 36rpx;
color: #000;
}
.visitor-tabs {
display: flex;
align-items: center;
justify-content: space-around;
background: #fff;
height: 80rpx;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
/* 防止被压缩 */
}
.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 {
margin: 25rpx 0 0 0;
padding: 0 35rpx;
flex: 1;
/* 占据所有剩余空间 */
overflow-y: auto;
/* 内容超出时,开启垂直滚动 */
padding-bottom: 200rpx;
/* 为底部按钮留出空间 */
}
.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>