Files
2025-07-16 15:34:34 +08:00

511 lines
13 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>