移动端V1.0

This commit is contained in:
2025-07-16 15:34:34 +08:00
commit 194b0750fd
1083 changed files with 178295 additions and 0 deletions

View File

@@ -0,0 +1,511 @@
<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>