钉钉管理端添加访客管理界面

This commit is contained in:
2026-03-13 11:50:06 +08:00
parent 783364b695
commit d73e8bff58
6 changed files with 496 additions and 193 deletions
+6
View File
@@ -86,6 +86,12 @@ const routes = [
component: () => import("../views/specialCrowd.vue"),
meta: { title: "特殊人员录入" },
},
{
path: "/visitorManage",
name: "visitorManage",
component: () => import("../views/visitorManage.vue"),
meta: { title: "访客管理" },
},
{
path: "/",
name: "",
+26 -1
View File
@@ -1,7 +1,10 @@
<template>
<div class="box">
<header>
<h2>门禁人脸信息</h2>
<div class="header-top">
<h2>门禁人脸信息</h2>
<button @click="router.push('/visitorManage')" class="visitor-manage-btn">访客管理</button>
</div>
<h3><img src="../assets/img/dunpai.png" alt=""> 公司门禁系统人脸上传和查看 (v{{ appVersion }})</h3>
<div class="entering-box">
<img src="../assets/img/shenfenzheng.png" alt="">
@@ -70,6 +73,7 @@ const form = reactive({ //
})
const token = ref('')
const isLoading = ref(false)
localStorage.setItem('redirect', '/home')
token.value = localStorage.getItem('token')
@@ -537,4 +541,25 @@ if (token.value) {
}
}
}
// 访客管理按钮
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.64rem 0.4rem 0;
h2 {
padding: 0;
}
.visitor-manage-btn {
background: #fff;
color: #1989fa;
border: none;
padding: 0.2rem 0.4rem;
border-radius: 0.2rem;
font-size: 0.37rem;
}
}
</style>
+177
View File
@@ -0,0 +1,177 @@
<template>
<div class="visitor-manage-page">
<div class="status-tabs">
<span :class="{ active: visitorStatus === 1 }" @click="changeVisitorStatus(1)">已通过</span>
<span :class="{ active: visitorStatus === 0 }" @click="changeVisitorStatus(0)">待审批</span>
<span :class="{ active: visitorStatus === 2 }" @click="changeVisitorStatus(2)">已拒绝</span>
</div>
<van-loading v-if="visitorLoading" />
<div v-else class="visitor-list">
<div v-for="item in visitorRecordList" :key="item.id" class="visitor-item">
<div class="visitor-info">
<p><span>访客姓名</span>{{ item.name }}</p>
<p><span>联系方式</span>{{ item.phone }}</p>
<p><span>来访时间</span>{{ item.hours }}</p>
<p><span>被访人</span>{{ item.visitingName }}</p>
<p><span>来访事由</span>{{ item.reason }}</p>
</div>
<div class="visitor-status" :class="visitorStatusMap[item.status]?.className || 'default'">
{{ visitorStatusMap[item.status]?.name || '未知' }}
</div>
</div>
<div v-if="visitorRecordList.length === 0" class="no-data">暂无数据</div>
</div>
</div>
</template>
<script setup>
import {onMounted, reactive, ref} from 'vue'
import {getVisitorRecord} from '@/api/admin.js'
import {Toast} from 'vant'
const visitorRecordList = ref([])
const visitorLoading = ref(false)
const visitorStatus = ref(1)
const visitorQuery = reactive({
status: 1,
size: 100,
current: 1
})
const visitorStatusMap = {
0: {name: '待审批', className: 'wait'},
1: {name: '已通过', className: 'pass'},
2: {name: '已拒绝', className: 'reject'},
11: {name: '待到访', className: 'pending'},
12: {name: '已到访', className: 'visited'},
90: {name: '待审批', className: 'wait'},
91: {name: '已通过', className: 'pass'},
99: {name: '已完成', className: 'finished'}
}
const fetchVisitorRecords = () => {
visitorLoading.value = true
getVisitorRecord(visitorQuery).then(res => {
visitorRecordList.value = res.data || []
visitorLoading.value = false
}).catch(err => {
console.error('获取访客记录失败:', err)
visitorLoading.value = false
Toast.fail('获取访客记录失败')
})
}
const changeVisitorStatus = (status) => {
visitorStatus.value = status
visitorQuery.status = status
fetchVisitorRecords()
}
onMounted(() => {
fetchVisitorRecords()
})
</script>
<style lang="scss" scoped>
.visitor-manage-page {
padding: 0.4rem;
height: 100%;
overflow-y: auto;
background: #f5f5f5;
.status-tabs {
display: flex;
gap: 0.3rem;
margin-bottom: 0.4rem;
span {
flex: 1;
text-align: center;
padding: 0.2rem;
background: #fff;
border-radius: 0.2rem;
font-size: 0.37rem;
&.active {
background: #1989fa;
color: #fff;
}
}
}
.visitor-list {
.visitor-item {
background: #fff;
border-radius: 0.2rem;
padding: 0.3rem;
margin-bottom: 0.3rem;
display: flex;
justify-content: space-between;
align-items: center;
.visitor-info {
flex: 1;
p {
margin: 0.1rem 0;
font-size: 0.32rem;
color: #666;
span {
color: #333;
}
}
}
.visitor-status {
padding: 0.1rem 0.2rem;
border-radius: 0.1rem;
font-size: 0.28rem;
min-width: 1.2rem;
text-align: center;
&.wait {
background: #fff7cc;
color: #e6a23c;
}
&.pass {
background: #f0f9eb;
color: #67c23a;
}
&.reject {
background: #fef0f0;
color: #f56c6c;
}
&.pending {
background: #ecf5ff;
color: #409eff;
}
&.visited {
background: #f0f9eb;
color: #67c23a;
}
&.finished {
background: #f5f5f5;
color: #999;
}
&.default {
background: #f5f5f5;
color: #666;
}
}
}
.no-data {
text-align: center;
padding: 1rem;
color: #999;
}
}
}
</style>