Files
SmartParks_uniapp/pages/sys/user/myVisitor/myVisitor.vue
2025-09-09 20:52:35 +08:00

516 lines
12 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-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>
<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" v-if="activeTab===1" @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.visitingReason }}</view>
<view>来访时间{{ detailData.visitingBeginTime }}</view>
<view>访问地址{{ detailData.visitorUnit }}</view>
<view class="visitor-detail-qrCode" @click="showQRCode">查看二维码</view>
</view>
<!-- 二维码弹窗 -->
<view v-if="showQRCodeDialog" class="qr-code-mask" @click.self="closeQRCodeDialog">
<view class="qr-code-dialog">
<view class="qr-code-border">
<canvas id="qrcode" canvas-id="qrcode" style="width: 200px;height: 200px;"></canvas>
</view>
<view class="visitor-detail-qrCode" @click="closeQRCodeDialog">关闭</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
// 引入二维码生成库
import uQRCode from 'uqrcodejs';
export default {
data() {
return {
tabs: ['我的预约', '我的邀约'],
activeTab: 0,
tabData: [
[],
[]
], // 每个tab的数据
tabLoaded: [false, false, false], // 每个tab是否已加载
loading: false,
showDetail: false,
detailData: {},
showQRCodeDialog: false,
qrCodeImage: '/static/qr-code-placeholder.png' // 替换为实际的二维码图片路径
}
},
computed: {
list() {
return this.tabData[this.activeTab];
}
},
created() {
this.loadAllTabsData(); // 预加载所有标签页数据
},
methods: {
goBack() {
uni.navigateBack();
},
goCreateVisitor() {
uni.navigateTo({
url: '/pages/sys/user/myVisitor/creatVisitor'
});
},
showQRCode() {
this.showQRCodeDialog = true;
this.$nextTick(() => {
this.makeQRCode();
});
},
closeQRCodeDialog() {
this.showQRCodeDialog = false;
},
async changeTab(idx) {
this.activeTab = idx;
// 移除切换标签时的加载逻辑,避免重复调用接口
/*
if (!this.tabLoaded[idx]) {
await this.loadTabData(idx);
}
*/
},
makeQRCode() {
console.log(this.detailData.id)
let params = {}
params = {
"id": this.detailData.id
}
// 设置二维码内容
this.$u.api.visitor.qrCodeVisitor(params).then(res => {
if (res.code == 200) {
// 获取uQRCode实例
var qr = new uQRCode();
//获取二维码数据
qr.data = res.data
// 设置二维码大小单位px
qr.size = 200;
// 调用制作二维码方法
qr.make();
// 获取canvas上下文
var canvasContext = uni.createCanvasContext('qrcode', this); // 如果是组件this必须传入
// 设置uQRCode实例的canvas上下文
qr.canvasContext = canvasContext;
// 调用绘制方法将二维码图案绘制到canvas上
qr.drawCanvas();
}
}).catch(err => {
console.log(err);
uni.showToast({
title: "二维码获取失败",
icon: "none",
})
})
},
async loadTabData(idx) {
this.loading = true;
// 模拟接口请求不同tab返回不同mock数据
let params = {}
params = {
"type": idx
}
let data = [];
let res = await this.$u.api.visitor.getMyVisitor(params);
if (res.code == 200) {
data = res.data
}
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] || '';
},
// 添加预加载所有标签页数据的方法
async loadAllTabsData() {
// 并行加载所有标签页数据,提高加载速度
const loadPromises = [0, 1].map((index) => {
return this.loadTabData(index);
});
await Promise.all(loadPromises);
console.log()
// 标记所有标签页已加载
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-qrCode {
text-align: center;
color: #2186FF;
font-size: 28rpx;
margin-top: 40rpx;
cursor: pointer;
}
/* 二维码弹窗样式 */
.qr-code-mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
z-index: 99999;
display: flex;
align-items: center;
justify-content: center;
}
.qr-code-dialog {
background: #fff;
border-radius: 24rpx;
padding: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.qr-code-canvas {
width: 400rpx;
height: 400rpx;
margin-bottom: 20rpx;
}
.qr-code-border{
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
box-shadow: 0px 0px 12px 1px #00BBFF inset;
background: linear-gradient(to left, #196aa8, #196aa8) left top no-repeat,
linear-gradient(to bottom, #196aa8, #196aa8) left top no-repeat,
linear-gradient(to left, #196aa8, #196aa8) right top no-repeat,
linear-gradient(to bottom, #196aa8, #196aa8) right top no-repeat,
linear-gradient(to left, #196aa8, #196aa8) left bottom no-repeat,
linear-gradient(to bottom, #196aa8, #196aa8) left bottom no-repeat,
linear-gradient(to left, #196aa8, #196aa8) right bottom no-repeat,
linear-gradient(to left, #196aa8, #196aa8) right bottom no-repeat;
background-size:
1px 12px, /* 左上角垂直部分 */
12px 1px, /* 左上角水平部分 */
1px 12px, /* 右上角垂直部分 */
12px 1px, /* 右上角水平部分 */
1px 12px, /* 左下角垂直部分 */
12px 1px, /* 左下角水平部分 */
1px 12px, /* 右下角垂直部分 */
12px 1px; /* 右下角水平部分 */
}
</style>