511 lines
13 KiB
Vue
511 lines
13 KiB
Vue
<template>
|
||
<view class="reservation">
|
||
<view class="form-container">
|
||
<form>
|
||
<view class="form-item">
|
||
<label>功能房</label>
|
||
<input type="text" v-model="list.roomName" disabled placeholder-class="input-placeholder" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<label>主题</label>
|
||
<input type="text" v-model="formData.rtTheme" placeholder="请输入活动主题" placeholder-class="input-placeholder" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<label>申请人</label>
|
||
<input type="text" v-model="formData.rtCreatRole" placeholder="请输入申请人姓名" placeholder-class="input-placeholder" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<label>使用部门/学院</label>
|
||
<picker mode="selector" :range="deptXZJGList" @change="handleDeptChange">
|
||
<view class="picker">
|
||
{{ formData.rtDepar || '请选择' }}
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<label>参与人数</label>
|
||
<input type="number" v-model="formData.rtPeople" :placeholder="'最多可容纳'+list.roomCapacity+'人'" placeholder-class="input-placeholder" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<label>预约用途</label>
|
||
<picker range-key="dictLabel" :value="rangeIndex" :range="Reserveduse" @change="classChange">
|
||
<view class="uni-input">
|
||
<text v-if="Reserveduse.length" class="val">{{Reserveduse[rangeIndex].dictLabel}}</text>
|
||
<text v-else class="placeholder">请选择预约用途</text>
|
||
<uni-icons type="down" size="16" color="#202020"></uni-icons>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<label>人员类型</label>
|
||
<view class="trigger" @click="showDrawer = true">
|
||
<view class="uni-input">
|
||
<text v-if="selectedRoles.length">{{ selectedRoles.join(", ") }}</text>
|
||
<text v-else class="placeholder">请选择人员类型</text>
|
||
<uni-icons type="down" size="16" color="#202020"></uni-icons>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</form>
|
||
</view>
|
||
|
||
<view class="time-selection">
|
||
<view class="time-header">
|
||
<text>选择时间段</text>
|
||
<text><text style="color: red;">*</text>请选择可用时间段</text>
|
||
</view>
|
||
|
||
<view class="time-slots">
|
||
<view v-for="(item,index) in slotslist" :key="index" class="slot-item">
|
||
<button @click="selectTimeSlot(item,index)"
|
||
:disabled="item.isOccupy==1"
|
||
:class="{
|
||
'active': timeindex===index,
|
||
'disabled': item.isOccupy==1,
|
||
'warning': formData.rtPeople > list.roomCapacity
|
||
}">
|
||
{{item.openingHours}}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="usage-matters">
|
||
<text>使用事项:</text>
|
||
<text>{{list.roomMatter}}</text>
|
||
</view>
|
||
|
||
<button type="primary"
|
||
:disabled="isSubmitting || !isFormValid"
|
||
class="submit-btn"
|
||
@tap="onSubmit">
|
||
{{isSubmitting ? '提交中...' : '提交预约'}}
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 人员类型选择抽屉 -->
|
||
<view v-if="showDrawer" class="drawer-overlay" @click="showDrawer = false"></view>
|
||
<view v-if="showDrawer" class="drawer">
|
||
<view class="drawer-header">
|
||
<text>选择人员类型</text>
|
||
<text class="drawer-close" @click="showDrawer = false">完成</text>
|
||
</view>
|
||
<view class="drawer-body">
|
||
<uni-data-checkbox
|
||
:multiple="true"
|
||
v-model="selectedRoles"
|
||
mode="tag"
|
||
:map="{text:'dictLabel',value:'dictValue'}"
|
||
:localdata="rolelist">
|
||
</uni-data-checkbox>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
selectRtFuReservationTime,
|
||
addRoomReservation,
|
||
getXZJGDept
|
||
} from '@/api/OneStopCommunity/room.js';
|
||
import { getDicts } from '@/api/system/dict/data.js';
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
showDrawer: false,
|
||
selectedRoles: [],
|
||
isSubmitting: false,
|
||
timeindex: null,
|
||
formData: {
|
||
rtTheme: "",
|
||
rtCreatRole: "",
|
||
rtDepar: "",
|
||
rtPeople: "",
|
||
},
|
||
rangeIndex: 0,
|
||
roomNo: "",
|
||
rtTime: "",
|
||
list: {},
|
||
slotslist: [],
|
||
Reserveduse: [],
|
||
rolelist: [],
|
||
deptXZJGList: []
|
||
};
|
||
},
|
||
computed: {
|
||
isFormValid() {
|
||
return (
|
||
this.formData.rtTheme &&
|
||
this.formData.rtCreatRole &&
|
||
this.formData.rtDepar &&
|
||
this.formData.rtPeople > 0 &&
|
||
this.formData.rtPeople <= this.list.roomCapacity &&
|
||
this.Reserveduse.length > 0 &&
|
||
this.selectedRoles.length > 0 &&
|
||
this.timeindex !== null
|
||
);
|
||
}
|
||
},
|
||
methods: {
|
||
handleDeptChange(e) {
|
||
this.formData.rtDepar = this.deptXZJGList[e.detail.value];
|
||
},
|
||
classChange(e) {
|
||
this.rangeIndex = e.detail.value;
|
||
},
|
||
selectTimeSlot(item, index) {
|
||
this.timeindex = index;
|
||
},
|
||
async getXZJGDeptList() {
|
||
try {
|
||
const res = await getXZJGDept();
|
||
//二级学院排列位置调整
|
||
let depts = res.data;
|
||
let quList = depts.slice(14,25)
|
||
depts.splice(14,11);
|
||
depts.splice(0, 0, ...quList);
|
||
this.deptXZJGList = depts;
|
||
//this.deptXZJGList = res.data;
|
||
} catch (error) {
|
||
console.error('获取部门列表失败:', error);
|
||
uni.showToast({ title: '获取部门列表失败', icon: 'none' });
|
||
}
|
||
},
|
||
async getlist() {
|
||
try {
|
||
const res = await selectRtFuReservationTime({
|
||
roomNo: this.list.roomNo,
|
||
rtTime: this.rtTime
|
||
});
|
||
this.slotslist = res.data || [];
|
||
} catch (error) {
|
||
console.error('获取时间段失败:', error);
|
||
uni.showToast({ title: '获取可用时间段失败', icon: 'none' });
|
||
}
|
||
},
|
||
async getReserved() {
|
||
try {
|
||
const res = await getDicts('routine_rtfureservations_rt_purpose');
|
||
this.Reserveduse = res.data;
|
||
} catch (error) {
|
||
console.error('获取预约用途失败:', error);
|
||
}
|
||
},
|
||
async getrole() {
|
||
try {
|
||
const res = await getDicts('routine_rtfureservation_rt_role');
|
||
this.rolelist = res.data;
|
||
} catch (error) {
|
||
console.error('获取人员类型失败:', error);
|
||
}
|
||
},
|
||
validateForm() {
|
||
if (!this.formData.rtTheme) {
|
||
uni.showToast({ title: '请输入活动主题', icon: 'none' });
|
||
return false;
|
||
}
|
||
|
||
if (!this.formData.rtCreatRole) {
|
||
uni.showToast({ title: '请输入申请人姓名', icon: 'none' });
|
||
return false;
|
||
}
|
||
|
||
if (!this.formData.rtDepar) {
|
||
uni.showToast({ title: '请选择使用部门/学院', icon: 'none' });
|
||
return false;
|
||
}
|
||
|
||
if (!this.formData.rtPeople || this.formData.rtPeople <= 0) {
|
||
uni.showToast({ title: '请输入有效的参与人数', icon: 'none' });
|
||
return false;
|
||
}
|
||
|
||
if (this.formData.rtPeople > this.list.roomCapacity) {
|
||
uni.showToast({
|
||
title: `人数超过限制(最多${this.list.roomCapacity}人)`,
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
|
||
if (!this.Reserveduse.length || !this.Reserveduse[this.rangeIndex]) {
|
||
uni.showToast({ title: '请选择预约用途', icon: 'none' });
|
||
return false;
|
||
}
|
||
|
||
if (this.selectedRoles.length === 0) {
|
||
uni.showToast({ title: '请选择人员类型', icon: 'none' });
|
||
return false;
|
||
}
|
||
|
||
if (this.timeindex === null) {
|
||
uni.showToast({ title: '请选择预约时间段', icon: 'none' });
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
},
|
||
async onSubmit() {
|
||
if (this.isSubmitting) return;
|
||
|
||
if (!this.validateForm()) return;
|
||
|
||
this.isSubmitting = true;
|
||
|
||
try {
|
||
const form = {
|
||
...this.formData,
|
||
rtPurpose: this.Reserveduse[this.rangeIndex].dictLabel,
|
||
rtRole: this.selectedRoles.join(","),
|
||
roomNo: this.list.roomNo,
|
||
roomName: this.list.roomName,
|
||
rtNo: this.slotslist[this.timeindex].rtNo,
|
||
rtTime: this.rtTime,
|
||
rtTimePeriod: this.slotslist[this.timeindex].openingHours
|
||
};
|
||
|
||
console.log('提交数据:', form);
|
||
|
||
const res = await addRoomReservation(form);
|
||
|
||
if (res.code === 200) {
|
||
uni.showToast({ title: '预约成功' });
|
||
setTimeout(() => {
|
||
uni.redirectTo({ url: "../appointment/index" });
|
||
}, 1500);
|
||
} else {
|
||
uni.showToast({ title: res.msg || '预约失败', icon: 'none' });
|
||
}
|
||
} catch (error) {
|
||
console.error('预约提交失败:', error);
|
||
uni.showToast({ title: '预约提交失败', icon: 'none' });
|
||
} finally {
|
||
this.isSubmitting = false;
|
||
}
|
||
}
|
||
},
|
||
onLoad(option) {
|
||
this.rtTime = option.rtTime;
|
||
this.list = JSON.parse(option.list);
|
||
this.formData.roomName = this.list.roomName;
|
||
|
||
Promise.all([
|
||
this.getlist(),
|
||
this.getReserved(),
|
||
this.getrole(),
|
||
this.getXZJGDeptList()
|
||
]).catch(error => {
|
||
console.error('初始化失败:', error);
|
||
});
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.reservation {
|
||
background-color: #f4f4f4;
|
||
min-height: 100vh;
|
||
padding-bottom: 40rpx;
|
||
|
||
.form-container {
|
||
padding: 40rpx;
|
||
background-color: #fff;
|
||
margin-bottom: 20rpx;
|
||
|
||
.form-item {
|
||
margin-bottom: 30rpx;
|
||
padding: 20rpx;
|
||
background-color: #fff;
|
||
border-radius: 12rpx;
|
||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||
|
||
label {
|
||
display: block;
|
||
margin-bottom: 20rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
input, .picker {
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
padding: 0 20rpx;
|
||
border: 1rpx solid #e1e1e1;
|
||
border-radius: 10rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.uni-input {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
|
||
.val {
|
||
flex: 1;
|
||
}
|
||
|
||
.placeholder {
|
||
color: #b6b6b6;
|
||
}
|
||
}
|
||
|
||
.trigger {
|
||
.uni-input {
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
padding: 0 20rpx;
|
||
border: 1rpx solid #e1e1e1;
|
||
border-radius: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.time-selection {
|
||
background-color: #fff;
|
||
padding: 40rpx;
|
||
border-radius: 20rpx 20rpx 0 0;
|
||
|
||
.time-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 30rpx;
|
||
|
||
text:first-child {
|
||
font-size: 32rpx;
|
||
font-weight: 550;
|
||
}
|
||
|
||
text:last-child {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.time-slots {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
margin: 0 -10rpx;
|
||
|
||
.slot-item {
|
||
width: 33.33%;
|
||
padding: 10rpx;
|
||
box-sizing: border-box;
|
||
|
||
button {
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
font-size: 26rpx;
|
||
border-radius: 10rpx;
|
||
background-color: #f9fbfc;
|
||
color: #1890FF;
|
||
border: 1rpx solid #1890FF;
|
||
|
||
&.active {
|
||
background-color: #1890FF;
|
||
color: #fff;
|
||
}
|
||
|
||
&.disabled {
|
||
background-color: #f5f5f5;
|
||
color: #c1c1c1;
|
||
border-color: #e1e1e1;
|
||
}
|
||
|
||
&.warning {
|
||
border-color: #ff9900;
|
||
color: #ff9900;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.usage-matters {
|
||
margin-top: 40rpx;
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
|
||
text:first-child {
|
||
font-weight: bold;
|
||
margin-right: 10rpx;
|
||
}
|
||
}
|
||
|
||
.submit-btn {
|
||
margin-top: 50rpx;
|
||
height: 90rpx;
|
||
line-height: 90rpx;
|
||
font-size: 32rpx;
|
||
border-radius: 45rpx;
|
||
background-color: #1890FF;
|
||
|
||
&[disabled] {
|
||
background-color: #c1ccdf;
|
||
}
|
||
}
|
||
}
|
||
|
||
.drawer-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
z-index: 998;
|
||
}
|
||
|
||
.drawer {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: #fff;
|
||
border-radius: 20rpx 20rpx 0 0;
|
||
padding: 30rpx;
|
||
z-index: 999;
|
||
box-shadow: 0 -5rpx 20rpx rgba(0, 0, 0, 0.1);
|
||
|
||
.drawer-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 30rpx;
|
||
padding-bottom: 20rpx;
|
||
border-bottom: 1rpx solid #eee;
|
||
|
||
text:first-child {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.drawer-close {
|
||
color: #1890FF;
|
||
font-size: 28rpx;
|
||
}
|
||
}
|
||
|
||
.drawer-body {
|
||
max-height: 60vh;
|
||
overflow-y: auto;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.input-placeholder {
|
||
color: #b6b6b6;
|
||
font-size: 28rpx;
|
||
}
|
||
</style> |