This commit is contained in:
2025-08-14 11:50:11 +08:00
commit d488db04bc
242 changed files with 34863 additions and 0 deletions

638
pages/sys/msg/index.vue Normal file
View File

@@ -0,0 +1,638 @@
<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.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>
<!-- 被访人信息 -->
<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.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',
visitorPhone: '15555555555',
visitingReason: '1',
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: '已完成'
}
}
},
onLoad(options) {
// 新增获取url中的code参数并打印
if (options.code) {
console.log('从URL中获取到的code参数', options.code);
let p = {};
p.qrcode = options.code
this.formData.qrCodeId = options.code;
this.$u.api.codesub(p).then(res => {
console.log(res)
})
} else {
console.log('URL中未包含code参数');
uni.showToast({
title: '二维码失效,请重新扫描',
icon: 'success',
duration: 2000
});
}
// #ifdef APP-PLUS
plus.screen.lockOrientation('default');
// #endif
},
onReady() {
// #ifdef APP-PLUS
plus.screen.lockOrientation('landscape-primary');
// #endif
},
methods: {
// 处理日期时间选择
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,
visitorPhone,
visitingReason,
interviewedPerson,
bookingParkingSpace,
licensePlate,
facePictures,
visitingBeginDate,
visitingBeginTime,
visitingEndDate,
visitingEndTime
} = this.formData;
// 验证姓名
if (!visitorName) {
return '请输入访客姓名';
}
// 验证公司
if (!visitorUnit) {
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}`;
// 准备提交数据
const submitData = {
...this.formData,
bookingParkingSpace: this.formData.bookingParkingSpace ? 0 : 1
};
console.log(submitData)
// 显示加载提示
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.uploadResult = res.data;
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"
// })
// }
// })
});
this.$u.api.fksub(submitData).then(res => {
console.log(res)
if (res.code == 200) {
uni.showToast({
title: "提交成功,请等待审核!",
icon: "success"
})
} else {
uni.showToast({
title: "提交失败!",
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;
}
.input-wrapper input {
flex: 1;
height: 100%;
font-size: 14px;
color: #333;
}
.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;
}
</style>