访客页面

This commit is contained in:
zjf committed 2025-04-17 15:02:03 +08:00
1 parent 40de72ee99
commit f1e4ba7d31
9 files changed
+783 -447

No files matched your search

+13 -2
View File
@@ -8,6 +8,8 @@
/* 5+App特有相关 */
"app-plus": {
"usingComponents": true,
"nvueCompiler": "uni-app",
"renderer": "native",
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
@@ -53,9 +55,18 @@
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
"urlCheck": false,
"es6": true,
"postcss": true,
"minified": true
},
"usingComponents": true
"usingComponents": true,
"requiredPrivateInfos": ["getLocation"],
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
},
"mp-alipay": {
"usingComponents": true
+105 -56
View File
@@ -9,34 +9,34 @@
}
},
"pages": [
{
"path": "pages/tabbar/canteen",
"style": {
"navigationBarTitleText": "食堂",
"disableScroll": true
}
},
{
"path": "pages/tabbar/selfMerchant",
"style": {
"navigationBarTitleText": "自营商家",
"disableScroll": true
}
},
{
"path": "pages/tabbar/coopMerchant",
"style": {
"navigationBarTitleText": "合作商家",
"disableScroll": true
}
},
{
"path": "pages/tabbar/order",
"style": {
"navigationBarTitleText": "订单",
"disableScroll": true
}
},
// {
// "path": "pages/tabbar/canteen",
// "style": {
// "navigationBarTitleText": "食堂",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/selfMerchant",
// "style": {
// "navigationBarTitleText": "自营商家",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/coopMerchant",
// "style": {
// "navigationBarTitleText": "合作商家",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/order",
// "style": {
// "navigationBarTitleText": "订单",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/flowStatistics",
// "style": {
@@ -64,6 +64,12 @@
"navigationBarTitleText": "我的",
"disableScroll": true
}
},
{
"path": "pages/integrated/visitor/verify",
"style": {
"navigationStyle": "custom"
}
}
],
"subPackages": [
@@ -380,6 +386,18 @@
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/visitorDetail",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "记录详情"
}
}
]
}
],
"globalStyle": {
@@ -392,47 +410,77 @@
"tabBar": {
"list": [
{
"pagePath": "pages/tabbar/canteen",
"text": "",
"pagePath": "pages/integrated/visitor/index",
"text": "访客申请",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/selfMerchant",
"text": "",
"pagePath": "pages/integrated/visitor/myvisitor",
"text": "申请记录",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/coopMerchant",
"text": "",
"pagePath": "pages/integrated/visitor/myyyvisitor",
"text": "访客邀约",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/order",
"text": "",
"pagePath": "pages/integrated/visitor/myyyvisitorlist",
"text": "邀约记录",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/flowStatistics",
"text": "",
"pagePath": "pages/integrated/visitor/verify",
"text": "核销",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/individualOrder",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/my",
"text": "",
"iconPath": "",
"selectedIconPath": ""
}
// {
// "pagePath": "pages/tabbar/canteen",
// "text": "",
// "iconPath": "",
// "selectedIconPath": ""
// },
// {
// "pagePath": "pages/tabbar/selfMerchant",
// "text": "",
// "iconPath": "",
// "selectedIconPath": ""
// },
// {
// "pagePath": "pages/tabbar/coopMerchant",
// "text": "",
// "iconPath": "",
// "selectedIconPath": ""
// },
// {
// "pagePath": "pages/tabbar/order",
// "text": "",
// "iconPath": "",
// "selectedIconPath": ""
// },
// {
// "pagePath": "pages/tabbar/flowStatistics",
// "text": "",
// "iconPath": "",
// "selectedIconPath": ""
// },
// {
// "pagePath": "pages/tabbar/individualOrder",
// "text": "",
// "iconPath": "",
// "selectedIconPath": ""
// },
// {
// "pagePath": "pages/tabbar/my",
// "text": "",
// "iconPath": "",
// "selectedIconPath": ""
// }
// {
// "pagePath": "pages/tabbar/canteen",
// "text": "食堂",
@@ -469,12 +517,13 @@
// "iconPath": "static/tabbar/order.png",
// "selectedIconPath": "static/tabbar/order-select.png"
// },
// {
// "pagePath": "pages/tabbar/my",
// "text": "我的",
// "iconPath": "static/tabbar/my.png",
// "selectedIconPath": "static/tabbar/my-select.png"
// },
{
"pagePath": "pages/tabbar/my",
"text": "我的",
"iconPath": "static/tabbar/my.png",
"selectedIconPath": "static/tabbar/my-select.png"
}
]
}
}
+270 -247
View File
@@ -14,164 +14,160 @@
scroll-y
class="page-content"
>
<!-- 提示信息 -->
<view class="tips-box">
<text class="tips-icon">ℹ</text>
<text class="tips-text">入园预约请填写以下信息:</text>
</view>
<!-- 表单内容 -->
<view class="form-content">
<view class="form-item">
<text class="label">您的姓名</text>
<text class="required">*必填</text>
<input
type="text"
v-model="formData.name"
placeholder="请输入姓名"
placeholder-class="placeholder"
class="input"
/>
<view class="content-wrapper">
<view class="tips-box">
<text class="tips-icon">ℹ</text>
<text class="tips-text">入园预约请填写以下信息:</text>
</view>
<view class="form-item">
<text class="label">联系方式</text>
<text class="required">*必填</text>
<input
type="text"
v-model="formData.mobile"
placeholder="请输入手机号"
placeholder-class="placeholder"
class="input"
/>
</view>
<view class="form-item">
<text class="label">公司名称</text>
<text class="required">*</text>
<input
type="text"
v-model="formData.companyName"
placeholder="请输入公司名称"
placeholder-class="placeholder"
class="input"
/>
</view>
<view class="form-item">
<text class="label">拜访原因<text class="required">*</text></text>
<picker
:range="reasonList"
range-key="label"
@change="onReasonChange"
>
<view class="input-wrapper">
<text :class="['placeholder', formData.reasonValue ? 'active' : '']">
{{ formData.reasonLabel || '请选择拜访原因' }}
</text>
<text class="arrow">></text>
</view>
</picker>
</view>
<view class="form-item switch-item">
<text class="label">是否有随行人员</text>
<switch
:checked="formData.isOtherPeople === '1'"
@change="onSwitchOtherPeople"
style="transform:scale(0.9)"
/>
</view>
<!-- 随行人员信息 -->
<block v-if="formData.isOtherPeople === '1'">
<view v-for="(person, index) in otherPeople" :key="index" class="companion-item">
<view class="companion-inputs">
<input
type="text"
v-model="person.name"
placeholder="姓名"
class="companion-input"
placeholder-class="placeholder"
/>
<input
type="text"
v-model="person.mobile"
placeholder="手机号"
class="companion-input"
placeholder-class="placeholder"
/>
</view>
<text v-if="index > 0" class="delete-text" @tap="removeOtherPeople(index)">删除</text>
</view>
<view v-if="otherPeople.length < 5" class="add-btn" @tap="addOtherPeople">
<text class="add-icon">+</text>
<text class="add-text">添加随行人员</text>
</view>
</block>
<view class="form-item switch-item">
<text class="label">是否驾驶车辆</text>
<switch
:checked="formData.isDrive === '1'"
@change="onSwitchDrive"
style="transform:scale(0.9)"
/>
</view>
<!-- 车牌信息 -->
<block v-if="formData.isDrive === '1'">
<view v-for="(car, index) in carList" :key="index" class="car-item">
<!-- 表单内容 -->
<view class="form-content">
<view class="form-item">
<text class="label">您的姓名</text>
<text class="required">*必填</text>
<input
type="text"
v-model="car.carNumber"
placeholder="请输入车牌号"
class="car-input"
v-model="formData.name"
placeholder="请输入姓名"
placeholder-class="placeholder"
class="input"
/>
<text v-if="index > 0" class="delete-text" @tap="removeCarNumber(index)">删除</text>
</view>
<view v-if="carList.length < 2" class="add-btn" @tap="addCarNumber">
<text class="add-icon">+</text>
<text class="add-text">添加车牌号</text>
</view>
</block>
<view class="form-item select-item" @tap="showDeptPicker">
<text class="label">到访单位</text>
<view class="select-right">
<text class="select-value">{{ selectedDeptName || '请选择' }}</text>
<text class="select-arrow">></text>
<view class="form-item">
<text class="label">联系方式</text>
<text class="required">*必填</text>
<input
type="text"
v-model="formData.mobile"
placeholder="请输入手机号"
placeholder-class="placeholder"
class="input"
/>
</view>
</view>
<view class="form-item select-item" @tap="showUserPicker">
<text class="label">接待人</text>
<text class="required">*必填</text>
<view class="select-right">
<text class="select-value">{{ selectedUserName || '请选择' }}</text>
<text class="select-arrow">></text>
<view class="form-item">
<text class="label">公司名称</text>
<text class="required">*</text>
<input
type="text"
v-model="formData.companyName"
placeholder="请输入公司名称"
placeholder-class="placeholder"
class="input"
/>
</view>
</view>
<view class="form-item select-item" @tap="showTimePicker">
<text class="label">到访时间</text>
<text class="required">*必填</text>
<view class="select-right">
<text class="select-value">{{ selectedTime || '请选择' }}</text>
<text class="select-arrow">></text>
<view class="form-item">
<text class="label">拜访原因<text class="required">*</text></text>
<picker
:range="reasonList"
range-key="label"
@change="onReasonChange"
>
<view class="input-wrapper">
<text :class="['placeholder', formData.reasonValue ? 'active' : '']">
{{ formData.reasonLabel || '请选择拜访原因' }}
</text>
<text class="arrow">></text>
</view>
</picker>
</view>
<view class="form-item switch-item">
<text class="label">是否有随行人员</text>
<switch
:checked="formData.isOtherPeople === '1'"
@change="onSwitchOtherPeople"
style="transform:scale(0.9)"
/>
</view>
<!-- 随行人员信息 -->
<block v-if="formData.isOtherPeople === '1'">
<view v-for="(person, index) in otherPeople" :key="index" class="companion-item">
<view class="companion-inputs">
<input
type="text"
v-model="person.name"
placeholder="姓名"
class="companion-input"
placeholder-class="placeholder"
/>
<input
type="text"
v-model="person.mobile"
placeholder="手机号"
class="companion-input"
placeholder-class="placeholder"
/>
</view>
<text v-if="index > 0" class="delete-text" @tap="removeOtherPeople(index)">删除</text>
</view>
<view v-if="otherPeople.length < 5" class="add-btn" @tap="addOtherPeople">
<text class="add-icon">+</text>
<text class="add-text">添加随行人员</text>
</view>
</block>
<view class="form-item switch-item">
<text class="label">是否驾驶车辆</text>
<switch
:checked="formData.isDrive === '1'"
@change="onSwitchDrive"
style="transform:scale(0.9)"
/>
</view>
<!-- 车牌信息 -->
<block v-if="formData.isDrive === '1'">
<view v-for="(car, index) in carList" :key="index" class="car-item">
<input
type="text"
v-model="car.carNumber"
placeholder="请输入车牌号"
class="car-input"
placeholder-class="placeholder"
/>
<text v-if="index > 0" class="delete-text" @tap="removeCarNumber(index)">删除</text>
</view>
<view v-if="carList.length < 2" class="add-btn" @tap="addCarNumber">
<text class="add-icon">+</text>
<text class="add-text">添加车牌号</text>
</view>
</block>
<view class="form-item select-item" @tap="showDeptPicker">
<text class="label">到访单位</text>
<view class="select-right">
<text class="select-value">{{ selectedDeptName || '请选择' }}</text>
<text class="select-arrow">></text>
</view>
</view>
<view class="form-item select-item" @tap="showUserPicker">
<text class="label">接待人</text>
<text class="required">*必填</text>
<view class="select-right">
<text class="select-value">{{ selectedUserName || '请选择' }}</text>
<text class="select-arrow">></text>
</view>
</view>
<view class="form-item select-item" @tap="showTimePicker">
<text class="label">到访时间</text>
<text class="required">*必填</text>
<view class="select-right">
<text class="select-value">{{ selectedTime || '请选择' }}</text>
<text class="select-arrow">></text>
</view>
</view>
</view>
</view>
<!-- 底部占位,确保内容不被按钮遮挡 -->
<view class="bottom-space"></view>
</scroll-view>
<!-- 底部按钮区域 -->
<view class="bottom-safe-area">
<button class="submit-btn" @tap="handleSubmit">提交</button>
</view>
<!-- 提交按钮 -->
<button class="submit-btn" @tap="handleSubmit">提交</button>
<!-- 部门选择弹窗 -->
<view class="popup" v-if="showDeptPopup">
@@ -296,28 +292,33 @@ import deptApi from '@/sheep/api/integrated/dept'
// 表单数据
const formData = reactive({
name: '',
mobile: '',
companyName: '',
isOtherPeople: '0',
isDrive: '0',
deptId: '',
name: '测试访客',
mobile: '13800138000',
companyName: '测试公司',
isOtherPeople: '1', // 默认有随行人员
isDrive: '1', // 默认有车辆
deptId: '', // 这个需要等部门列表加载后设置
deptName: '',
userId: '',
userId: '', // 这个需要等用户列表加载后设置
userName: '',
vStartDate: '',
vEndDate: '',
vstartDate: '', // 这个会在页面加载时设置
vendDate: '',
type: '1',
checkStatus: '0',
reasonValue: '',
reasonValue: '', // 这个需要等原因列表加载后设置
reasonLabel: '',
})
// 随行人员列表
const otherPeople = ref<Array<{name: string, mobile: string}>>([])
// 随行人员列表默认数据
const otherPeople = ref([
{ name: '随行人员1', mobile: '13800138001' },
{ name: '随行人员2', mobile: '13800138002' }
])
// 车牌列表
const carList = ref<Array<{carNumber: string}>>([])
// 车牌列表默认数据
const carList = ref([
{ carNumber: '沪A12345' }
])
// 弹窗控制
const showDeptPopup = ref(false)
@@ -451,35 +452,45 @@ const goBack = () => {
uni.navigateBack()
}
// 提交表单
const handleSubmit = async () => {
// 表单验证
// 表单验证
const validateForm = () => {
if (!formData.name) {
uni.showToast({ title: '请输入姓名', icon: 'none' })
return
return false
}
if (!formData.mobile) {
uni.showToast({ title: '请输入联系方式', icon: 'none' })
return
return false
}
if (!formData.deptId || !formData.deptName) {
uni.showToast({ title: '请选择到访单位', icon: 'none' })
return
return false
}
if (!formData.userId || !formData.userName) {
uni.showToast({ title: '请选择接待人', icon: 'none' })
return
return false
}
if (!formData.vStartDate || !formData.vEndDate) {
if (!formData.vstartDate || !formData.vendDate) {
uni.showToast({ title: '请选择到访时间', icon: 'none' })
return
return false
}
if (!formData.reasonValue || !formData.reasonLabel) {
uni.showToast({ title: '请选择拜访原因', icon: 'none' })
return
return false
}
return true
}
// 提交表单
const handleSubmit = async () => {
if (!validateForm()) return
try {
uni.showLoading({
title: '提交中...',
mask: true
})
const submitData = {
...formData,
peopleNumber: formData.isOtherPeople === '1' ? otherPeople.value.length : 0,
@@ -488,16 +499,32 @@ const handleSubmit = async () => {
}
const { code, msg } = await visitorApi.save(submitData)
uni.hideLoading()
if (code === 0) {
uni.showToast({ title: '提交成功', icon: 'success' })
uni.showToast({
title: '提交成功,请耐心等待您的拜访对象接受此次申请',
icon: 'none',
duration: 3000
})
// 延迟返回,让用户能看到提示
setTimeout(() => {
uni.navigateBack()
}, 1500)
}, 2000)
} else {
uni.showToast({ title: msg || '提交失败', icon: 'none' })
uni.showToast({
title: msg || '提交失败',
icon: 'none'
})
}
} catch (error) {
uni.showToast({ title: '提交失败', icon: 'none' })
uni.hideLoading()
uni.showToast({
title: '提交失败',
icon: 'none'
})
}
}
@@ -536,8 +563,8 @@ const confirmTimeSelect = () => {
// 更新表单数据
const [startTime, endTime] = selectedTimeRange.split('-')
formData.vStartDate = `${selectedDate.fullDate} ${startTime}:00`
formData.vEndDate = `${selectedDate.fullDate} ${endTime}:00`
formData.vstartDate = `${selectedDate.fullDate} ${startTime}:00`
formData.vendDate = `${selectedDate.fullDate} ${endTime}:00`
// 更新显示文本
selectedTime.value = `${selectedDate.weekDay} ${selectedDate.date} ${selectedTimeRange}`
@@ -677,10 +704,50 @@ const onReasonChange = (e: any) => {
formData.reasonLabel = selected.label
}
// 页面加载时获取部门数据
onMounted(() => {
getDeptList()
getReasonList()
// 在页面加载时设置默认值
onMounted(async () => {
// 获取部门数据
await getDeptList()
// 获取拜访原因列表
await getReasonList()
// 设置默认的部门(选择第一个部门)
if (flatDeptList.value.length > 0) {
const firstDept = flatDeptList.value[0]
selectedDeptId.value = firstDept.id
selectedDeptName.value = firstDept.name
formData.deptId = firstDept.id
formData.deptName = firstDept.name
// 获取该部门的用户列表
await getUserList(firstDept.id)
// 设置默认的接待人(选择第一个用户)
if (userList.value.length > 0) {
const firstUser = userList.value[0]
selectedUserId.value = firstUser.userId
selectedUserName.value = firstUser.name
formData.userId = firstUser.userId
formData.userName = firstUser.name
}
}
// 设置默认的拜访原因(选择第一个原因)
if (reasonList.value.length > 0) {
const firstReason = reasonList.value[0]
formData.reasonValue = firstReason.value
formData.reasonLabel = firstReason.label
}
// 设置默认的时间(选择今天的第一个时间段)
selectedDateIndex.value = 0
selectedTimeIndex.value = 0
const selectedDate = dateList.value[0]
const selectedTimeRange = timeList.value[0]
const [startTime, endTime] = selectedTimeRange.split('-')
formData.vstartDate = `${selectedDate.fullDate} ${startTime}:00`
formData.vendDate = `${selectedDate.fullDate} ${endTime}:00`
selectedTime.value = `${selectedDate.weekDay} ${selectedDate.date} ${selectedTimeRange}`
})
</script>
@@ -690,6 +757,7 @@ onMounted(() => {
background: #fff;
display: flex;
flex-direction: column;
position: relative;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
}
@@ -732,6 +800,12 @@ onMounted(() => {
height: 0;
}
.content-wrapper {
min-height: 100%;
padding: 0 0 calc(240rpx + env(safe-area-inset-bottom));
background: #fff;
}
.tips-box {
padding: 24rpx 30rpx;
background: #FFF7E6;
@@ -863,7 +937,7 @@ onMounted(() => {
position: fixed;
left: 30rpx;
right: 30rpx;
bottom: 40rpx;
bottom: calc(120rpx + env(safe-area-inset-bottom));
height: 88rpx;
background: #1989FA;
border-radius: 44rpx;
@@ -875,6 +949,7 @@ onMounted(() => {
font-weight: 500;
letter-spacing: 1rpx;
box-shadow: 0 4rpx 8rpx rgba(25, 137, 250, 0.12);
z-index: 99;
}
// 优化switch样式
@@ -908,10 +983,12 @@ onMounted(() => {
position: absolute;
left: 0;
right: 0;
bottom: 0;
bottom: 100rpx;
background: #fff;
border-radius: 24rpx 24rpx 0 0;
padding-bottom: env(safe-area-inset-bottom);
max-height: 80vh;
display: flex;
flex-direction: column;
}
}
@@ -998,8 +1075,11 @@ onMounted(() => {
}
.popup-footer {
position: relative;
padding: 24rpx 32rpx;
background: #fff;
border-top: 1rpx solid #f5f5f5;
margin-bottom: env(safe-area-inset-bottom);
.confirm-btn {
height: 88rpx;
@@ -1014,10 +1094,12 @@ onMounted(() => {
}
}
// 添加部门选择相关样式
.dept-list {
flex: 1;
max-height: 60vh;
padding: 20rpx 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.dept-item {
@@ -1043,6 +1125,14 @@ onMounted(() => {
}
}
// 增加底部空间
.dept-list::after {
content: '';
display: block;
height: 400rpx; // 增加更多的底部空间
width: 100%;
}
// 添加用户选择相关样式
.search-box {
padding: 20rpx 32rpx;
@@ -1089,74 +1179,7 @@ onMounted(() => {
font-size: 28rpx;
}
// 底部占位
.bottom-space {
height: 180rpx;
}
.picker-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 32rpx;
background-color: #f7f7f7;
text {
font-size: 28rpx;
&:first-child {
color: #666;
}
&:last-child {
color: #1989fa;
}
}
}
.picker-view {
width: 100%;
height: 392rpx;
background-color: #fff;
}
.picker-item {
line-height: 88rpx;
text-align: center;
font-size: 32rpx;
}
.placeholder {
color: #999;
&.active {
color: #333;
}
}
.arrow {
color: #999;
font-size: 28rpx;
}
.bottom-safe-area {
padding: 20rpx 32rpx;
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom)); /* iOS 11.0 */
padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); /* iOS 11.2 */
background: #fff;
}
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #1989fa;
color: #fff;
font-size: 32rpx;
border-radius: 44rpx;
&:active {
opacity: 0.8;
}
display: none;
}
</style>
+30 -12
View File
@@ -26,6 +26,7 @@
v-for="item in visitorList"
:key="item.id"
class="visitor-item"
@tap="goToDetail(item)"
>
<view class="item-header">
<text class="visit-time">{{ formatTime(item.vstartDate, item.vendDate) }}</text>
@@ -86,7 +87,7 @@ import { ref, onMounted } from 'vue'
import visitorApi from '@/sheep/api/integrated/visitors'
// 标签页配置
const tabs = ['全部', '待审核', '待到访', '已到访']
const tabs = ['全部', '待审核', '待到访', '已到访', '已取消']
const currentTab = ref(0)
// 列表数据
@@ -117,7 +118,8 @@ const getStatusText = (status: string) => {
const statusMap: Record<string, string> = {
'1': '待审核',
'2': '待到访',
'0': '已到访'
'0': '已到访',
'5': '已取消'
}
return statusMap[status] || status
}
@@ -127,7 +129,8 @@ const getStatusClass = (status: string) => {
const statusMap: Record<string, string> = {
'1': 'status-pending',
'2': 'status-waiting',
'0': 'status-visited'
'0': 'status-visited',
'5': 'status-cancelled'
}
return statusMap[status] || ''
}
@@ -160,7 +163,7 @@ const getVisitorList = async () => {
// 添加状态筛选
if (currentTab.value > 0) {
const statusMap = ['', '1', '2', '0']
const statusMap = ['', '1', '2', '0', '5']
params.checkStatus = statusMap[currentTab.value]
}
@@ -206,7 +209,7 @@ const loadMore = () => {
// 判断是否可以取消
const canCancel = (status: string) => {
return status === '1' || status === '2'
return status === '1' || status === '2' || status === '5'
}
// 判断是否可以标记到访
@@ -304,6 +307,13 @@ const handleVisit = (item: any) => {
})
}
// 跳转到详情页
const goToDetail = (item: any) => {
uni.navigateTo({
url: `/pages/integrated/visitor/visitorDetail?id=${item.id}&token=${item.token}`
})
}
onMounted(() => {
getVisitorList()
})
@@ -363,6 +373,9 @@ onMounted(() => {
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
&:active {
opacity: 0.8;
}
}
.item-header {
@@ -380,22 +393,27 @@ onMounted(() => {
.status-tag {
font-size: 24rpx;
padding: 4rpx 16rpx;
padding: 4rpx 12rpx;
border-radius: 4rpx;
&.status-pending {
color: #1989FA;
background: rgba(25, 137, 250, 0.1);
background: #FFF3E0;
color: #FF9800;
}
&.status-waiting {
color: #FF9900;
background: rgba(255, 153, 0, 0.1);
background: #E3F2FD;
color: #2196F3;
}
&.status-visited {
color: #07C160;
background: rgba(7, 193, 96, 0.1);
background: #E8F5E9;
color: #4CAF50;
}
&.status-cancelled {
background: #EEEEEE;
color: #9E9E9E;
}
}
+118 -45
View File
@@ -2,8 +2,8 @@
<view class="page-container">
<!-- 顶部状态切换 -->
<view class="status-tabs">
<view
v-for="(tab, index) in tabs"
<view
v-for="(tab, index) in tabs"
:key="index"
:class="['tab-item', { active: currentTab === tab.value }]"
@tap="handleTabChange(tab.value)"
@@ -13,10 +13,11 @@
</view>
<view class="visitor-list">
<view
v-for="(item, index) in visitorList"
:key="index"
<view
v-for="(item, index) in visitorList"
:key="item.id"
class="visitor-item"
@tap="goToDetail(item)"
>
<view class="visitor-header">
<text class="visit-time">{{ formatDateTime(item.vStartDate) }}</text>
@@ -41,7 +42,7 @@
</view>
<view class="info-row">
<text class="label">拜访时间:</text>
<text class="value">{{ formatDateTime(item.vStartDate) }} - {{ formatDateTime(item.vEndDate) }}</text>
<text class="value">{{ formatDateTime(item.vstartDate) }} - {{ formatDateTime(item.vendDate) }}</text>
</view>
<view class="info-row">
<text class="label">拜访原因:</text>
@@ -68,6 +69,30 @@
<view class="empty" v-if="!loading && visitorList.length === 0">
<text>暂无数据</text>
</view>
<!-- 底部固定导航 -->
<view class="bottom-nav">
<view class="nav-item">
<image src="/static/images/visitor/visitor-apply.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text active">访客申请</text>
</view>
<view class="nav-item">
<image src="/static/images/visitor/visitor-record.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text">申请记录</text>
</view>
<view class="nav-item">
<image src="/static/images/visitor/visitor-invite.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text">主动邀约</text>
</view>
<view class="nav-item">
<image src="/static/images/visitor/visitor-invited.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text">邀约记录</text>
</view>
<view class="nav-item">
<image src="/static/images/visitor/visitor-my.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text">我的</text>
</view>
</view>
</view>
</template>
@@ -76,6 +101,15 @@ import { ref, onMounted } from 'vue'
import visitorApi from '@/sheep/api/integrated/visitors'
import dayjs from 'dayjs'
// 定义响应式变量
const visitorList = ref<any[]>([])
const loading = ref(false)
const hasMore = ref(true)
const current = ref(1)
const size = ref(10)
const currentTab = ref('')
const reasonDict = ref<any[]>([])
const tabs = [
{ label: '全部', value: '' },
{ label: '待审核', value: '1' },
@@ -83,14 +117,6 @@ const tabs = [
{ label: '已取消', value: '5' }
]
const currentTab = ref('')
const visitorList = ref<any[]>([])
const loading = ref(false)
const hasMore = ref(true)
const current = ref(1)
const size = ref(10)
const reasonDict = ref<any[]>([])
// 获取拜访原因字典
const getReasonDict = async () => {
try {
@@ -171,14 +197,14 @@ const getVisitorList = async (isRefresh = false) => {
size: size.value,
checkStatus: currentTab.value || undefined
})
if (code === 0 && data) {
if (isRefresh) {
visitorList.value = data.records || []
} else {
visitorList.value = [...visitorList.value, ...(data.records || [])]
}
hasMore.value = data.records.length === size.value
if (hasMore.value) {
current.value++
@@ -299,12 +325,28 @@ const handleReject = async (item: any) => {
})
}
// 跳转到详情页
const goToDetail = (item: any) => {
uni.navigateTo({
url: `/pages/integrated/visitor/visitorDetail?token=${item.token}`
})
}
// 页面生命周期钩子
onMounted(() => {
getReasonDict()
getVisitorList(true)
})
// 页面配置
defineOptions({
navigationBarTitleText: '我的邀约',
enablePullDownRefresh: true,
backgroundColor: '#f5f5f5'
})
// 下拉刷新
defineExpose({
async onLoad() {
await getReasonDict()
getVisitorList(true)
},
onPullDownRefresh() {
getVisitorList(true).then(() => {
uni.stopPullDownRefresh()
@@ -314,19 +356,13 @@ defineExpose({
getVisitorList()
}
})
// 页面配置
defineOptions({
navigationBarTitleText: '我的邀约',
enablePullDownRefresh: true,
backgroundColor: '#f5f5f5'
})
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx; // 为底部导航留出空间
}
.status-tabs {
@@ -334,7 +370,7 @@ defineOptions({
background-color: #fff;
padding: 20rpx 30rpx;
margin-bottom: 20rpx;
.tab-item {
flex: 1;
text-align: center;
@@ -342,11 +378,11 @@ defineOptions({
color: #666;
position: relative;
padding: 20rpx 0;
&.active {
color: #1777FF;
font-weight: 500;
&::after {
content: '';
position: absolute;
@@ -364,62 +400,63 @@ defineOptions({
.visitor-list {
padding: 0 20rpx;
padding-bottom: 20rpx; // 为底部添加一些间距
.visitor-item {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
padding: 24rpx;
.visitor-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.visit-time {
font-size: 28rpx;
color: #333;
}
.status-tag {
padding: 4rpx 16rpx;
border-radius: 4rpx;
font-size: 24rpx;
&.status-pending {
background-color: #fff7e6;
color: #ffa940;
}
&.status-waiting {
background-color: #e6f7ff;
color: #1890ff;
}
&.status-visited {
background-color: #f6ffed;
color: #52c41a;
}
&.status-cancelled {
background-color: #f5f5f5;
color: #999;
}
}
}
.visitor-content {
.info-row {
display: flex;
margin-bottom: 16rpx;
.label {
width: 160rpx;
color: #666;
font-size: 28rpx;
}
.value {
flex: 1;
color: #333;
@@ -427,19 +464,19 @@ defineOptions({
}
}
}
.visitor-footer {
margin-top: 20rpx;
display: flex;
justify-content: flex-end;
gap: 20rpx;
.action-btn {
padding: 12rpx 24rpx;
border: 1rpx solid #ddd;
border-radius: 4rpx;
font-size: 26rpx;
&:active {
opacity: 0.8;
}
@@ -469,4 +506,40 @@ defineOptions({
color: #999;
font-size: 26rpx;
}
</style>
.bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #fff;
display: flex;
justify-content: space-around;
align-items: center;
border-top: 1rpx solid #eee;
padding-bottom: env(safe-area-inset-bottom); // 适配iPhone底部安全区
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 10rpx 0;
.nav-icon {
width: 44rpx;
height: 44rpx;
margin-bottom: 4rpx;
}
.nav-text {
font-size: 20rpx;
color: #999;
&.active {
color: #1777FF;
}
}
}
}
</style>
+8 -6
View File
@@ -70,16 +70,18 @@ const state = reactive<{
wallet: [],
userInfo: {},
menuList: [
{ text: "扫一扫", icon: "/static/icon/scan.png", router: "" },
{ text: "钱包管理", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
// { text: "扫一扫", icon: "/static/icon/scan.png", router: "" },
// { text: "钱包管理", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
// { text: "包厢预定", icon: "/static/icon/service_4.png", router: "/pages/boxes/apply-list" },
// { text: "外来人员就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/foreign-list" },
// { text: "跨单位就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/stride-list" },
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
{ text: "访客", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/index" },
{ text: "访客记录", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myvisitor" },
{ text: "访客邀约", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myyyvisitor" },
{ text: "邀约记录", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myyyvisitorlist" },
// { text: "访客", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/index" },
// { text: "访客记录", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myvisitor" },
// { text: "访客邀约", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myyyvisitor" },
// { text: "邀约记录", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myyyvisitorlist" },
// { text: "访客核销", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/verify" },
// { text: "意见反馈", icon: "/static/icon/service_5.png", router: "/pages/integrated/feedback/index" }
],
appInfo: "",
+18
View File
@@ -117,6 +117,24 @@ const visitorApi = {
showLoading: false,
},
}),
// 获取访客详情
getVisitorDetail: (params: { token: string }) => {
return request({
url: '/integrated/visitors/app/get/visitorDetail',
method: 'GET',
params
})
},
// 访客核销
verifyVisitor: (params: { token: string }) => {
return request({
url: '/integrated/visitors/app/verify',
method: 'POST',
data: params
})
}
}
export default visitorApi