diff --git a/src/components/affix/index.vue b/src/components/affix/index.vue index 1b3f3ae..b60ee34 100644 --- a/src/components/affix/index.vue +++ b/src/components/affix/index.vue @@ -1,47 +1,33 @@ @@ -49,310 +35,379 @@ import { deleteAffix, download, downloadAll, queryAffixs, upload } from '@/api/affix/affix' export default { - name: 'CmAffix', - inject: { - elForm: { - default: '' - } - }, - props: { - notupload:{ - type:Boolean, - default:false - }, - value: String, // 父组件值 - disabled: Boolean, - maxSize:{ - type: Number, - default: 20 - }, - accpet:{ - type:String, - default:'*' - }, - uploadStyle: - { - type: String, - default: '' - }, - - - }, - data() { - return { - uploadCnt: 0, - affixId: '', - fileList: [], - downloading:false,//控制打包下载loading动画 - baseurl: process.env.VUE_APP_BASE_API , - hiddenSrc:'', - previewList:[], - } - }, - computed: { - inputDisabled() { - return this.disabled || (this.elForm || {}).disabled - } - }, - watch: { - // 父组件值监听事件 - value: { - handler: function(newVal/*, oldVal*/) { - if (newVal === undefined || newVal == null || newVal === '' - || this.affixId === undefined || this.affixId == null || this.affixId === '' - || newVal !== this.affixId) { - this.loadData() - } - } - } - }, - created() { - this.loadData() - }, - methods: { - // 预览 - preview(item){ - this.hiddenSrc = item.savePath - - this.$refs.preview.showViewer = true - }, - isImageURL(url) { - // let imgRegex = /(\jpg|\jpeg|\png|\gif|\webp)$/i; - const regex = /(\jpg|\jpeg|\png|\gif|\webp)$/i - return regex.test(url) - }, - handleUpload(file) { - - upload({'file': file.file, 'affixId': this.affixId}).then(res => { - this.uploadCnt-- - - if (res.code == 200) { - for (let i = 0; i < this.fileList.length; i++) { - let item = this.fileList[i] - console.log(res.savePath) - if(item.name == res.trueName && item.status == 1){ - - this.fileList[i].id = res.id - this.fileList[i].status = 2 - this.fileList[i].savePath = this.baseurl + res.savePath - if(this.isImageURL(res.savePath)){ - this.previewList.push( this.fileList[i].savePath) - } - // this.fileList[i].savePaths =[this.baseurl + res.savePath]; - - // 上传成功后,向外传递当前文件的完整信息(包含后端返回的res数据) - this.$emit('fileUploaded', { - fileId: res.id, // 文件ID - fileName: res.trueName, // 文件名 - filePath: res.savePath, // 文件相对路径(不含baseurl) - fullPath: this.fileList[i].savePath, // 完整路径(含baseurl) - fileType: this.getFileType(res.trueName), // 文件类型 - originalFile: file.file // 原始文件对象(可选) - }); - - } - - } - - - } else { - this.$message.error(res.message) - } - }).catch(() => { - this.uploadCnt-- - }) - }, - // 获取文件类型的方法(Affix组件中新增) - getFileType(fileName) { - if (!fileName) return ''; - const lastDotIndex = fileName.lastIndexOf('.'); - return lastDotIndex > -1 ? fileName.substring(lastDotIndex + 1).toLowerCase() : ''; - }, - handleBeforeUpload(file) { - - if (this.affixId == null || this.affixId === '') { - this.affixId = this.$tool.uuid() - this.$emit('input', this.affixId) - } - - let isLt20M = file.size / 1024 / 1024 < this.maxSize - if (!isLt20M) { - this.$message.error('上传大小不能超过 '+this.maxSize+'MB!') - } else { - this.fileList.push({name: file.name, status: 1}) - this.uploadCnt++ - } - - return isLt20M - }, - getFileName(id){ - for (let i = 0; i < this.fileList.length; i++) { - let item = this.fileList[i] - - if(item.id == id){ - console.log(item) - return item.name - } - } - }, - downloadFile(file) { - download(file.id).then((res) => { - console.log(res) - let fileName = this.getFileName(file.id).replace(/"/g, '') - var blob = new Blob([res], {type: 'application/octet-stream;'}) - var downloadElement = document.createElement('a') - var href = window.URL.createObjectURL(blob) // 创建下载的链接 - downloadElement.href = href - downloadElement.download = decodeURI(fileName) // 下载后文件名 - document.body.appendChild(downloadElement) - downloadElement.click() // 点击下载 - document.body.removeChild(downloadElement) // 下载完成移除元素 - window.URL.revokeObjectURL(href) // 释放掉blob对象 - }).catch((res) => { - this.$message.error(res.message) - }) - }, - downloadPack() { - - if (this.affixId === null || this.affixId === '') { - this.$message.error('暂无附件!') - return - } - //如果只有一个文件就不打包下载 - if(this.fileList.length == 1){ - - this.downloadFile(this.fileList[0]) - }else{ - this.downloading = true - downloadAll(this.affixId).then((res) => { - var blob = new Blob([res], {type: 'application/octet-stream'}) - var downloadElement = document.createElement('a') - var href = window.URL.createObjectURL(blob) // 创建下载的链接 - downloadElement.href = href - downloadElement.download = decodeURI('download.zip') // 下载后文件名 - document.body.appendChild(downloadElement) - downloadElement.click() // 点击下载 - document.body.removeChild(downloadElement) // 下载完成移除元素 - window.URL.revokeObjectURL(href) // 释放掉blob对象 - this.downloading = false - }).catch(() => { - this.downloading = false - }) - } - - - }, - deleteFile(file) { - this.$confirm('请确认是否删除此文件?', '提示', {type: 'info'}).then(() => { - deleteAffix(file.id).then((res) => { - if (res.code == 200) { - this.fileList = this.fileList.filter(item => item.id !== file.id) - this.previewList = this.previewList.filter(item => item !== file.savePath)//删除图片 - if(this.fileList.length == 0){ - this.affixId = '' - } - - // 关键:触发自定义事件,传递被删除的文件名 - // 事件名建议:delete-file,参数:file.name(文件名) - this.$emit('delete-file', file.name); - } else { - this.$message.error(res.message) - } - }) - }) - }, - loadData() { - this.fileList = [] - if (typeof this.value !== 'undefined' && this.value !== null && this.value !== '') { - queryAffixs(this.value).then((res) => { - if (res.code ==200) { - if(res.data.length > 0){ - this.affixId = this.value - for (var i=0;i { - this.editLoading = false - this.$message.error(res.message) - }) - } else { - this.affixId = '' - } - }, - clearData() { - this.affixId = '' - this.fileList = [] - } + name: 'CmAffix', + inject: { + elForm: { + default: '' } + }, + props: { + notupload: { + type: Boolean, + default: false + }, + value: String, // 父组件值 + disabled: Boolean, + maxSize: { + type: Number, + default: 20 + }, + accpet: { + type: String, + default: '*' + }, + uploadStyle: + { + type: String, + default: '' + }, + + + }, + data() { + return { + uploadCnt: 0, + affixId: '', + fileList: [], + downloading: false,//控制打包下载loading动画 + baseurl: process.env.VUE_APP_BASE_API, + hiddenSrc: '', + previewList: [], + // 解决重复提交的核心变量 + isQuerying: false, // 请求锁:标记是否正在查询附件 + retryCount: 0 // 重试次数:捕获重复提交错误时重试 + } + }, + computed: { + inputDisabled() { + return this.disabled || (this.elForm || {}).disabled + } + }, + watch: { + // 父组件值监听事件 + value: { + immediate: true, // 初始化立即执行 + handler: function (newVal, oldVal) { + // 新旧值不同时,先清空所有旧数据(核心解决数据残留) + if (newVal !== oldVal) { + this.fileList = []; + this.previewList = []; + this.affixId = ''; + this.retryCount = 0; + } + // 满足条件才加载数据 + if (newVal && newVal !== '' && newVal !== this.affixId) { + this.loadData(); + } + } + } + }, + created() { + this.loadData() + }, + // 新增:组件激活时清空旧数据(适配keep-alive页面) + activated() { + this.fileList = []; + this.previewList = []; + if (this.value) { + this.loadData(); + } + }, + methods: { + // 预览 + preview(item) { + this.hiddenSrc = item.savePath + + this.$refs.preview.showViewer = true + }, + isImageURL(url) { + // let imgRegex = /(\jpg|\jpeg|\png|\gif|\webp)$/i; + const regex = /(\jpg|\jpeg|\png|\gif|\webp)$/i + return regex.test(url) + }, + handleUpload(file) { + + upload({ 'file': file.file, 'affixId': this.affixId }).then(res => { + this.uploadCnt-- + + if (res.code == 200) { + for (let i = 0; i < this.fileList.length; i++) { + let item = this.fileList[i] + console.log(res.savePath) + if (item.name == res.trueName && item.status == 1) { + + this.fileList[i].id = res.id + this.fileList[i].status = 2 + this.fileList[i].savePath = this.baseurl + res.savePath + if (this.isImageURL(res.savePath)) { + this.previewList.push(this.fileList[i].savePath) + } + // this.fileList[i].savePaths =[this.baseurl + res.savePath]; + + // 上传成功后,向外传递当前文件的完整信息(包含后端返回的res数据) + this.$emit('fileUploaded', { + fileId: res.id, // 文件ID + fileName: res.trueName, // 文件名 + filePath: res.savePath, // 文件相对路径(不含baseurl) + fullPath: this.fileList[i].savePath, // 完整路径(含baseurl) + fileType: this.getFileType(res.trueName), // 文件类型 + originalFile: file.file // 原始文件对象(可选) + }); + + } + + } + + + } else { + this.$message.error(res.message) + } + }).catch(() => { + this.uploadCnt-- + }) + }, + // 获取文件类型的方法(Affix组件中新增) + getFileType(fileName) { + if (!fileName) return ''; + const lastDotIndex = fileName.lastIndexOf('.'); + return lastDotIndex > -1 ? fileName.substring(lastDotIndex + 1).toLowerCase() : ''; + }, + handleBeforeUpload(file) { + + if (this.affixId == null || this.affixId === '') { + this.affixId = this.$tool.uuid() + this.$emit('input', this.affixId) + } + + let isLt20M = file.size / 1024 / 1024 < this.maxSize + if (!isLt20M) { + this.$message.error('上传大小不能超过 ' + this.maxSize + 'MB!') + } else { + this.fileList.push({ name: file.name, status: 1 }) + this.uploadCnt++ + } + + return isLt20M + }, + getFileName(id) { + for (let i = 0; i < this.fileList.length; i++) { + let item = this.fileList[i] + + if (item.id == id) { + console.log(item) + return item.name + } + } + }, + downloadFile(file) { + download(file.id).then((res) => { + console.log(res) + let fileName = this.getFileName(file.id).replace(/"/g, '') + var blob = new Blob([res], { type: 'application/octet-stream;' }) + var downloadElement = document.createElement('a') + var href = window.URL.createObjectURL(blob) // 创建下载的链接 + downloadElement.href = href + downloadElement.download = decodeURI(fileName) // 下载后文件名 + document.body.appendChild(downloadElement) + downloadElement.click() // 点击下载 + document.body.removeChild(downloadElement) // 下载完成移除元素 + window.URL.revokeObjectURL(href) // 释放掉blob对象 + }).catch((res) => { + this.$message.error(res.message) + }) + }, + downloadPack() { + + if (this.affixId === null || this.affixId === '') { + this.$message.error('暂无附件!') + return + } + //如果只有一个文件就不打包下载 + if (this.fileList.length == 1) { + + this.downloadFile(this.fileList[0]) + } else { + this.downloading = true + downloadAll(this.affixId).then((res) => { + var blob = new Blob([res], { type: 'application/octet-stream' }) + var downloadElement = document.createElement('a') + var href = window.URL.createObjectURL(blob) // 创建下载的链接 + downloadElement.href = href + downloadElement.download = decodeURI('download.zip') // 下载后文件名 + document.body.appendChild(downloadElement) + downloadElement.click() // 点击下载 + document.body.removeChild(downloadElement) // 下载完成移除元素 + window.URL.revokeObjectURL(href) // 释放掉blob对象 + this.downloading = false + }).catch(() => { + this.downloading = false + }) + } + + + }, + deleteFile(file) { + this.$confirm('请确认是否删除此文件?', '提示', { type: 'info' }).then(() => { + deleteAffix(file.id).then((res) => { + if (res.code == 200) { + this.fileList = this.fileList.filter(item => item.id !== file.id) + this.previewList = this.previewList.filter(item => item !== file.savePath)//删除图片 + if (this.fileList.length == 0) { + this.affixId = '' + } + + // 关键:触发自定义事件,传递被删除的文件名 + // 事件名建议:delete-file,参数:file.name(文件名) + this.$emit('delete-file', file.name); + } else { + this.$message.error(res.message) + } + }) + }) + }, + loadData() { + // 1. 请求锁:防止同一时刻重复请求(解决重复提交) + if (this.isQuerying) return; + // 2. 清空旧数据(双重保障解决数据残留) + this.fileList = []; + this.previewList = []; + + if (typeof this.value !== 'undefined' && this.value !== null && this.value !== '') { + this.isQuerying = true; // 加锁 + // 带重试机制的查询方法 + const queryWithRetry = () => { + queryAffixs(this.value).then((res) => { + this.isQuerying = false; // 解锁 + if (res.code == 200) { + if (res.data.length > 0) { + this.affixId = this.value; + // 重新构建数组,避免引用污染 + const newFileList = []; + const newPreviewList = []; + for (var i = 0; i < res.data.length; i++) { + const savePath = this.baseurl + res.data[i].savePath; + if (this.isImageURL(savePath)) { + newPreviewList.push(savePath); + } + newFileList.push({ + name: res.data[i].trueName, + id: res.data[i].id, + status: 2, + savePath: savePath + }); + } + // 直接替换数组,彻底解决残留 + this.fileList = newFileList; + this.previewList = newPreviewList; + } else { + this.affixId = ''; + } + } else { + // 捕获"重复提交"错误,重试1次 + if (res.msg.includes('请勿重复提交') && this.retryCount < 1) { + this.retryCount++; + this.isQuerying = true; + setTimeout(() => queryWithRetry(), 500); // 500ms后重试 + return; + } + this.$message.error(res.msg); + } + }).catch((err) => { + this.isQuerying = false; // 解锁 + // 捕获异常中的重复提交错误,重试1次 + if (err.message?.includes('请勿重复提交') && this.retryCount < 1) { + this.retryCount++; + setTimeout(() => queryWithRetry(), 500); + return; + } + this.$message.error(err.message || '查询附件失败'); + }); + }; + // 执行查询 + queryWithRetry(); + } else { + this.affixId = ''; + this.isQuerying = false; + } + }, + clearData() { + this.affixId = ''; + this.fileList = []; + this.previewList = []; + this.isQuerying = false; + this.retryCount = 0; + } + } } diff --git a/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/applicationForm.vue b/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/applicationForm.vue index 9c341ac..d3114c0 100644 --- a/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/applicationForm.vue +++ b/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/applicationForm.vue @@ -274,7 +274,7 @@ 保存 - + 提交申请 @@ -703,63 +703,52 @@ export default { submitForm(status) { this.$refs.formRef.validate((valid) => { if (valid) { - // 生成申请编号 + // 生成申请编号等逻辑不变 if (!this.form.applyNo) { const year = new Date().getFullYear(); const randomNo = Math.floor(Math.random() * 1000000).toString().padStart(6, '0'); - this.form.applyNo = `WS${year}${randomNo}`; // 获取申请编号 + this.form.applyNo = `WS${year}${randomNo}`; } - // 生成外宿结束时间(默认次年8月31日) this.form.endDate = this.getOutsideDefaultEndTime() - // 生成本人承诺内容 this.form.promiseContent = `

1.自觉遵守国家法律、法规;

2.自觉遵守学生行为规范和学校的规章制度,遵守社会公德;

3.自觉遵守外宿住址所在社区的有关管理规定;

4.本人申请外宿,属个人自愿行为,外宿期间发生的一切事故,造成本人、他人或集体的人身、财产损害的,学校不负责任。

` - // 将地址数组转为字符串(用 / 拼接,后端可按此分隔解析) const submitForm = { ...this.form, - address: this.form.address ? this.form.address.join('/') : '', // 数组转字符串 - parentAddress: this.form.parentAddress ? this.form.parentAddress.join('/') : '', // 家长地址同理 - status: status + address: this.form.address ? this.form.address.join('/') : '', + parentAddress: this.form.parentAddress ? this.form.parentAddress.join('/') : '', + status: this.form.status != 0 ? this.form.status : status }; this.loading = true; - if (this.form.id != null) { - updateOutsideAccommodationApply(submitForm).then((response) => { - // 批量新增材料附件 - if (this.reasonFileList && this.reasonFileList.length > 0) { - batchAddOutsideAccommodationAttachment(this.reasonFileList) - this.resetForm() - } - this.loading = false; - this.$modal.msgSuccess('修改成功') - this.goBack() - }).catch(error => { - this.loading = false; - this.goBack() - }) - } else { - addOutsideAccommodationApply(submitForm).then((response) => { - // 填写附件里面的申请编号 + // 封装请求逻辑为Promise,确保完成后再操作 + const requestPromise = this.form.id != null + ? updateOutsideAccommodationApply(submitForm) + : addOutsideAccommodationApply(submitForm); + + requestPromise.then((response) => { + // 附件处理逻辑不变 + if (this.reasonFileList && this.reasonFileList.length > 0) { this.reasonFileList.forEach(element => { - element.applyId = response.data.id - }) - // 批量新增材料附件 - if (this.reasonFileList && this.reasonFileList.length > 0) { - batchAddOutsideAccommodationAttachment(this.reasonFileList) - this.resetForm() - } - this.loading = false; - this.$modal.msgSuccess('新增成功') - this.goBack() - }).catch(error => { - this.loading = false; - this.goBack() - }) - } + element.applyId = this.form.id || response.data.id; + }); + batchAddOutsideAccommodationAttachment(this.reasonFileList); + } + this.loading = false; + this.$modal.msgSuccess(this.form.id ? '修改成功' : '新增成功'); + // 延迟跳转:给后端数据落地留时间(关键!) + setTimeout(() => { + this.goBack(); + }, 800); // 800ms延迟,确保后端写入完成 + }).catch(error => { + this.loading = false; + this.$message.error('提交失败:' + (error.msg || '服务器处理异常')); + // 失败时不跳转,避免用户重复操作 + // this.goBack(); + }) } else { this.loading = false; this.$message.error('表单填写有误,请检查!') @@ -1143,4 +1132,4 @@ li { text-align: center !important; width: 120px; } - \ No newline at end of file + diff --git a/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/components/detailApply.vue b/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/components/detailApply.vue index 7f52143..1599d4e 100644 --- a/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/components/detailApply.vue +++ b/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/components/detailApply.vue @@ -44,8 +44,8 @@ - - 已交 绑定当前学年年度住宿费 人民币 + {{ renderData.accommodationFee }} + diff --git a/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/index.vue b/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/index.vue index f4d4b74..7089f3f 100644 --- a/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/index.vue +++ b/src/views/dormitory/outsideAccommodation/outsideAccommodationApply/index.vue @@ -150,7 +150,7 @@