You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

818 lines
18 KiB

1 month ago
<template>
<view class="page">
<!-- 门岗访客管理页面 -->
<view class="gate-container">
<!-- 头部导航 -->
<view class="gate-header">
<view class="gate-left">
<view class="today-visitors" @click="openList">访</view>
</view>
<view class="gate-right">
<view class="gate-info">
<text class="gate-name">{{ gateName }}</text>
<text class="switch-gate" @click="showGateSelector"></text>
</view>
<!-- #ifdef H5 -->
<view class="fullscreen-btn" @click="toggleFullscreen">
{{ fullscreen ? '取消全屏' : '打开全屏' }}
</view>
<!-- #endif -->
</view>
</view>
<!-- 主要内容区域 -->
<view class="gate-content">
<view class="form-container">
<!-- 拜访日期选择 -->
<view class="form-item">
<text class="form-label">拜访日期</text>
<u-datetime-picker
v-model="datePickerShow"
mode="range"
:start-year="2020"
:end-year="2030"
@confirm="onDateConfirm"
@cancel="datePickerShow = false">
</u-datetime-picker>
<u-input
v-model="dateRangeText"
placeholder="选择日期范围"
@click="datePickerShow = true"
readonly
class="date-input">
<u-icon name="calendar-fill" slot="suffix" color="#c0c4cc"></u-icon>
</u-input>
</view>
<!-- 核验销码输入 -->
<view class="form-item">
<text class="form-label">核验销码</text>
<u-input
v-model="select.code"
placeholder="请输入核销码或扫码"
clearable
@change="onCodeChange"
class="form-input">
</u-input>
</view>
<!-- 身份证输入 -->
<view class="form-item">
<text class="form-label">身份证件</text>
<view class="id-input-group">
<u-input
v-model="select.idcard"
placeholder="请输入身份证"
clearable
class="id-input">
</u-input>
<u-button type="primary" @click="getIdcard" class="id-btn">查询身份证</u-button>
</view>
</view>
<!-- 查询按钮 -->
<view class="form-item query-btn-container">
<u-button type="primary" @click="getList" class="query-btn" :loading="loading">查询</u-button>
</view>
</view>
</view>
</view>
<!-- 门岗人员选择弹窗 -->
<u-modal v-model="gateShow" title="请先选择门岗人员" :show-cancel-button="false" :mask-close-able="false">
<view class="gate-selector">
<u-radio-group v-model="gateAdminId" @change="onGateChange">
<u-radio
v-for="item in gateData"
:key="item.id"
:name="item.id"
class="gate-radio">
{{ item.name }}
</u-radio>
</u-radio-group>
</view>
<view slot="confirm-button">
<u-button type="primary" @click="confirmGate"></u-button>
</view>
</u-modal>
<!-- 访客信息弹窗 -->
<u-modal v-model="visitShow" title="访客信息" width="90%" :show-cancel-button="false">
<view class="visit-info" v-if="visitData">
<view class="info-item">
<text class="info-label">姓名</text>
<text class="info-value">{{ visitData.name }}</text>
</view>
<view class="info-item">
<text class="info-label">状态</text>
<text class="info-value">{{ visitData.audit_status_text }}</text>
</view>
<view class="info-item">
<text class="info-label">被访人</text>
<text class="info-value">{{ visitData.accept_admin ? visitData.accept_admin.name : '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">联系电话</text>
<text class="info-value">{{ visitData.mobile }}</text>
</view>
</view>
<view slot="confirm-button">
<u-button type="primary" @click="closeVisitModal"></u-button>
</view>
</u-modal>
<!-- 今日访客列表弹窗 -->
<u-modal v-model="listShow" title="今日访客" width="90%">
<view class="visitor-list">
<view v-if="todayVisitors.length === 0" class="empty-state">
<text>今日暂无访客</text>
</view>
<view v-else>
<view v-for="visitor in todayVisitors" :key="visitor.id" class="visitor-item">
<view class="visitor-name">{{ visitor.name }}</view>
<view class="visitor-status">{{ visitor.audit_status_text }}</view>
</view>
</view>
</view>
</u-modal>
</view>
</template>
<script>
export default {
data() {
return {
fullscreen: false,
gateShow: false,
gateAdminId: '',
gateName: '',
gateData: [],
gateUser: {},
loading: false,
datePickerShow: false,
dateRangeText: '',
select: {
page: 1,
rows: 10,
keyword: '',
audit_status: '',
start_date: '',
end_date: '',
is_export: 0,
code: '',
idcard: ''
},
visitShow: false,
visitData: null,
listShow: false,
todayVisitors: []
}
},
onLoad() {
this.getUserList()
this.getToday()
},
onReachBottom() {
// 上拉加载更多
},
onPullDownRefresh() {
// 下拉刷新
this.getToday()
uni.stopPullDownRefresh()
},
methods: {
// 打开今日访客列表
openList() {
this.getTodayVisitors()
this.listShow = true
},
// 获取今日日期
getToday() {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const today = `${year}-${month}-${day}`
this.select.start_date = today
this.select.end_date = today
this.dateRangeText = `${today}${today}`
},
// 日期选择确认
onDateConfirm(data) {
this.select.start_date = data.startDate
this.select.end_date = data.endDate
this.dateRangeText = `${data.startDate}${data.endDate}`
this.datePickerShow = false
},
// 核销码变化
onCodeChange() {
if (this.select.code) {
this.getList()
}
},
// 清空输入
clearInputs() {
this.select.code = ''
this.select.idcard = ''
},
// 查询访客信息
async getList() {
if (!this.select.code && !this.select.idcard) {
uni.showToast({
title: '请输入核销码或身份证件',
icon: 'none'
})
return
}
this.loading = true
try {
// 这里需要根据实际情况替换API调用
const res = await this.apiGetList(this.select)
if (res && res.data && res.data.length > 0) {
const visitor = res.data[0]
if (visitor.audit_status == 1 || visitor.audit_status == 3) {
this.visitData = visitor
this.visitShow = true
} else {
uni.showToast({
title: visitor.audit_status_text,
icon: 'none'
})
}
} else {
uni.showToast({
title: '未查询到记录',
icon: 'none'
})
}
} catch (error) {
uni.showToast({
title: '查询失败',
icon: 'none'
})
} finally {
this.loading = false
this.clearInputs()
}
},
// 获取门岗人员列表
async getUserList() {
try {
// 从存储中获取门岗用户信息
const gateUser = uni.getStorageSync('gateUser')
if (gateUser) {
this.gateUser = gateUser
this.gateAdminId = gateUser.gateAdminId
this.gateName = gateUser.gateName
}
// 获取门岗人员列表
const res = await this.apiGetUserList()
this.gateData = res || []
if (!this.gateAdminId && this.gateData.length > 0) {
this.gateShow = true
}
} catch (error) {
console.error('获取门岗人员列表失败:', error)
}
},
// 门岗人员选择变化
onGateChange(value) {
this.gateAdminId = value
},
// 显示门岗选择器
showGateSelector() {
this.gateShow = true
},
// 确认门岗选择
confirmGate() {
if (!this.gateAdminId) {
uni.showToast({
title: '请先选择门岗',
icon: 'none'
})
return
}
const selectedGate = this.gateData.find(item => item.id == this.gateAdminId)
if (selectedGate) {
this.gateName = selectedGate.name
// 保存到存储
const gateUser = {
gateName: this.gateName,
gateAdminId: this.gateAdminId
}
uni.setStorageSync('gateUser', gateUser)
}
this.gateShow = false
},
// 读取身份证
getIdcard() {
// #ifdef H5
// H5环境下的身份证读取逻辑
uni.request({
url: 'https://127.0.0.1:24011/ZKIDROnline/ScanReadIdCardInfo?OP-DEV=1&CMD-URL=4&REPEAT=1&READTYPE=1',
method: 'GET',
header: {
'Content-Type': 'application/json'
},
success: (res) => {
if (res.data) {
try {
const data1 = res.data.split('"IDNumber"')
if (data1.length > 1) {
const data2 = data1[1].split(',')
const idNumber = data2[0].replace(/[^\d]/g, '')
this.select.idcard = idNumber
this.getList()
}
} catch (error) {
console.error('解析身份证信息失败:', error)
}
}
},
fail: (error) => {
console.error('读取身份证失败:', error)
uni.showToast({
title: '读取身份证失败',
icon: 'none'
})
}
})
// #endif
// #ifndef H5
uni.showToast({
title: '该功能仅支持H5环境',
icon: 'none'
})
// #endif
},
// 切换全屏
toggleFullscreen() {
// #ifdef H5
const element = document.documentElement
if (this.fullscreen) {
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen()
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen()
} else if (document.msExitFullscreen) {
document.msExitFullscreen()
}
} else {
if (element.requestFullscreen) {
element.requestFullscreen()
} else if (element.webkitRequestFullScreen) {
element.webkitRequestFullScreen()
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen()
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen()
}
}
this.fullscreen = !this.fullscreen
// #endif
},
// 关闭访客信息弹窗
closeVisitModal() {
this.visitShow = false
this.visitData = null
},
// 获取今日访客列表
async getTodayVisitors() {
try {
// 这里需要根据实际情况替换API调用
const todayParam = {
start_date: this.select.start_date,
end_date: this.select.end_date
}
const res = await this.apiGetTodayVisitors(todayParam)
this.todayVisitors = res?.data || []
} catch (error) {
console.error('获取今日访客列表失败:', error)
this.todayVisitors = []
}
},
// API调用方法需要根据实际情况调整
async apiGetList(params) {
return new Promise((resolve, reject) => {
uni.request({
url: '/api/gate/list', // 替换为实际API地址
method: 'GET',
data: params,
success: (res) => {
resolve(res.data)
},
fail: (error) => {
reject(error)
}
})
})
},
async apiGetUserList() {
return new Promise((resolve, reject) => {
uni.request({
url: '/api/gate/users', // 替换为实际API地址
method: 'GET',
success: (res) => {
resolve(res.data)
},
fail: (error) => {
reject(error)
}
})
})
},
async apiGetTodayVisitors(params) {
return new Promise((resolve, reject) => {
uni.request({
url: '/api/gate/today-visitors', // 替换为实际API地址
method: 'GET',
data: params,
success: (res) => {
resolve(res.data)
},
fail: (error) => {
reject(error)
}
})
})
}
},
};
</script>
<style lang="scss" scoped>
.page {
background: linear-gradient(135deg, #e4eafa, #f1f7fa);
min-height: 100vh;
padding: 20rpx;
box-sizing: border-box;
}
/* 门岗容器样式 */
.gate-container {
background-color: #fff;
min-height: calc(100vh - 40rpx);
border-radius: 20rpx;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* 头部导航样式 */
.gate-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 40rpx;
border-bottom: 2rpx solid #f0f0f0;
background: #fafafa;
/* 移动端样式 */
@media (max-width: 750px) {
padding: 20rpx 30rpx;
flex-direction: column;
gap: 20rpx;
align-items: stretch;
}
}
.gate-left {
.today-visitors {
color: #004593;
font-size: 36rpx;
font-weight: 600;
cursor: pointer;
@media (max-width: 750px) {
font-size: 32rpx;
}
&:active {
opacity: 0.7;
}
}
}
.gate-right {
display: flex;
align-items: center;
gap: 40rpx;
@media (max-width: 750px) {
justify-content: space-between;
gap: 20rpx;
}
}
.gate-info {
display: flex;
align-items: center;
gap: 20rpx;
.gate-name {
font-size: 32rpx;
color: #333;
@media (max-width: 750px) {
font-size: 28rpx;
}
}
.switch-gate {
color: #004593;
text-decoration: underline;
cursor: pointer;
font-size: 28rpx;
@media (max-width: 750px) {
font-size: 24rpx;
}
&:active {
opacity: 0.7;
}
}
}
.fullscreen-btn {
color: #666;
cursor: pointer;
font-size: 28rpx;
@media (max-width: 750px) {
font-size: 24rpx;
}
&:active {
color: #004593;
}
}
/* 主要内容区域 */
.gate-content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
@media (max-width: 750px) {
align-items: flex-start;
padding: 40rpx 30rpx;
}
}
.form-container {
width: 100%;
max-width: 1000rpx;
@media (min-width: 768px) {
max-width: 1200rpx;
}
}
.form-item {
margin-bottom: 50rpx;
@media (max-width: 750px) {
margin-bottom: 40rpx;
}
&:last-child {
margin-bottom: 0;
}
}
.form-label {
display: block;
font-size: 32rpx;
color: #333;
margin-bottom: 20rpx;
font-weight: 500;
@media (max-width: 750px) {
font-size: 28rpx;
margin-bottom: 16rpx;
}
@media (min-width: 768px) {
font-size: 40rpx;
margin-bottom: 24rpx;
}
}
.form-input, .date-input {
width: 100%;
}
.id-input-group {
display: flex;
gap: 20rpx;
@media (max-width: 750px) {
flex-direction: column;
gap: 16rpx;
}
}
.id-input {
flex: 1;
}
.id-btn {
@media (max-width: 750px) {
width: 100%;
}
@media (min-width: 768px) {
min-width: 240rpx;
}
}
.query-btn-container {
text-align: center;
margin-top: 60rpx;
@media (max-width: 750px) {
margin-top: 50rpx;
}
}
.query-btn {
width: 100%;
height: 100rpx;
font-size: 36rpx;
font-weight: 600;
@media (max-width: 750px) {
height: 88rpx;
font-size: 32rpx;
}
@media (min-width: 768px) {
height: 120rpx;
font-size: 48rpx;
}
}
/* 弹窗样式 */
.gate-selector {
padding: 20rpx 0;
}
.gate-radio {
margin-bottom: 20rpx;
padding: 20rpx;
border: 2rpx solid #e4e7ed;
border-radius: 8rpx;
&:last-child {
margin-bottom: 0;
}
}
.visit-info {
padding: 20rpx 0;
}
.info-item {
display: flex;
margin-bottom: 20rpx;
align-items: center;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
font-size: 28rpx;
color: #666;
min-width: 120rpx;
margin-right: 20rpx;
}
.info-value {
font-size: 28rpx;
color: #333;
flex: 1;
word-break: break-all;
}
.visitor-list {
max-height: 60vh;
overflow-y: auto;
}
.empty-state {
text-align: center;
padding: 60rpx 20rpx;
color: #999;
font-size: 28rpx;
}
.visitor-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.visitor-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.visitor-status {
font-size: 24rpx;
color: #666;
padding: 8rpx 16rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
}
/* uView组件样式覆盖 */
::v-deep .u-input {
.u-input__input {
height: 88rpx !important;
font-size: 32rpx !important;
@media (max-width: 750px) {
height: 80rpx !important;
font-size: 28rpx !important;
}
@media (min-width: 768px) {
height: 100rpx !important;
font-size: 36rpx !important;
}
}
}
::v-deep .u-button {
height: 88rpx !important;
font-size: 28rpx !important;
@media (max-width: 750px) {
height: 80rpx !important;
font-size: 26rpx !important;
}
@media (min-width: 768px) {
height: 100rpx !important;
font-size: 32rpx !important;
}
}
::v-deep .u-modal {
@media (max-width: 750px) {
.u-modal__content {
width: 90% !important;
margin-top: 15vh !important;
}
}
}
::v-deep .u-radio-group {
.u-radio {
margin-bottom: 20rpx !important;
@media (max-width: 750px) {
width: 100% !important;
}
}
}
</style>