764 lines
19 KiB
Vue
764 lines
19 KiB
Vue
<template>
|
||
<scroll-view class="container" scroll-y>
|
||
<view class="form-card">
|
||
<view class="form-title">访客预约表单</view>
|
||
|
||
<!-- 基本信息 -->
|
||
<view class="section-title">基本信息</view>
|
||
<view class="form-item">
|
||
<text class="label required">访客姓名</text>
|
||
<view class="input-wrapper">
|
||
<input type="text" placeholder="请输入您的姓名" v-model="formData.visitorName" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label required">证件号</text>
|
||
<view class="input-wrapper">
|
||
<input type="text" placeholder="请输入您的证件号" v-model="formData.idCard" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label">所属公司</text>
|
||
<view class="input-wrapper">
|
||
<input type="text" placeholder="请输入您的公司名称" v-model="formData.visitorUnit" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label required">联系电话</text>
|
||
<view class="input-wrapper">
|
||
<input type="number" placeholder="请输入您的手机号码" v-model="formData.visitorPhone" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label required">拜访事由</text>
|
||
<!-- <view class="input-wrapper">
|
||
<input type="text" placeholder="请简要描述拜访目的" v-model="formData.visitingReason" />
|
||
</view> -->
|
||
<view class="select-wrapper" :class="{active: showTypeDialog}" @click.stop="showTypeDialog = true">
|
||
<text>{{ formData.visitingReason || '请选择拜访事由' }}</text>
|
||
<image class="filter-img" src="/static/ic_down_arrow_g.png" />
|
||
<view v-if="showTypeDialog" class="dropdown">
|
||
<view class="dropdown-list">
|
||
<view v-for="(item, index) in typeList" :key="index" class="dropdown-item"
|
||
@click.stop="selectVisitingReason(item)">
|
||
{{ item }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
<!-- 被访人信息 -->
|
||
<view class="section-title">被访人信息</view>
|
||
<view class="form-item">
|
||
<text class="label">被访人姓名</text>
|
||
<view class="input-wrapper">
|
||
<input type="text" placeholder="请输入被访人姓名" v-model="formData.interviewedPerson" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label">被访单位</text>
|
||
<view class="input-wrapper">
|
||
<input type="text" placeholder="请输入被访单位名称" v-model="formData.interviewedUnit" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label">被访人电话</text>
|
||
<view class="input-wrapper">
|
||
<input type="number" placeholder="请输入被访人联系电话" v-model="formData.interviewedPhone" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 拜访时间 -->
|
||
<view class="section-title">拜访时间</view>
|
||
<view class="form-item">
|
||
<text class="label">开始时间</text>
|
||
<view class="picker-wrapper">
|
||
<picker mode="date" :value="formData.visitingBeginDate" start="2020-01-01" end="2030-12-31"
|
||
@change="onBeginDateChange">
|
||
<view class="picker-value">{{ formData.visitingBeginDate }}</view>
|
||
</picker>
|
||
</view>
|
||
<view class="picker-wrapper time-picker">
|
||
<picker mode="time" :value="formData.visitingBeginTime" @change="onBeginTimeChange">
|
||
<view class="picker-value">{{ formData.visitingBeginTime }}</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label">结束时间</text>
|
||
<view class="picker-wrapper">
|
||
<picker mode="date" :value="formData.visitingEndDate" start="2020-01-01" end="2030-12-31"
|
||
@change="onEndDateChange">
|
||
<view class="picker-value">{{ formData.visitingEndDate }}</view>
|
||
</picker>
|
||
</view>
|
||
<view class="picker-wrapper time-picker">
|
||
<picker mode="time" :value="formData.visitingEndTime" @change="onEndTimeChange">
|
||
<view class="picker-value">{{ formData.visitingEndTime }}</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 预约信息 -->
|
||
<!-- <view class="section-title">预约信息</view>
|
||
<view class="form-item">
|
||
<text class="label">预约车位</text>
|
||
<view class="picker-wrapper">
|
||
<picker mode="selector" :range="parkingOptions" @change="handleParkingChange">
|
||
<view class="picker-value">{{formData.bookingParkingSpace ? '需要' : '不需要'}}</view>
|
||
</picker>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 条件显示:预约车位时显示车牌号 -->
|
||
<!-- <view class="form-item" v-if="formData.bookingParkingSpace">
|
||
<text class="label required">车牌号</text>
|
||
<view class="input-wrapper">
|
||
<input type="text" placeholder="请输入车牌号" v-model="formData.licensePlate" />
|
||
</view>
|
||
</view> -->
|
||
|
||
<view class="form-item">
|
||
<text class="label required">人脸照片</text>
|
||
<view class="upload-wrapper">
|
||
<view class="upload-btn" @click="takePhoto">
|
||
<text class="upload-text">+ 拍照上传</text>
|
||
</view>
|
||
<view class="preview-image" v-if="formData.facePictures">
|
||
<image :src="formData.facePictures" mode="aspectFill" @click="delimg"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- <view class="form-item">
|
||
<text class="label">预约状态</text>
|
||
<view class="picker-wrapper">
|
||
<picker mode="selector" :range="statusOptions" @change="handleStatusChange">
|
||
<view class="picker-value">{{statusMap[formData.serveStatus]}}</view>
|
||
</picker>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 提交按钮 -->
|
||
<view class="submit-wrapper">
|
||
<button class="submit-btn" @click="submitForm">确认提交</button>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
const now = new Date();
|
||
const date = now.getFullYear() + '-' +
|
||
(now.getMonth() + 1).toString().padStart(2, '0') + '-' +
|
||
now.getDate().toString().padStart(2, '0');
|
||
const time = now.getHours().toString().padStart(2, '0') + ':' +
|
||
now.getMinutes().toString().padStart(2, '0');
|
||
|
||
return {
|
||
formData: {
|
||
visitorName: '1',
|
||
visitorUnit: '1',
|
||
idCard: '',
|
||
visitorPhone: '15555555555',
|
||
visitingReason: '',
|
||
interviewedPerson: '1',
|
||
interviewedUnit: '1',
|
||
interviewedPhone: '15555555555',
|
||
visitingBeginDate: date,
|
||
visitingBeginTime: time,
|
||
visitingEndDate: date,
|
||
visitingEndTime: time,
|
||
bookingParkingSpace: false,
|
||
licensePlate: '',
|
||
facePictures: '',
|
||
serveStatus: 0
|
||
},
|
||
parkingOptions: ['不需要', '需要'],
|
||
statusOptions: ['待确认', '已确认', '已取消', '已完成'],
|
||
statusMap: {
|
||
0: '待确认',
|
||
1: '已确认',
|
||
2: '已取消',
|
||
3: '已完成'
|
||
},
|
||
typeList: ['业务洽谈', '会议参与', '面试应聘', '技术支持', '办事咨询', '调研考察'],
|
||
showTypeDialog: false
|
||
}
|
||
},
|
||
onLoad() {
|
||
// #ifdef APP-PLUS
|
||
plus.screen.lockOrientation('default');
|
||
// #endif
|
||
},
|
||
onReady() {
|
||
// #ifdef APP-PLUS
|
||
plus.screen.lockOrientation('landscape-primary');
|
||
// #endif
|
||
},
|
||
methods: {
|
||
// 添加选择拜访事由方法
|
||
selectVisitingReason(reason) {
|
||
this.formData.visitingReason = reason;
|
||
this.showTypeDialog = false;
|
||
},
|
||
|
||
// 点击外部关闭下拉菜单
|
||
handleClickOutside() {
|
||
this.showTypeDialog = false;
|
||
},
|
||
|
||
// 处理日期时间选择
|
||
onBeginDateChange(e) {
|
||
this.formData.visitingBeginDate = e.detail.value;
|
||
},
|
||
onBeginTimeChange(e) {
|
||
this.formData.visitingBeginTime = e.detail.value;
|
||
},
|
||
onEndDateChange(e) {
|
||
this.formData.visitingEndDate = e.detail.value;
|
||
},
|
||
onEndTimeChange(e) {
|
||
this.formData.visitingEndTime = e.detail.value;
|
||
},
|
||
|
||
// 处理车位预约选择
|
||
// handleParkingChange(e) {
|
||
// this.formData.bookingParkingSpace = e.detail.value === 1;
|
||
// if (!this.formData.bookingParkingSpace) {
|
||
// this.formData.licensePlate = '';
|
||
// }
|
||
// },
|
||
|
||
// 处理状态选择
|
||
handleStatusChange(e) {
|
||
this.formData.serveStatus = e.detail.value;
|
||
},
|
||
|
||
// 拍照
|
||
takePhoto() {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sourceType: ['camera'], // 只允许使用相机
|
||
success: (res) => {
|
||
// console.log(res.tempFilePaths[0])
|
||
this.formData.facePictures = res.tempFilePaths[0];
|
||
},
|
||
fail: (err) => {
|
||
uni.showToast({
|
||
title: '拍照失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 表单验证
|
||
validateForm() {
|
||
const {
|
||
qrCodeId,
|
||
visitorName,
|
||
visitorUnit,
|
||
idCard,
|
||
visitorPhone,
|
||
visitingReason,
|
||
interviewedPerson,
|
||
bookingParkingSpace,
|
||
licensePlate,
|
||
facePictures,
|
||
visitingBeginDate,
|
||
visitingBeginTime,
|
||
visitingEndDate,
|
||
visitingEndTime
|
||
} = this.formData;
|
||
|
||
// 验证姓名
|
||
if (!visitorName) {
|
||
return '请输入访客姓名';
|
||
}
|
||
|
||
// 验证公司
|
||
if (!idCard) {
|
||
return '请输入证件号';
|
||
}
|
||
|
||
// 验证电话
|
||
if (!visitorPhone) {
|
||
return '请输入联系电话';
|
||
}
|
||
|
||
// 简单的手机号格式验证
|
||
if (!/^1[3-9]\d{9}$/.test(visitorPhone)) {
|
||
return '请输入正确的手机号码';
|
||
}
|
||
|
||
// 验证事由
|
||
if (!visitingReason) {
|
||
return '请输入拜访事由';
|
||
}
|
||
|
||
// 验证被访人
|
||
// if (!interviewedPerson) {
|
||
// return '请输入被访人姓名';
|
||
// }
|
||
|
||
// 验证车牌号(如果需要预约车位)
|
||
// if (bookingParkingSpace && !licensePlate) {
|
||
// return '请输入车牌号';
|
||
// }
|
||
|
||
|
||
if (!facePictures) {
|
||
return '请拍照上传人脸照片';
|
||
}
|
||
|
||
// 验证时间逻辑
|
||
const beginDateTime = new Date(`${visitingBeginDate} ${visitingBeginTime}`);
|
||
const endDateTime = new Date(`${visitingEndDate} ${visitingEndTime}`);
|
||
|
||
if (beginDateTime >= endDateTime) {
|
||
return '开始时间必须早于结束时间';
|
||
}
|
||
|
||
return ''; // 验证通过
|
||
},
|
||
|
||
// 提交表单
|
||
submitForm() {
|
||
const errorMsg = this.validateForm();
|
||
if (errorMsg) {
|
||
uni.showToast({
|
||
title: errorMsg,
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 合并日期和时间
|
||
this.formData.visitingBeginTime = `${this.formData.visitingBeginDate} ${this.formData.visitingBeginTime}`;
|
||
this.formData.visitingEndTime = `${this.formData.visitingEndDate} ${this.formData.visitingEndTime}`;
|
||
|
||
|
||
|
||
// 显示加载提示
|
||
uni.showLoading({
|
||
title: '提交中...',
|
||
mask: true
|
||
});
|
||
|
||
// 模拟API请求
|
||
setTimeout(() => {
|
||
uni.request({
|
||
url: this.formData.facePictures
|
||
})
|
||
// .then(response => response.blob())
|
||
.then(blob => {
|
||
// 此时得到了原始Blob对象
|
||
console.log(blob);
|
||
const file = new File([blob], 'filename.png', {
|
||
type: blob.type
|
||
});
|
||
// 构建FormData进行上传
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
console.log(formData)
|
||
// 发送上传请求
|
||
// this.$u.api.uploadimg()
|
||
// 接上面的代码,假设Blob是图片类型
|
||
uni.uploadFile({
|
||
url: 'http://183.230.235.66:11010/api/resource/oss/qrupload', // 后端上传接口地址
|
||
filePath: file.filePath, // 要上传的文件路径
|
||
name: 'file', // 后端接收文件的参数名
|
||
// FormData中的其他参数
|
||
formData: {
|
||
'code': this.formData.qrCodeId // 示例:其他表单字段
|
||
},
|
||
// // 上传进度回调
|
||
// onProgressUpdate: (res) => {
|
||
// this.progress = res.progress;
|
||
// console.log('上传进度:' + res.progress);
|
||
// },
|
||
// 上传成功回调
|
||
success: (res) => {
|
||
console.log('上传成功', res);
|
||
|
||
// this.formData.facePictures = res.data.ossId;
|
||
// 准备提交数据
|
||
const submitData = {
|
||
...this.formData,
|
||
bookingParkingSpace: this.formData
|
||
.bookingParkingSpace ? 0 : 1
|
||
};
|
||
|
||
const parsedData = JSON.parse(res.data);
|
||
if (parsedData.code == 200) {
|
||
// 第二步:从解析后的数据中获取ossId
|
||
const ossId = parsedData.data.ossId;
|
||
console.log("ossId", ossId)
|
||
submitData.facePictures = ossId;
|
||
console.log(submitData)
|
||
this.$u.api.fksub(submitData).then(res => {
|
||
console.log(res)
|
||
if (res.code == 200) {
|
||
uni.showToast({
|
||
title: "提交成功,请等待审核!",
|
||
icon: "success"
|
||
})
|
||
}
|
||
})
|
||
}else{
|
||
uni.showToast({
|
||
title: '上传失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
|
||
|
||
// uni.showToast({
|
||
// title: '上传成功',
|
||
// icon: 'success'
|
||
// });
|
||
},
|
||
// 上传失败回调
|
||
fail: (err) => {
|
||
console.error('上传失败', err);
|
||
uni.showToast({
|
||
title: '上传失败',
|
||
icon: 'none'
|
||
});
|
||
},
|
||
// 无论成功失败都会执行
|
||
complete: () => {
|
||
this.progress = 0; // 重置进度
|
||
}
|
||
});
|
||
// this.$u.api.uploadimg(formData).then(res => {
|
||
// console.log(res)
|
||
// if (res.code == 200) {
|
||
// uni.showToast({
|
||
// title: "提交成功,请等待审核!",
|
||
// icon: "success"
|
||
// })
|
||
// } else {
|
||
// uni.showToast({
|
||
// title: "tp提交失败!",
|
||
// icon: "error"
|
||
// })
|
||
// }
|
||
// })
|
||
|
||
});
|
||
|
||
|
||
uni.hideLoading();
|
||
|
||
// 显示成功提示
|
||
uni.showToast({
|
||
title: '提交成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
});
|
||
|
||
// 提交成功后,可跳转到成功页面或重置表单
|
||
setTimeout(() => {
|
||
// 重置表单
|
||
this.resetForm();
|
||
// 返回到上一页或跳转到其他页面
|
||
// uni.navigateBack();
|
||
}, 2000);
|
||
}, 1500);
|
||
},
|
||
delimg() {
|
||
this.formData.facePictures = ''
|
||
},
|
||
|
||
// 重置表单
|
||
resetForm() {
|
||
const now = new Date();
|
||
const date = now.getFullYear() + '-' +
|
||
(now.getMonth() + 1).toString().padStart(2, '0') + '-' +
|
||
now.getDate().toString().padStart(2, '0');
|
||
const time = now.getHours().toString().padStart(2, '0') + ':' +
|
||
now.getMinutes().toString().padStart(2, '0');
|
||
|
||
this.formData = {
|
||
visitorName: '',
|
||
visitorUnit: '',
|
||
visitorPhone: '',
|
||
visitingReason: '',
|
||
interviewedPerson: '',
|
||
interviewedUnit: '',
|
||
interviewedPhone: '',
|
||
visitingBeginDate: date,
|
||
visitingBeginTime: time,
|
||
visitingEndDate: date,
|
||
visitingEndTime: time,
|
||
bookingParkingSpace: false,
|
||
licensePlate: '',
|
||
facePictures: '',
|
||
serveStatus: 0
|
||
};
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.container {
|
||
background-color: #f5f7fa;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.form-card {
|
||
background-color: #fff;
|
||
border-radius: 12px;
|
||
margin: 16px;
|
||
padding: 20px;
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.form-title {
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
text-align: center;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-top: 20px;
|
||
margin-bottom: 12px;
|
||
padding-left: 8px;
|
||
border-left: 3px solid #007aff;
|
||
}
|
||
|
||
.form-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.label {
|
||
width: 96px;
|
||
font-size: 14px;
|
||
color: #666;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.label.required::before {
|
||
content: '*';
|
||
color: #ff4d4f;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.input-wrapper {
|
||
flex: 1;
|
||
height: 40px;
|
||
border: 1px solid #e5e6eb;
|
||
border-radius: 6px;
|
||
padding: 0 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
position: relative;
|
||
}
|
||
|
||
.select-wrapper {
|
||
flex: 1;
|
||
height: 40px;
|
||
border: 1px solid #e5e6eb;
|
||
border-radius: 6px;
|
||
padding: 0 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
position: relative;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.select-wrapper text {
|
||
flex: 1;
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
|
||
.filter-img {
|
||
width: 18rpx;
|
||
height: 10rpx;
|
||
margin-left: 8rpx;
|
||
transition: transform 0.3s;
|
||
}
|
||
|
||
.select-wrapper.active .filter-img {
|
||
transform: rotate(180deg);
|
||
}
|
||
|
||
.input-wrapper input {
|
||
flex: 1;
|
||
height: 100%;
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
|
||
.input-wrapper .filter-img {
|
||
position: absolute;
|
||
right: 12px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 18px;
|
||
/* Adjust based on your image size */
|
||
height: 10px;
|
||
/* Adjust based on your image size */
|
||
}
|
||
|
||
.picker-wrapper {
|
||
flex: 1;
|
||
height: 40px;
|
||
border: 1px solid #e5e6eb;
|
||
border-radius: 6px;
|
||
padding: 0 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
position: relative;
|
||
}
|
||
|
||
.picker-wrapper::after {
|
||
content: '';
|
||
width: 0;
|
||
height: 0;
|
||
border-left: 5px solid transparent;
|
||
border-right: 5px solid transparent;
|
||
border-top: 6px solid #999;
|
||
position: absolute;
|
||
right: 12px;
|
||
}
|
||
|
||
.time-picker {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.picker-value {
|
||
font-size: 14px;
|
||
color: #333;
|
||
flex: 1;
|
||
}
|
||
|
||
.upload-wrapper {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.upload-btn {
|
||
width: 80px;
|
||
height: 80px;
|
||
background-color: #f5f7fa;
|
||
border-radius: 6px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 1px dashed #e5e6eb;
|
||
}
|
||
|
||
.upload-text {
|
||
font-size: 12px;
|
||
color: #999;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.preview-image {
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: 6px;
|
||
margin-left: 12px;
|
||
overflow: hidden;
|
||
border: 1px solid #e5e6eb;
|
||
}
|
||
|
||
.preview-image image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.submit-wrapper {
|
||
margin-top: 32px;
|
||
}
|
||
|
||
.submit-btn {
|
||
width: 100%;
|
||
height: 44px;
|
||
background-color: #007aff;
|
||
color: #fff;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
border-radius: 22px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.filter-btn {
|
||
padding: 15rpx 22rpx;
|
||
background: #f7f7f7;
|
||
border-radius: 25rpx;
|
||
height: 58rpx;
|
||
color: #9a9a9a;
|
||
font-size: 28rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
margin-right: 24rpx;
|
||
position: relative;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.filter-img {
|
||
width: 18rpx;
|
||
height: 10rpx;
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
.dropdown {
|
||
position: absolute;
|
||
top: calc(100% + 5px);
|
||
left: 0;
|
||
right: 0;
|
||
background: #fff;
|
||
border-radius: 6px;
|
||
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.1);
|
||
z-index: 999;
|
||
max-height: 200px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.dropdown-item {
|
||
padding: 12px;
|
||
font-size: 14px;
|
||
color: #333;
|
||
border-bottom: 1rpx solid #eee;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.dropdown-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.dropdown-item:hover {
|
||
background-color: #f5f7fa;
|
||
}
|
||
</style> |