371 lines
13 KiB
Vue
371 lines
13 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<u-navbar title="申请假期留宿" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
|
||
<view slot="left" @click="goBack">
|
||
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
|
||
</view>
|
||
</u-navbar>
|
||
|
||
<view class="form-wrap">
|
||
<view class="dorm-card">
|
||
<view class="dorm-title">当前宿舍(自动带出)</view>
|
||
<view v-if="dormLoading" class="dorm-text">加载中...</view>
|
||
<view v-else-if="dormText" class="dorm-text">{{ dormText }}</view>
|
||
<view v-else class="dorm-text dorm-warn">未查询到在住床位,请先完成宿舍床位分配后再申请</view>
|
||
</view>
|
||
|
||
<u-form :model="form" ref="formRef" label-width="90">
|
||
<u-form-item label="假期类型" prop="holidayType" required>
|
||
<view @click="openHolidayPicker" style="width: 100%">
|
||
<u-input
|
||
v-model="holidayTypeLabel"
|
||
placeholder="请选择假期类型"
|
||
readonly
|
||
border="none"
|
||
suffix-icon="arrow-down"
|
||
suffix-icon-style="color: #c8c9cc;"
|
||
style="pointer-events: none"
|
||
/>
|
||
</view>
|
||
</u-form-item>
|
||
|
||
<u-form-item label="开始时间" prop="applyStartDate" required>
|
||
<view @click="openDate('start')" style="width: 100%">
|
||
<u-input
|
||
v-model="form.applyStartDate"
|
||
placeholder="请选择开始时间"
|
||
readonly
|
||
border="none"
|
||
suffix-icon="arrow-down"
|
||
suffix-icon-style="color: #c8c9cc;"
|
||
style="pointer-events: none"
|
||
/>
|
||
</view>
|
||
</u-form-item>
|
||
|
||
<u-form-item label="结束时间" prop="applyEndDate" required>
|
||
<view @click="openDate('end')" style="width: 100%">
|
||
<u-input
|
||
v-model="form.applyEndDate"
|
||
placeholder="请选择结束时间"
|
||
readonly
|
||
border="none"
|
||
suffix-icon="arrow-down"
|
||
suffix-icon-style="color: #c8c9cc;"
|
||
style="pointer-events: none"
|
||
/>
|
||
</view>
|
||
</u-form-item>
|
||
|
||
<u-form-item label="原因类型" prop="stayReasonType">
|
||
<view @click="openReasonPicker" style="width: 100%">
|
||
<u-input
|
||
v-model="stayReasonTypeLabel"
|
||
placeholder="请选择原因类型"
|
||
readonly
|
||
border="none"
|
||
suffix-icon="arrow-down"
|
||
suffix-icon-style="color: #c8c9cc;"
|
||
style="pointer-events: none"
|
||
/>
|
||
</view>
|
||
</u-form-item>
|
||
|
||
<u-form-item label="留宿原因" prop="stayReason" required>
|
||
<u-textarea
|
||
v-model="form.stayReason"
|
||
placeholder="请填写留宿原因"
|
||
maxlength="500"
|
||
count
|
||
></u-textarea>
|
||
</u-form-item>
|
||
<u-form-item label="联系电话" prop="studentPhone">
|
||
<u-input v-model="form.studentPhone" placeholder="可自动带出,可修改" />
|
||
</u-form-item>
|
||
<u-form-item label="紧急联系人" prop="emergencyContact">
|
||
<u-input v-model="form.emergencyContact" placeholder="请输入紧急联系人" />
|
||
</u-form-item>
|
||
<u-form-item label="紧急电话" prop="emergencyPhone">
|
||
<u-input v-model="form.emergencyPhone" placeholder="请输入紧急联系电话" />
|
||
</u-form-item>
|
||
<u-form-item label="家长同意" prop="parentConsent">
|
||
<u-radio-group v-model="form.parentConsent" class="radio-group">
|
||
<u-radio label="1" name="1" class="radio-item">是</u-radio>
|
||
<u-radio label="0" name="0" class="radio-item">否</u-radio>
|
||
</u-radio-group>
|
||
</u-form-item>
|
||
<u-form-item label="备注" prop="remark">
|
||
<u-textarea v-model="form.remark" placeholder="选填" maxlength="200"></u-textarea>
|
||
</u-form-item>
|
||
</u-form>
|
||
|
||
<u-button type="primary" :loading="submitting" text="提交申请" @click="submit"></u-button>
|
||
<view class="tip">提交后由班主任一级审批;宿舍信息将按当前入住床位自动带出。</view>
|
||
</view>
|
||
|
||
<u-picker
|
||
:show="showHolidayPicker"
|
||
:columns="holidayColumns"
|
||
keyName="label"
|
||
@confirm="onHolidayConfirm"
|
||
@cancel="showHolidayPicker = false"
|
||
@close="showHolidayPicker = false"
|
||
></u-picker>
|
||
|
||
<u-picker
|
||
:show="showReasonPicker"
|
||
:columns="reasonColumns"
|
||
keyName="label"
|
||
@confirm="onReasonConfirm"
|
||
@cancel="showReasonPicker = false"
|
||
@close="showReasonPicker = false"
|
||
></u-picker>
|
||
|
||
<u-datetime-picker
|
||
:show="showDatePicker"
|
||
v-model="dateValue"
|
||
mode="datetime"
|
||
@confirm="onDateConfirm"
|
||
@cancel="showDatePicker = false"
|
||
@close="showDatePicker = false"
|
||
></u-datetime-picker>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { applyHolidayStay } from '@/api/holidayStay.js';
|
||
import { getDict } from '@/api/system/dict/data.js';
|
||
import { request } from '@/common/request.js';
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
submitting: false,
|
||
showHolidayPicker: false,
|
||
showReasonPicker: false,
|
||
showDatePicker: false,
|
||
dateField: 'start',
|
||
dateValue: Number(new Date()),
|
||
holidayTypeDict: [],
|
||
stayReasonTypeDict: [],
|
||
holidayTypeLabel: '',
|
||
stayReasonTypeLabel: '',
|
||
dormLoading: false,
|
||
dormText: '',
|
||
hasDorm: false,
|
||
studentId: null,
|
||
form: {
|
||
holidayType: '',
|
||
applyStartDate: '',
|
||
applyEndDate: '',
|
||
stayReasonType: 'other',
|
||
stayReason: '',
|
||
studentPhone: '',
|
||
emergencyContact: '',
|
||
emergencyPhone: '',
|
||
parentConsent: '1',
|
||
remark: ''
|
||
}
|
||
};
|
||
},
|
||
computed: {
|
||
holidayColumns() {
|
||
return [this.holidayTypeDict || []];
|
||
},
|
||
reasonColumns() {
|
||
return [this.stayReasonTypeDict || []];
|
||
}
|
||
},
|
||
async onLoad() {
|
||
try {
|
||
this.holidayTypeDict = (await getDict('xg_holiday_type')) || [];
|
||
this.stayReasonTypeDict = (await getDict('xg_stay_reason_type')) || [];
|
||
const reason = this.stayReasonTypeDict.find((d) => String(d.value) === 'other');
|
||
if (reason) {
|
||
this.stayReasonTypeLabel = reason.label;
|
||
this.form.stayReasonType = reason.value;
|
||
}
|
||
const stuRes = await request({
|
||
url: '/studentMsg/studentMsg/currentUserInfo',
|
||
method: 'GET'
|
||
});
|
||
const stu = stuRes.data || stuRes;
|
||
if (stu && stu.phone) {
|
||
this.form.studentPhone = stu.phone;
|
||
}
|
||
if (stu && stu.id) {
|
||
this.studentId = stu.id;
|
||
await this.loadDormInfo(stu.id);
|
||
}
|
||
} catch (e) {
|
||
// ignore
|
||
}
|
||
},
|
||
methods: {
|
||
goBack() {
|
||
uni.navigateBack();
|
||
},
|
||
async loadDormInfo(studentId) {
|
||
this.dormLoading = true;
|
||
this.hasDorm = false;
|
||
this.dormText = '';
|
||
try {
|
||
const res = await request({
|
||
url: `/dormBedStudent/dormBedStudent/currentDormInfo/${studentId}`,
|
||
method: 'GET'
|
||
});
|
||
const dorm = res.data || res;
|
||
if (dorm && (dorm.roomId || dorm.bedId || dorm.buildingName || dorm.roomName)) {
|
||
const parts = [
|
||
dorm.campusName,
|
||
dorm.buildingName,
|
||
dorm.roomName || dorm.roomNo,
|
||
dorm.bedName || dorm.bedNo
|
||
].filter(Boolean);
|
||
this.dormText = parts.join(' / ');
|
||
this.hasDorm = !!(dorm.roomId && dorm.bedId);
|
||
}
|
||
} catch (e) {
|
||
this.dormText = '';
|
||
this.hasDorm = false;
|
||
} finally {
|
||
this.dormLoading = false;
|
||
}
|
||
},
|
||
openHolidayPicker() {
|
||
if (!this.holidayTypeDict.length) {
|
||
uni.showToast({ title: '假期类型加载中,请稍后', icon: 'none' });
|
||
return;
|
||
}
|
||
this.showHolidayPicker = true;
|
||
},
|
||
openReasonPicker() {
|
||
if (!this.stayReasonTypeDict.length) {
|
||
uni.showToast({ title: '原因类型加载中,请稍后', icon: 'none' });
|
||
return;
|
||
}
|
||
this.showReasonPicker = true;
|
||
},
|
||
openDate(field) {
|
||
this.dateField = field;
|
||
const current = field === 'start' ? this.form.applyStartDate : this.form.applyEndDate;
|
||
this.dateValue = current ? new Date(current.replace(/-/g, '/')).getTime() : Number(new Date());
|
||
this.showDatePicker = true;
|
||
},
|
||
onHolidayConfirm(e) {
|
||
const item = (e.value && e.value[0]) || null;
|
||
if (item) {
|
||
this.form.holidayType = item.value;
|
||
this.holidayTypeLabel = item.label;
|
||
}
|
||
this.showHolidayPicker = false;
|
||
},
|
||
onReasonConfirm(e) {
|
||
const item = (e.value && e.value[0]) || null;
|
||
if (item) {
|
||
this.form.stayReasonType = item.value;
|
||
this.stayReasonTypeLabel = item.label;
|
||
}
|
||
this.showReasonPicker = false;
|
||
},
|
||
onDateConfirm(e) {
|
||
const ts = e && e.value !== undefined ? e.value : e;
|
||
const d = new Date(Number(ts));
|
||
if (Number.isNaN(d.getTime())) {
|
||
this.showDatePicker = false;
|
||
return;
|
||
}
|
||
const pad = (n) => (n < 10 ? '0' + n : '' + n);
|
||
const str =
|
||
d.getFullYear() +
|
||
'-' +
|
||
pad(d.getMonth() + 1) +
|
||
'-' +
|
||
pad(d.getDate()) +
|
||
' ' +
|
||
pad(d.getHours()) +
|
||
':' +
|
||
pad(d.getMinutes()) +
|
||
':' +
|
||
pad(d.getSeconds());
|
||
if (this.dateField === 'start') {
|
||
this.form.applyStartDate = str;
|
||
} else {
|
||
this.form.applyEndDate = str;
|
||
}
|
||
this.showDatePicker = false;
|
||
},
|
||
async submit() {
|
||
if (!this.hasDorm) {
|
||
return uni.showToast({ title: '未分配宿舍床位,无法申请', icon: 'none' });
|
||
}
|
||
if (!this.form.holidayType) {
|
||
return uni.showToast({ title: '请选择假期类型', icon: 'none' });
|
||
}
|
||
if (!this.form.applyStartDate || !this.form.applyEndDate) {
|
||
return uni.showToast({ title: '请选择留宿时间', icon: 'none' });
|
||
}
|
||
if (!this.form.stayReason || !this.form.stayReason.trim()) {
|
||
return uni.showToast({ title: '请填写留宿原因', icon: 'none' });
|
||
}
|
||
this.submitting = true;
|
||
try {
|
||
await applyHolidayStay({ ...this.form });
|
||
uni.showToast({ title: '提交成功', icon: 'success' });
|
||
setTimeout(() => {
|
||
uni.navigateBack();
|
||
}, 500);
|
||
} catch (e) {
|
||
uni.showToast({ title: (e && e.msg) || '提交失败', icon: 'none' });
|
||
} finally {
|
||
this.submitting = false;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page-container {
|
||
min-height: 100vh;
|
||
background: #f5f6f8;
|
||
}
|
||
.form-wrap {
|
||
margin: 24rpx;
|
||
padding: 24rpx;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
}
|
||
.tip {
|
||
margin-top: 24rpx;
|
||
font-size: 24rpx;
|
||
color: #909399;
|
||
line-height: 1.5;
|
||
}
|
||
.dorm-card {
|
||
margin-bottom: 24rpx;
|
||
padding: 20rpx;
|
||
background: #f7f8fa;
|
||
border-radius: 12rpx;
|
||
}
|
||
.dorm-title {
|
||
font-size: 26rpx;
|
||
color: #909399;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
.dorm-text {
|
||
font-size: 28rpx;
|
||
color: #303133;
|
||
}
|
||
.dorm-warn {
|
||
color: #e6a23c;
|
||
}
|
||
.radio-group {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.radio-item {
|
||
margin-right: 32rpx;
|
||
}
|
||
</style>
|