Files
zhxg_app_v1.0/pages/applyleave/applyleave.vue

733 lines
18 KiB
Vue
Raw Normal View History

2025-07-16 15:34:34 +08:00
<template>
<view class="container" style="padding-bottom: 60px;">
<!--学号 -->
<view class="form-item">
<text class="label">学号</text>
<text class="uni-input">{{ stuInfo.stuNo }}</text>
</view>
<!--姓名 -->
<view class="form-item">
<text class="label">姓名</text>
<text class="uni-input">{{ stuInfo.studentName }}</text>
</view>
<!-- 性别 -->
<view class="form-item">
<text class="label">性别</text>
<text class="uni-input">{{ stuInfo.gender }}</text>
</view>
<!--院部 -->
<view class="form-item">
<text class="label">院部</text>
<text class="uni-input">{{ stuInfo.departmentName }}</text>
</view>
<!--班级 -->
<view class="form-item">
<text class="label">班级</text>
<text class="uni-input">{{ stuInfo.className }}</text>
</view>
<!--联系电话 -->
<view class="form-item">
<text class="label">联系电话</text>
<text class="uni-input">{{ stuInfo.phoneNumber }}</text>
</view>
<!--家长姓名 -->
<view class="form-item">
<text class="label"><text class="required">*</text>家长姓名</text>
<input v-model="formData.fatherName" class="uni-input" focus placeholder="家长姓名" />
</view>
<!--家长联系电话 -->
<view class="form-item">
<text class="label"><text class="required">*</text>家长联系电话</text>
<input v-model="formData.fatherRelation" class="uni-input" focus placeholder="家长联系电话" />
</view>
<!-- 请假事由 -->
<view class="form-item">
<text class="label"> <text class="required">*</text> 请假事由</text>
<picker :range="politicalOptions" v-model="formData.selectedPoliticalIndex" @change="onPoliticalChange">
<view class="select">{{ politicalOptions[formData.selectedPoliticalIndex] || '请选择请假事由' }}</view>
</picker>
</view>
<!-- 请假事由说明 -->
<view class="form-text">
<text class="label"> <text class="required">*</text> 请假事由说明</text>
<view class="txt">
<uni-forms-item>
<uni-easyinput type="textarea" v-model="formData.deeds" maxlength="500" placeholder="输入请假事由说明" />
</uni-forms-item>
</view>
</view>
<!-- 请假时间 -->
<view class="form-item"> <!-- 根据选择的申请类型来决定是否显示 -->
<text class="label"> <text class="required">*</text> 请假起始时间</text>
<view class="example-body">
<uni-datetime-picker @change="changeDate('start',$event)" returnType="timestamp"
v-model="formData.single" />
</view>
</view>
<view class="form-item"> <!-- 根据选择的申请类型来决定是否显示 -->
<text class="label"> <text class="required">*</text> 请假结束时间</text>
<view class="example-body">
<uni-datetime-picker @change="changeDate('end',$event)" returnType="timestamp"
v-model="formData.endsingle" />
</view>
</view>
<!--请假天数 -->
<view class="form-item">
<text class="label"> <text class="required">*</text> 请假天数</text>
<input v-model="formData.dataNum" class="uni-input" focus placeholder="请输入请假天数 " />
</view>
<!-- 目的地 -->
<view class="form-item">
<text class="label"> <text class="required">*</text> 目的地</text>
<input v-model="formData.endAddress" class="uni-input" focus placeholder="请输入目的地 " />
</view>
<!--详细地址 -->
<view class="form-item">
<text class="label"> <text class="required">*</text> 详细地址</text>
<input v-model="formData.address" class="uni-input" focus placeholder="请输入详细地址 " />
</view>
<!-- 是否离桂 -->
<view class="form-item">
<text class="label">是否离桂</text>
<checkbox class="uni-input" :value="isLeaveGui.toString()" @click="toggleLG" />
</view>
<!-- 上传佐证 -->
<view class="form-img">
<text class="label">上传佐证</text>
<view class="example-body">
<uni-file-picker @select="uploadEvidence" @delete="onDelImg" :auto-upload="false"
limit="9"></uni-file-picker>
</view>
</view>
<!--协议 -->
<view class="form-item">
安全承诺书
<text class="labelsafe">广西水利电力职业技术学院安全承诺书
</text>
<label>
<checkbox :value="checkboxValue.toString()" @click="toggleCheckbox" />
</label>
</view>
<!-- 签名 -->
<view class="form-canvas">
<text class="label"> <text class="required">*</text> 手写签字</text>
<view class="sign-img" v-if="signImg!=''">
<image :src="signImg" mode=""></image>
<text @tap="signToggle">重新签名</text>
</view>
<view v-else class="sign" @tap="signToggle">
点击签名
</view>
</view>
<view class="form-item">
<text class="label">备注</text>
<input v-model="remark" class="uni-input" placeholder="请输入备注" />
</view>
<!-- 按钮 -->
<view class="btn">
<button style="background-color: red;" @click="clean">清除</button>
<button style="background-color: #fff; color: #4097FE;border: 1px solid #4097FE;" @click="save">保存</button>
<button :disabled="isSubmitting" @click="applyLeave()">提交申请</button>
</view>
<!-- 旧签名 -->
<!-- <uni-popup ref="popup" background-color="#fff">
<view class="popup-content">
<liu-signature @change="uploadSign" bgColor="#e8e8e8" lineColor="#000" :height="1500"></liu-signature>
</view>
</uni-popup> -->
<!-- 新签名组件 -->
<jp-signature-popup ref="jpSignature" :required="true" popup @change="uploadSign"/>
<uni-popup class="safety-popup" :is-mask-click="false" ref="safetyPopup" background-color="#fff">
<view class="popup-content">
<view class="title">
请假承诺书
</view>
<view class="content">
<view class="txt">
1严格按照学校规定的请假程序提出请假申请并如实说明请假原因
2在请假期间积极补习错过的课程内容保持对学习的持续关注和学习态度
3遵守学校纪律和规定不参与任何影响班级秩序和学校形象的行为
4如有特殊情况需要延长请假时间会及时向学校或老师做出说明和沟通
5意识到请假可能会对我的学业和成绩造成负面影响我愿意承担相应的责任和后果
</view>
<view class="time" v-if="safetime>0">
请阅读{{safetime}}s承诺书
</view>
</view>
<view class="safety-btn">
<button :disabled="isDisabled" @tap="closeSafePopup">我知道了</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import uploadFile from "@/plugins/upload.js";
import {
baseUrl
} from "@/config.js";
import {
calculateDays,
convertToTimestamp
} from "@/utils/time-calculation.js"
import {
addApply,
getStuInfo,
commonUpload,
leaveSave
} from "@/api/applyleave/applyleave.js";
export default {
data() {
return {
isSubmitting: false, //表单提交标志位
isDisabled: true,
safetime: 5,
// 用户信息
stuInfo: {},
studID: "",
name: "",
courtyard: "",
sex: '',
classroom: "",
phone: "",
parent: "",
parentPhone: "",
selectedOptionIndex: -1, // 当前选中的奖项索引
politicalOptions: ['因病', '因事'], // 请假选项
beginData: '',
endData: "",
remark: "",
leave: ['是', '否'],
leaveIndex: -1,
url: "",
urltwo: "",
checkboxValue: 0,
isLeaveGui: 0, //是否离桂
//附件
attachmentFile: "",
signImg: "",
formData: {
selectedPoliticalIndex: "", // 当前选中的请假索引
deeds: "",
endsingle: "",
single: '',
dataNum: "",
endAddress: "",
address: "",
signFile: "",
},
};
},
created() {
this.getSut();
},
mounted() {
console.log(this.one)
},
methods: {
toggleLG() {
this.isLeaveGui = this.isLeaveGui === 1 ? 0 : 1;
},
// 获取学生信息
getSut() {
getStuInfo().then(res => {
console.log(res);
if (res.code == 200) {
this.stuInfo = res.data
}
if (res.code == 500) {
uni.showToast({
title: res.msg,
icon: "error"
})
}
});
},
changeDate(type, e) {
this.formData.single = convertToTimestamp(this.formData.single);
this.formData.endsingle = convertToTimestamp(this.formData.endsingle);
if (type == "end") {
this.formData.endsingle = e;
if (this.formData.endsingle < this.formData.single) {
uni.showToast({
title: "时间不能早于请假开始时间",
icon: "none"
})
}
this.formData.dataNum = calculateDays(this.formData.single, this.formData.endsingle);
} else {
this.formData.single = e;
if (this.formData.endsingle < this.formData.single) {
uni.showToast({
title: "起始时间不能晚于结束时间",
icon: "none"
})
}
this.formData.dataNum = calculateDays(this.formData.single, this.formData.endsingle);
}
},
signToggle() {
// open 方法传入参数 等同在 uni-popup 组件上绑定 type属性
// this.$refs.popup.open('center')
//打开签名
this.$refs.jpSignature.toPop();
},
closeSafePopup() {
this.$refs.safetyPopup.close('center')
},
// 提交申请
applyLeave() {
uni.showLoading({
title: "正在提交"
})
if (this.checkboxValue == 0) {
// 如果复选框未被选中,则不允许提交
uni.showToast({
title: "请勾选承诺书",
icon: "error"
});
return; // 阻止表单提交
}
if (this.formData.signFile == '') {
// 如果复选框未被选中,则不允许提交
uni.showToast({
title: "请先进行签名",
icon: "error"
});
return; // 阻止表单提交
}
if (this.formData.endsingle < this.formData.single) {
uni.showToast({
title: "起始时间不能晚于结束时间",
icon: "none"
})
return; // 阻止表单提交
}
//表单非空校验
const requiredFields = [
'selectedPoliticalIndex',
'deeds',
'endsingle',
'single',
'dataNum',
'endAddress',
'address'
];
const emptyField = requiredFields.find(field => this.formData[field] === "");
if (emptyField) {
uni.showToast({
title: `请填写完必填字段`,
icon: "none"
})
return;
}
let data = {
applicantSignature: this.formData.signFile,
attachment: this.attachmentFile,
destination: this.formData.endAddress,
destinationDetails: this.formData.address,
endDate: this.formData.endsingle,
leaveDays: this.formData.dataNum,
leaveType: this.formData.selectedPoliticalIndex,
reason: this.formData.deeds,
remark: this.remark,
safetyPromise: 1,
startDate: this.formData.single
}
this.isSubmitting = true; // 设置为正在提交
addApply(data).then(res => {
if (res.code == 200) {
uni.showToast({
title: "提交成功",
icon: "success"
});
uni.hideLoading();
this.isSubmitting = false;
uni.navigateBack({
success: () => {
const pages = getCurrentPages();
if (pages.length > 0) {
const prevPage = pages[pages.length - 2]
if (prevPage && typeof prevPage.getLeaveList ===
'function') {
prevPage.getLeaveList();
}
}
}
})
} else {
uni.showToast({
title: "提交失败,请先填写完整内容",
icon: "none"
});
}
});
},
//上传佐证
uploadEvidence(e) {
uploadFile('/common/upload', e.tempFilePaths[0]).then((res) => {
if (this.attachmentFile !== "") {
this.attachmentFile = this.attachmentFile + "," + JSON.parse(res).fileName;
} else {
this.attachmentFile = JSON.parse(res).fileName;
}
})
},
//删除已选图片
onDelImg(e) {
console.log(this.attachmentFile);
console.log(e);
},
//上传签名
uploadSign(e) {
const blob = this.base64ToBlob(e);
const fileObj = new File([blob], 'signature', {
type: 'image/png'
});
const formData = new FormData();
formData.append("file", fileObj);
const formDataObj = this.formDataToObject(formData);
uploadFile('/common/upload', '', formDataObj).then((res) => {
this.formData.signFile = JSON.parse(res).fileName;
this.signImg = baseUrl + JSON.parse(res).fileName;
// this.$refs.popup.close('center')
})
},
formDataToObject(formData) {
const obj = {};
formData.forEach((value, key) => {
obj[key] = value;
});
return obj;
},
base64ToBlob(base64String) {
const byteCharacters = atob(base64String.split(',')[1]);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
return new Blob([byteArray], {
type: 'application/octet-stream'
});
},
onPickerChange(event) {
this.selectedOptionIndex = event.detail.value;
},
onPoliticalChange(event) {
this.formData.selectedPoliticalIndex = event.detail.value;
},
toggleCheckbox() {
// 切换 checkboxValue 的值
this.checkboxValue = this.checkboxValue === 1 ? 0 : 1;
if (this.checkboxValue) {
this.$refs.safetyPopup.open('center')
if (this.safetime > 0) {
const intervalId = setInterval(() => {
this.safetime--;
console.log(this.safetime);
if (this.safetime === 0) {
clearInterval(intervalId); // 停止倒计时
this.isDisabled = false;
}
}, 1000);
}
}
},
// 清空表单
clean() {
this.studID = "";
this.name = "";
this.courtyard = "";
this.formData.selectedOptionIndex = "";
this.classroom = '';
this.phone = '';
this.parent = '';
this.parentPhone = "";
this.selectedPoliticalIndex = ""
this.beginData = "";
this.endData = '';
this.formData.dataNum = '';
this.formData.deeds = '';
this.leaveIndex = -1;
this.formData.endAddress = '';
this.formData.address = '';
this.isChecked = false;
this.$forceUpdate(); // 强制更新组件
this.url = ''
localStorage.removeItem('formData');
},
//保存假条信息
save() {
if (this.checkboxValue == 0) {
// 如果复选框未被选中,则不允许提交
uni.showToast({
title: "请勾选承诺书",
icon: "error"
});
return; // 阻止表单提交
}
if (this.formData.signFile == '' || this.formData.signFile == null) {
// 如果复选框未被选中,则不允许提交
uni.showToast({
title: "请先进行签名",
icon: "error"
});
return; // 阻止表单提交
}
if (this.endsingle < this.single) {
uni.showToast({
title: "起始时间不能晚于结束时间",
icon: "none"
})
return; // 阻止表单提交
}
this.isSubmitting = true;
let data = {
applicantSignature: this.formData.signFile,
attachment: this.attachmentFile,
destination: this.endAddress,
destinationDetails: this.address,
endDate: this.endsingle,
leaveDays: this.dataNum,
leaveType: this.selectedPoliticalIndex,
reason: this.deeds,
remark: this.remark,
safetyPromise: 1,
startDate: this.single
}
leaveSave(data).then(res => {
if (res.code == 200) {
uni.showToast({
title: "保存成功",
icon: "success"
});
this.isSubmitting = false;
uni.navigateBack({
success: () => {
const pages = getCurrentPages();
if (pages.length > 0) {
const prevPage = pages[pages.length - 2]
if (prevPage && typeof prevPage.getLeaveList ===
'function') {
prevPage.getLeaveList();
}
}
}
})
} else {
uni.showToast({
title: "提交失败,请先填写完整内容",
icon: "none"
});
}
});
},
},
};
</script>
<style scoped lang="scss">
.container {
display: flex;
flex-direction: column;
align-items: center;
}
.form-item {
padding: 15rpx 20rpx;
width: 100%;
height: 80rpx;
display: flex;
align-items: center;
margin-bottom: 20rpx;
justify-content: space-between;
checkbox {
/deep/.uni-checkbox-input.uni-checkbox-input-checked {
background: #007aff;
}
}
}
.form-text {
width: 95%;
margin-bottom: 20rpx;
border-bottom: 1px solid #ededee;
}
.form-img {
width: 95%;
margin-bottom: 20rpx;
border-bottom: 1px solid #ededee;
}
.example-body {
margin-top: 20rpx;
margin-bottom: 20rpx;
}
.txt {
margin-top: 20rpx;
}
.label {
text-align: right;
margin-right: 20rpx;
.required {
color: red;
}
}
.select {
color: #888889;
white-space: nowrap;
/* 防止换行 */
overflow: hidden;
/* 隐藏溢出部分 */
text-overflow: ellipsis;
/* 显示省略号 */
}
.uni-input {
color: #888889;
text-align: right;
}
.btn {
display: flex;
justify-content: space-between;
position: fixed;
bottom: 0;
left: 0;
margin: 0 auto;
border-top: 1px solid #d6d6d6;
background: #fff;
padding: 10px;
right: 0;
button {
width: 30%;
background-color: #4097FE;
color: white;
}
}
.form-address {
width: 100%;
display: flex;
justify-content: space-between;
}
.labelsafe {
color: red;
font-size: 24rpx;
}
.form-canvas {
width: 95%;
margin-bottom: 25px;
.sign-img {
position: relative;
margin-top: 8px;
image {
width: 100%;
}
text {
position: absolute;
right: 9px;
bottom: 10px;
background: white;
padding: 3px 5px;
border-radius: 2px;
color: #5c5c5c;
}
}
/* height: 700rpx; */
.sign {
margin-top: 8px;
border: 1px solid #ebebeb;
padding: 25px;
color: #1e6fff;
text-align: center;
font-weight: bold;
}
}
.tab-box {
width: 80%;
height: 100vh;
background-color: #f0f0f0;
.success-img {
width: 90%;
height: 300rpx;
}
}
.safety-popup {}
</style>
<style lang="scss">
.popup-content {
height: 100vh;
box-sizing: border-box;
.title {
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 25px;
}
.content {
color: #7e7e7e;
padding: 0 25px 0 25px;
line-height: 25px;
.time {
text-align: center;
margin-top: 25px;
}
}
.safety-btn {
margin-top: 35px;
border-top: 1px solid #b6b6b6;
padding: 5px 0;
button {
&::after {
border: none;
}
}
}
}
</style>