1109 lines
31 KiB
Vue
1109 lines
31 KiB
Vue
<template>
|
||
<view class="meeting-room-detail">
|
||
<!-- 顶部轮播图 -->
|
||
<u-swiper class="room-swiper" :list="list1" height="420" indicator indicatorMode="dot"
|
||
indicatorActiveColor="#2979ff" bgColor="#f8faff">
|
||
</u-swiper>
|
||
<!-- 会议室基本信息卡片 -->
|
||
<view class="info-card">
|
||
<view class="room-info">
|
||
<text class="room-name">{{detail.title}}</text>
|
||
<view class="meta-info">
|
||
<text class="room-capacity"> 可容纳 {{detail.maxNum}} 人</text>
|
||
</view>
|
||
<text class="room-desc" v-if="detail.description">{{detail.description}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 预订信息卡片 -->
|
||
<view class="booking-card">
|
||
<view class="card-header">
|
||
<text class="card-title">预订信息</text>
|
||
</view>
|
||
|
||
<!-- 日期选择 -->
|
||
<view class="date-section">
|
||
<view class="section-header">
|
||
<u-icon name="calendar" size="38" color="#2979ff"></u-icon>
|
||
<text class="section-title">申请时间</text>
|
||
</view>
|
||
<view class="date-display" @click="showCalendar = true">
|
||
<text>{{ formattedSelectedDate }}</text>
|
||
<u-icon name="arrow-right" color="#999"></u-icon>
|
||
</view>
|
||
</view>
|
||
<view class="date-section">
|
||
<view class="section-header">
|
||
<u-icon name="tags" size="38" color="#2979ff"></u-icon>
|
||
<text class="section-title">事由主题</text>
|
||
</view>
|
||
<view>
|
||
<radio-group @change="thingCheckboxChange">
|
||
<label v-for="item in thingThemes">
|
||
<radio :value="item.value" style="transform:scale(0.7)"
|
||
:checked="item.value === applyTheme" />
|
||
<text style="font-size: 12px;color: #333333;">{{item.name}}</text>
|
||
</label>
|
||
</radio-group>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="date-section">
|
||
<view class="section-header">
|
||
<u-icon name="man-add" size="38" color="#2979ff"></u-icon>
|
||
<text class="section-title">申请人类型</text>
|
||
</view>
|
||
<view class="date-display">
|
||
<view class="">{{applyName}}</view>
|
||
<!-- <u-icon slot="right" name="arrow-right"></u-icon> -->
|
||
</view>
|
||
<view v-show="applyName=='单位申请'">
|
||
<view style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入单位全称" border="null" v-model="companyName"></u--input>
|
||
</view>
|
||
<view class="" style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入负责人姓名" border="null" v-model="concatName"></u--input>
|
||
</view>
|
||
<view class="" style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入负责人电话" border="null" type='tel' v-model="concatPhone"></u--input>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-show="applyName=='个人申请'">
|
||
<view class="" style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入姓名" border="null" v-model="userName"></u--input>
|
||
</view>
|
||
<view class="" style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入手机号" border="null" type="tel" v-model="userPhone"></u--input>
|
||
</view>
|
||
<view class="" style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入身份证号" border="null" type="idcard" v-model="userCardId"></u--input>
|
||
</view>
|
||
<view class="" style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入申请人地址" border="null" v-model="userAddress"></u--input>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="date-section">
|
||
<view class="section-header">
|
||
<u-icon name="pushpin" size="38" color="#2979ff"></u-icon>
|
||
<text class="section-title">申请场次及人数(选填)</text>
|
||
</view>
|
||
|
||
<view>
|
||
<view class="" style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入场次" border="null" v-model="counter"></u--input>
|
||
</view>
|
||
<view class="" style="margin-top: 10px;background-color: #f8f9fa;">
|
||
<u--input placeholder="请输入人数" border="null" v-model="num"></u--input>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 时间选择 -->
|
||
<view class="time-section">
|
||
<view class="section-header">
|
||
<u-icon name="clock" size="30" color="#2979ff"></u-icon>
|
||
<text class="section-title">借用时间</text>
|
||
</view>
|
||
<view class="time-picker" v-if="!isSelfStudy">
|
||
<view class="time-selector" @click="borrowTimeShow = true">
|
||
<text class="time-label">日期</text>
|
||
<view class="time-display">{{ borrowTimeData }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="time-picker" v-if="!isSelfStudy">
|
||
<view class="time-selector" @click="noIsSelfStudy = true;pickerType=1">
|
||
<text class="time-label">开始</text>
|
||
<view class="time-display">
|
||
{{ startTime }}
|
||
</view>
|
||
</view>
|
||
<view class="time-separator">—</view>
|
||
<view class="time-selector" @click="noIsSelfStudy = true;pickerType=2">
|
||
<text class="time-label">结束</text>
|
||
<view class="time-display">
|
||
{{ endTime }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="time-picker" v-if="isSelfStudy">
|
||
<view class="time-selector" @click="showStartTimePicker = true">
|
||
<text class="time-label">开始</text>
|
||
<view class="time-display">
|
||
{{ startTime }}
|
||
</view>
|
||
</view>
|
||
<view class="time-separator">—</view>
|
||
<view class="time-selector" @click="showEndTimePicker = true">
|
||
<text class="time-label">结束</text>
|
||
<view class="time-display">
|
||
{{ endTime }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- <view class="date-section">
|
||
<view class="section-header">
|
||
<u-icon name="list-dot" size="38" color="#2979ff"></u-icon>
|
||
<text class="section-title">使用情况</text>
|
||
</view>
|
||
<view>
|
||
<checkbox-group @change="usagesCheckboxChange" v-model="usage">
|
||
<label v-for="item in usages">
|
||
<checkbox :value="item.value" style="transform:scale(0.7)" /><text
|
||
style="font-size: 12px;color: #333333;">{{item.name}}</text>
|
||
</label>
|
||
</checkbox-group>
|
||
</view>
|
||
</view> -->
|
||
<view class="date-section">
|
||
<view class="section-header">
|
||
<u-icon name="list-dot" size="38" color="#2979ff"></u-icon>
|
||
<text class="section-title">申请理由</text>
|
||
</view>
|
||
<view>
|
||
<u--input placeholder="请输入申请理由" border="null" v-model="reason"></u--input>
|
||
</view>
|
||
</view>
|
||
<!-- <view class="date-section" v-show="applyName=='个人申请'">
|
||
<view class="section-header">
|
||
<u-icon name="plus-circle" size="38" color="#2979ff"></u-icon>
|
||
<text class="section-title">签字</text>
|
||
</view>
|
||
<view class="upload-item" v-if="signUrl">
|
||
<image :src="signUrl" mode="aspectFill" @click="previewSign()"></image>
|
||
<view class="delete-btn" @click="handleDeleteSign()">
|
||
<u-icon name="close" color="#fff" size="24"></u-icon>
|
||
</view>
|
||
</view>
|
||
<view class="upload-btn" @click="signShow=true" v-if="!signUrl">
|
||
<u-icon name="plus" size="40" color="#c0c4cc"></u-icon>
|
||
</view>
|
||
</view> -->
|
||
<view class="date-section" v-show="applyName=='单位申请'">
|
||
<view class="section-header">
|
||
<u-icon name="plus-circle" size="38" color="#2979ff"></u-icon>
|
||
<text class="section-title">上传印章</text>
|
||
</view>
|
||
<!-- 上传照片/视频 - 完全重写的上传组件 -->
|
||
<gx-upload @upload-success="handleUploadSuccess" />
|
||
</view>
|
||
<view>
|
||
<u-button type="primary" shape="circle" @click="handleBook()">立即预订</u-button>
|
||
</view>
|
||
</view>
|
||
<!-- 底部固定操作栏 -->
|
||
|
||
|
||
<!-- 日历组件 -->
|
||
<u-calendar :key="calendarKey" :show="showCalendar" mode="single" :min-date="minDate" :max-date="maxDate"
|
||
@confirm="handleDateConfirm" @close="showCalendar = false" :mask-close-able="true"></u-calendar>
|
||
|
||
<!-- 时间选择器 -->
|
||
<u-picker :show="noIsSelfStudy" :columns="startTimeColumns" @confirm="borrowTimeConfirm"
|
||
@cancel="noIsSelfStudy=false"></u-picker>
|
||
<u-datetime-picker :show="borrowTimeShow" v-model="borrowTime" mode="date" :min-date="Date.now()"
|
||
@confirm="handleborrowTimeConfirm" @cancel="borrowTimeShow = false"></u-datetime-picker>
|
||
<!-- <u-datetime-picker :show="showStartTimePicker"
|
||
v-model="startTimePickerValue"
|
||
:min-date="Date.now()"
|
||
:mode="mode"
|
||
@confirm="handleStartTimeConfirm"
|
||
@cancel="showStartTimePicker = false"
|
||
ref="startTimePicker"></u-datetime-picker> -->
|
||
<u-datetime-picker :show="showEndTimePicker" v-model="endTimePickerValue" :min-date="Date.now()" :mode="mode"
|
||
@confirm="handleEndTimeConfirm" @cancel="showEndTimePicker = false" ref="endTimePicker">
|
||
</u-datetime-picker>
|
||
<u-picker :show="showApplyType" :columns="applyTypeColumns" @confirm="applyConfirm"
|
||
@cancel="showApplyType=false"></u-picker>
|
||
<u-popup :show="readShow" mode="center" :round="10">
|
||
<instructionVue @change='readChange' />
|
||
<u-button type="primary" text="我已知晓" :disabled="hasReaded" @click="postApply"></u-button>
|
||
</u-popup>
|
||
<!-- <u-popup :show="signShow" mode="center" :round="10">
|
||
<gxsign />
|
||
</u-popup> -->
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
get,
|
||
post
|
||
} from '@/utils/request';
|
||
import {
|
||
IMAGE_BASE_URL,
|
||
BASE_URL
|
||
} from '@/utils/config';
|
||
import {
|
||
formatTime,
|
||
formatRelativeTime
|
||
} from '@/utils/timeFormat';
|
||
import {
|
||
applyType,
|
||
usages,
|
||
thingThemes
|
||
} from '@/utils/dict.js'
|
||
import gxsign from '../../components/sign/sign.vue';
|
||
import gxUpload from '../../components/gx-upload.vue';
|
||
// import processImage from '../../components/process-image.vue'
|
||
import instructionVue from '../../components/instruction.vue';
|
||
|
||
export default {
|
||
components: {
|
||
// processImage,
|
||
instructionVue,
|
||
gxUpload,
|
||
gxsign
|
||
},
|
||
data() {
|
||
const now = new Date();
|
||
const year = now.getFullYear();
|
||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||
const day = now.getDate().toString().padStart(2, '0');
|
||
|
||
// 计算默认开始/结束时间
|
||
let startHours = now.getHours();
|
||
let startMinutes = now.getMinutes() < 30 ? 30 : 0;
|
||
if (now.getMinutes() >= 30) startHours += 1;
|
||
if (startHours >= 24) startHours = 0;
|
||
|
||
let endHours = startHours + 1;
|
||
let endMinutes = startMinutes;
|
||
if (endHours >= 24) endHours = 0;
|
||
|
||
return {
|
||
noIsSelfStudy: false,
|
||
pickerType: 1,
|
||
borrowTimeData: '',
|
||
borrowTimeShow: false,
|
||
borrowTime: Number(new Date()),
|
||
companyName: '',
|
||
startTimeColumns: [
|
||
['08', '09', '10', '11', '13', '14', '15', '16', '17'],
|
||
['00', '30']
|
||
],
|
||
readShow: false,
|
||
hasReaded: true,
|
||
signUrl: '',
|
||
signShow: false,
|
||
applyRules: {
|
||
userPhone: {
|
||
name: '申请人手机号',
|
||
reg: /^1[3-9]\d{9}$/,
|
||
errMsg: '请输入正确的手机号码格式'
|
||
},
|
||
userName: {
|
||
name: '申请人姓名',
|
||
reg: /^.+$/,
|
||
errMsg: '申请人姓名不能为空'
|
||
},
|
||
userCardId: {
|
||
name: '申请人身份证号',
|
||
reg: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
|
||
errMsg: '身份证格式不正确'
|
||
},
|
||
userAddress: {
|
||
name: '申请人地址',
|
||
reg: /^.+$/,
|
||
errMsg: '申请人地址不能为空'
|
||
},
|
||
concatName: {
|
||
name: '负责人姓名',
|
||
reg: /^.+$/,
|
||
errMsg: '负责人姓名不能为空'
|
||
},
|
||
concatPhone: {
|
||
name: '负责人电话',
|
||
reg: /^1[3-9]\d{9}$/,
|
||
errMsg: '请输入正确的手机号码格式'
|
||
},
|
||
companyName: {
|
||
name: '公司名称',
|
||
reg: /^.+$/,
|
||
errMsg: '公司名称不能为空'
|
||
},
|
||
startTime: {
|
||
name: '开始时间',
|
||
reg: /^.+$/,
|
||
errMsg: '开始时间不能为空'
|
||
},
|
||
endTime: {
|
||
name: '结束时间',
|
||
reg: /^.+$/,
|
||
errMsg: '结束时间不能空'
|
||
},
|
||
counter: {
|
||
name: '申请场次',
|
||
reg: /^[1-9]\d*$/,
|
||
errMsg: '申请场次不能空且是正整数'
|
||
},
|
||
num: {
|
||
name: '申请人数',
|
||
reg: /^[1-9]\d*$/,
|
||
errMsg: '申请人数不能空且是正整数'
|
||
},
|
||
},
|
||
// userName: '谢雨晴',
|
||
// userPhone: "15189809052",
|
||
// userCardId: '32068319950902002X',
|
||
// userAddress: '江苏省南通市紫琅科技城3号楼',
|
||
// startTime: `2025-08-11 09:00`, // 默认开始时间
|
||
// endTime: `2025-08-11 10:00`, // 默认结束时间
|
||
startTime: '', // 默认开始时间
|
||
endTime: '', // 默认结束时间
|
||
userName: '',
|
||
userPhone: "",
|
||
userCardId: '',
|
||
userAddress: '',
|
||
counter: '',
|
||
num: '',
|
||
// 会议室图片数组
|
||
applyTheme: 1,
|
||
applyArea: 1,
|
||
thingThemes: thingThemes.getAll(),
|
||
usage: [],
|
||
usages: usages.getAll(),
|
||
list1: [],
|
||
detail: {},
|
||
signatureShow: false,
|
||
isSelfStudy: true, //dateTime,yearmonth
|
||
mode: 'date',
|
||
precision: null,
|
||
concatPhone: '',
|
||
reason: '',
|
||
applySign: '', //签名地址
|
||
// 日历控制(默认隐藏)
|
||
showCalendar: false,
|
||
calendarKey: 0,
|
||
showApplyType: false,
|
||
applyTypeColumns: [
|
||
['个人申请', '单位申请']
|
||
],
|
||
applyType: 1, //企业1 个人2
|
||
|
||
concatName: '', //联系人姓名
|
||
concatPhone: '', //联系人电话
|
||
// 时间选择器控制
|
||
showStartTimePicker: false,
|
||
showEndTimePicker: false,
|
||
stampUrl: '',
|
||
// 使用字符串格式的时间
|
||
startTimePickerValue: `${year}-${month}-${day}`,
|
||
endTimePickerValue: ``,
|
||
applyInfo: {},
|
||
// 日期相关
|
||
selectedDate: `${year}-${month}-${day}`,
|
||
minDate: `${year}-${month}-${day}`,
|
||
maxDate: `${year + 1}-12-31`,
|
||
|
||
|
||
|
||
startTimeValue: `09:00`,
|
||
endTimeValue: `10:00`,
|
||
startTimePickerValue: `2025-08-11 09:00`,
|
||
endTimePickerValue: `2025-08-11 10:00`,
|
||
// 新增备注字段
|
||
remark: '',
|
||
|
||
noticeList: [
|
||
'',
|
||
'使用优惠券支付,取消预订后优惠券会自动退还。',
|
||
'使用微信、支付宝支付,取消订单后需要转社区运营人员进行退款。',
|
||
'系统不支持自动退款。',
|
||
'场地搭建,则需在活动开始48小时前支付相应保证金,具体费用可与运营人员进行确认。'
|
||
],
|
||
Id: '',
|
||
};
|
||
},
|
||
onLoad(options) {
|
||
if (options && options.Id) {
|
||
this.Id = options.Id;
|
||
this.isSelfStudy = JSON.parse(options.isSelfStudy)
|
||
this.mode = options.isSelfStudy === 'true' ? 'year-month' : 'datetime'
|
||
|
||
// 设置默认时间值
|
||
if (this.isSelfStudy) {
|
||
// 自习室模式,只显示年月
|
||
const now = new Date();
|
||
const year = now.getFullYear();
|
||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||
|
||
this.startTime = `${year}-${month}`;
|
||
this.endTime = `${year}-${month}`;
|
||
this.startTimePickerValue = this.startTime;
|
||
this.endTimePickerValue = this.endTime;
|
||
} else {
|
||
// 普通会议室模式,显示完整日期时间
|
||
const now = new Date();
|
||
const year = now.getFullYear();
|
||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||
const day = now.getDate().toString().padStart(2, '0');
|
||
this.borrowTimeData = `${year}-${month}-${day} `
|
||
this.startTime = `09:00`;
|
||
this.endTime = `10:00`;
|
||
this.startTimePickerValue = this.startTime;
|
||
this.endTimePickerValue = this.endTime;
|
||
}
|
||
|
||
}
|
||
},
|
||
computed: {
|
||
formattedSelectedDate() {
|
||
if (!this.selectedDate) return '请选择日期';
|
||
|
||
if (this.isSelfStudy) {
|
||
// 自习室模式,显示年月格式
|
||
const [year, month] = this.selectedDate.split('-');
|
||
return `${year}年${month}月`;
|
||
} else {
|
||
// 普通会议室模式,显示完整日期
|
||
const date = new Date(this.selectedDate);
|
||
const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||
const month = date.getMonth() + 1;
|
||
const day = date.getDate();
|
||
const weekday = weekdays[date.getDay()];
|
||
|
||
return `${month}月${day}日 ${weekday}`;
|
||
}
|
||
},
|
||
applyName() {
|
||
if (this.applyType === 1) {
|
||
return '单位申请'
|
||
} else {
|
||
return '个人申请'
|
||
}
|
||
},
|
||
borrowMinDate() {
|
||
// 今年1月1日 00:00:00 的时间戳
|
||
const year = new Date().getFullYear();
|
||
return new Date(year, 0, 1).getTime();
|
||
},
|
||
borrowMaxDate() {
|
||
// 明年12月31日 23:59:59 的时间戳
|
||
const year = new Date().getFullYear() + 1;
|
||
return new Date(year, 11, 31, 23, 59, 59).getTime();
|
||
}
|
||
},
|
||
|
||
mounted() {
|
||
this.getDetail();
|
||
},
|
||
methods: {
|
||
readChange(flag) {
|
||
this.hasReaded = flag
|
||
|
||
},
|
||
showApplyTypeClick() {
|
||
if (this.isSelfStudy) return
|
||
this.showApplyType = true
|
||
},
|
||
handleUploadSuccess(e) {
|
||
this.stampUrl = e
|
||
},
|
||
// 获取详情页面
|
||
async getDetail() {
|
||
try {
|
||
const res = await get(`/api/v1/apps/home/metting-room/${this.Id}`);
|
||
if (!res || !res.success) {
|
||
throw new Error('获取详情失败');
|
||
}
|
||
console.log("======res", res)
|
||
// 如果有返回的图片数据,可以这样处理
|
||
if (res.data.imgs && res.data.imgs.length > 0) {
|
||
this.list1 = res.data.imgs.map(img => {
|
||
return img.startsWith('http') ? img : IMAGE_BASE_URL + img;
|
||
});
|
||
}
|
||
this.detail = {
|
||
...res.data
|
||
};
|
||
this.applyType = res.data.roomType
|
||
console.log(this.applyType)
|
||
} catch (err) {
|
||
console.error('获取详情失败:', err);
|
||
}
|
||
},
|
||
thingCheckboxChange(e) {
|
||
this.applyTheme = JSON.parse(e.detail.value)
|
||
},
|
||
areaCheckboxChange(e) {
|
||
this.applyArea = JSON.parse(e.detail.value)
|
||
},
|
||
usagesCheckboxChange(e) {
|
||
console.log(e);
|
||
},
|
||
applyConfirm(e) {
|
||
console.log(e.value[0])
|
||
const list = ['个人申请', '单位申请']
|
||
this.applyName = e.value[0]
|
||
const index = list.findIndex((item) => item === e.value[0])
|
||
console.log(index)
|
||
if (index != -1) {
|
||
this.applyType = parseInt(index + 1)
|
||
this.showApplyType = false
|
||
} else {
|
||
console.log('未找到');
|
||
}
|
||
|
||
},
|
||
handleDateConfirm(e) {
|
||
let selectedDate;
|
||
|
||
// 处理不同格式的返回值
|
||
if (Array.isArray(e)) {
|
||
selectedDate = e[0];
|
||
} else if (e.result && Array.isArray(e.result)) {
|
||
selectedDate = e.result[0];
|
||
} else if (typeof e === 'string') {
|
||
selectedDate = e;
|
||
} else {
|
||
selectedDate = e.date;
|
||
}
|
||
|
||
this.selectedDate = selectedDate;
|
||
this.showCalendar = false;
|
||
|
||
// 强制更新视图
|
||
this.$nextTick(() => {
|
||
this.calendarKey += 1;
|
||
});
|
||
},
|
||
|
||
handleStartTimeConfirm(e) {
|
||
if (this.isSelfStudy) {
|
||
// 自习室模式处理年月
|
||
const date = new Date(e.value);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
|
||
this.startTime = `${year}-${month}`;
|
||
this.startTimePickerValue = this.startTime;
|
||
} else {
|
||
// 普通会议室模式处理完整日期时间
|
||
const date = new Date(e.value);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
|
||
this.startTime = `${year}-${month}-${day} ${hours}:${minutes}`;
|
||
this.startTimePickerValue = this.startTime;
|
||
}
|
||
|
||
this.showStartTimePicker = false;
|
||
},
|
||
borrowTimeConfirm(e) {
|
||
console.log(e)
|
||
if (this.pickerType === 1) {
|
||
this.startTime = `${e.value[0]}:${e.value[1]}`
|
||
} else {
|
||
this.endTime = `${e.value[0]}:${e.value[1]}`
|
||
}
|
||
this.noIsSelfStudy = false
|
||
},
|
||
handleEndTimeConfirm(e) {
|
||
if (this.isSelfStudy) {
|
||
// 自习室模式处理年月
|
||
const date = new Date(e.value);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
|
||
this.endTime = `${year}-${month}`;
|
||
this.endTimePickerValue = this.endTime;
|
||
} else {
|
||
// 普通会议室模式处理完整日期时间
|
||
const date = new Date(e.value);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
|
||
this.endTime = `${year}-${month}-${day} ${hours}:${minutes}`;
|
||
this.endTimePickerValue = this.endTime;
|
||
}
|
||
|
||
this.showEndTimePicker = false;
|
||
},
|
||
handleborrowTimeConfirm(e) {
|
||
const date = new Date(e.value);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
this.borrowTimeData = `${year}-${month}-${day}`;
|
||
this.borrowTimeShow = false
|
||
},
|
||
adjustEndTime() {
|
||
console.log(this.startTime)
|
||
const [startH, startM] = this.startTime.split(':').map(Number);
|
||
let [endH, endM] = this.endTime.split(':').map(Number);
|
||
|
||
const startTotal = startH * 60 + startM;
|
||
let endTotal = endH * 60 + endM;
|
||
|
||
if (endTotal <= startTotal) {
|
||
endTotal = startTotal + 60;
|
||
endH = Math.floor(endTotal / 60);
|
||
endM = endTotal % 60;
|
||
this.endTime = `${endH.toString().padStart(2, '0')}:${endM.toString().padStart(2, '0')}`;
|
||
this.endTimeValue = `${endH}:${endM}`;
|
||
this.endTimePickerValue = this.endTime;
|
||
}
|
||
},
|
||
|
||
validateTime() {
|
||
const [startH, startM] = this.startTime.split(':').map(Number);
|
||
const [endH, endM] = this.endTime.split(':').map(Number);
|
||
|
||
const duration = (endH * 60 + endM) - (startH * 60 + startM);
|
||
if (duration < 30) {
|
||
uni.showToast({
|
||
title: '最少预订30分钟',
|
||
icon: 'none'
|
||
});
|
||
this.adjustEndTime();
|
||
}
|
||
},
|
||
/**
|
||
* 验证对象属性是否为空(Promise版)
|
||
* @param {Object} obj - 要验证的对象
|
||
* @param {Object} rules - 验证规则对象 (每个 rule.reg 应为 RegExp 对象)
|
||
* @returns {Promise} 返回Promise,resolve通过验证,reject失败信息
|
||
*/
|
||
validateObject(obj, rules = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
// 参数校验
|
||
if (!obj || typeof obj !== 'object' || obj === null) {
|
||
return reject(new Error('第一个参数必须是一个非空对象'));
|
||
}
|
||
if (!rules || typeof rules !== 'object') {
|
||
return reject(new Error('第二个参数(验证规则)必须是一个对象'));
|
||
}
|
||
|
||
const keys = Object.keys(obj);
|
||
// 建议:只检查规则中定义的字段,而不是所有 obj 的 key
|
||
// 这样更可控,避免验证不存在的规则
|
||
for (const field of keys) {
|
||
const rule = rules[field];
|
||
if (!rule) {
|
||
continue;
|
||
}
|
||
const errMsg = rule.errMsg
|
||
const value = obj[field]; // 获取该字段的值
|
||
// 检查该字段是否有规则定义
|
||
if (!rule || !rule.reg || !rule.name) {
|
||
console.warn(`验证规则 ${field} 不完整`);
|
||
continue; // 跳过不完整的规则
|
||
}
|
||
// 确保 rule.reg 是一个 RegExp 对象
|
||
if (!(rule.reg instanceof RegExp)) {
|
||
reject(new Error(`${rule.name} 的验证规则不是有效的正则表达式`));
|
||
return;
|
||
}
|
||
// 执行验证
|
||
// 注意:这里也应处理 undefined 或 null 的情况
|
||
const stringValue = value == null ? '' : String(value); // 转为字符串并处理 null/undefined
|
||
if (!rule.reg.test(stringValue)) {
|
||
return reject(new Error(errMsg)); // 找到第一个错误就立即 reject
|
||
}
|
||
}
|
||
// 如果循环完成,说明所有验证都通过了
|
||
resolve('所有属性验证通过');
|
||
});
|
||
},
|
||
validateBorrowTime(applyInfo = {}) {
|
||
return new Promise(async (resolve, reject) => {
|
||
const response = await post('/api/v1/app_auth/metting-room/order/check_time', applyInfo)
|
||
if (!response || !response.success) {
|
||
reject(
|
||
uni.showToast({
|
||
title: response.msg,
|
||
icon: 'none'
|
||
})
|
||
)
|
||
}
|
||
if (response.success == true) {
|
||
resolve()
|
||
}
|
||
})
|
||
},
|
||
async handleBook() {
|
||
try {
|
||
this.readShow = false
|
||
if (!this.isSelfStudy) {
|
||
if (!this.startTime || !this.endTime) {
|
||
return uni.showToast({
|
||
title: '开始和结束时间不能为空',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
if(this.startTime>this.endTime){
|
||
return uni.showToast({
|
||
title: '开始时间不能晚于结束时间',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
// console.log(this.applyType);
|
||
let bookingInfo = {
|
||
roomId: this.Id,
|
||
applyType: this.applyType,
|
||
reason: this.reason,
|
||
applyTheme: this.applyTheme
|
||
};
|
||
//个人申请
|
||
if (this.applyType === 2) {
|
||
const userApplyInfo = {
|
||
userName: this.userName,
|
||
userPhone: this.userPhone,
|
||
userAddress: this.userAddress,
|
||
userCardId: this.userCardId,
|
||
startTime: this.isSelfStudy ? this.startTime :
|
||
`${this.borrowTimeData} ${this.startTime}`,
|
||
endTime: this.isSelfStudy ? this.endTime : `${this.borrowTimeData} ${this.endTime}`,
|
||
counter: this.counter,
|
||
num: this.num,
|
||
// applyType:this.applyType
|
||
}
|
||
await this.validateObject(userApplyInfo, this.applyRules)
|
||
|
||
const applyInfo = Object.assign(bookingInfo, userApplyInfo)
|
||
await this.validateBorrowTime(applyInfo)
|
||
// 页面A设置参数
|
||
const app = getApp()
|
||
app.globalData.applyInfo = applyInfo
|
||
this.applyInfo = applyInfo
|
||
this.readShow = true
|
||
}
|
||
//单位申请
|
||
if (this.applyType === 1) {
|
||
if (!this.stampUrl) return uni.showToast({
|
||
title: '请上传印章',
|
||
icon: 'none'
|
||
})
|
||
const userApplyInfo = {
|
||
concatName: this.concatName,
|
||
concatPhone: this.concatPhone,
|
||
companyName: this.companyName,
|
||
startTime: this.isSelfStudy ? this.startTime :
|
||
`${this.borrowTimeData} ${this.startTime}`,
|
||
endTime: this.isSelfStudy ? this.endTime : `${this.borrowTimeData} ${this.endTime}`,
|
||
counter: this.counter,
|
||
num: this.num,
|
||
stampUrl: this.stampUrl,
|
||
// applyType:this.applyType
|
||
}
|
||
await this.validateObject(userApplyInfo, this.applyRules)
|
||
const applyInfo = Object.assign(bookingInfo, userApplyInfo)
|
||
await this.validateBorrowTime(applyInfo)
|
||
const app = getApp()
|
||
app.globalData.applyInfo = applyInfo
|
||
this.readShow = true
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({
|
||
title: error.message,
|
||
icon: 'none'
|
||
})
|
||
}
|
||
|
||
},
|
||
async postApply() {
|
||
this.readShow = false
|
||
uni.navigateTo({
|
||
url: `/pages/sign/sign?isSelfStudy=${this.isSelfStudy}`
|
||
})
|
||
//单位申请
|
||
// if(this.applyType===2){
|
||
// uni.navigateTo({
|
||
// url: `/pages/sign/sign?isSelfStudy=${this.isSelfStudy}`
|
||
// })
|
||
// }else{
|
||
// // 这里可以添加实际的API调用
|
||
// const res = await post('/api/v1/app_auth/metting-room/order/register', this.applyInfo)
|
||
// if (!res || !res.success) {
|
||
// throw new Error('会议室预定失败');
|
||
// }
|
||
|
||
// if (res.success == true) {
|
||
// uni.redirectTo({
|
||
// url: `/pages/docList/index?files=${JSON.stringify(res.data)}&&isSelfStudy=${this.isSelfStudy}`
|
||
// })
|
||
// }
|
||
// }
|
||
|
||
}
|
||
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.meeting-room-detail {
|
||
padding: 20rpx;
|
||
// padding-bottom: 140rpx;
|
||
/* 增大底部空间 */
|
||
background-color: #f5f7fa;
|
||
}
|
||
|
||
/* 轮播图优化 */
|
||
.room-swiper {
|
||
width: 100%;
|
||
// height: 420rpx;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
/* 卡片通用样式 */
|
||
.info-card,
|
||
.booking-card,
|
||
.notice-card {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 24rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
/* 会议室信息卡片 */
|
||
.room-info {
|
||
.room-name {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 16rpx;
|
||
display: block;
|
||
}
|
||
|
||
.meta-info {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
margin-bottom: 16rpx;
|
||
|
||
text {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.room-desc {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
|
||
/* 卡片标题 */
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 24rpx;
|
||
padding-bottom: 16rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
.card-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-left: 10rpx;
|
||
}
|
||
}
|
||
|
||
/* 日期选择区域 */
|
||
.date-section {
|
||
margin-bottom: 32rpx;
|
||
|
||
.section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
.date-display {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx 24rpx;
|
||
background: #f8f9fa;
|
||
border-radius: 12rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
/* 时间选择区域 */
|
||
|
||
|
||
.time-section {
|
||
flex: 1;
|
||
margin-bottom: 32rpx;
|
||
|
||
.time-selector {
|
||
flex: 1
|
||
}
|
||
|
||
.time-label {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
margin-bottom: 8rpx;
|
||
display: block;
|
||
}
|
||
|
||
.time-display {
|
||
padding: 20rpx;
|
||
background: #f8f9fa;
|
||
border-radius: 12rpx;
|
||
text-align: center;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
height: 15px;
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
.time-picker {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.time-separator {
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
padding-top: 28rpx;
|
||
}
|
||
|
||
}
|
||
|
||
.upload-item,
|
||
.upload-btn {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
margin: 5rpx;
|
||
position: relative;
|
||
background: #f8f8f8;
|
||
border-radius: 8rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
|
||
image,
|
||
video {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.delete-btn {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border-radius: 0 0 0 8rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.upload-btn {
|
||
border: 1rpx dashed #c0c4cc;
|
||
}
|
||
|
||
/* 新增备注区域 */
|
||
.remark-section {
|
||
margin-top: 24rpx;
|
||
|
||
.section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-left: 8rpx;
|
||
}
|
||
}
|
||
|
||
/* 预订须知 */
|
||
.notice-card {
|
||
.notice-list {
|
||
.notice-item {
|
||
display: flex;
|
||
margin-bottom: 16rpx;
|
||
line-height: 1.6;
|
||
|
||
.notice-index {
|
||
color: #2979ff;
|
||
font-weight: bold;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
.notice-content {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
flex: 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 底部操作栏 */
|
||
.action-bar {
|
||
// position: fixed;
|
||
// bottom: 0;
|
||
// left: 0;
|
||
// right: 0;
|
||
padding: 20rpx;
|
||
background: #fff;
|
||
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||
z-index: 999;
|
||
|
||
.u-button {
|
||
height: 88rpx;
|
||
/* 增大高度 */
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
/* 动画效果 */
|
||
.time-display,
|
||
.date-display {
|
||
transition: all 0.2s;
|
||
|
||
&:active {
|
||
background: #eef2f7;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
</style> |