zip批量导出
This commit is contained in:
parent
cc24edb8a9
commit
d7f7b85783
|
|
@ -228,49 +228,27 @@ export function downloadJson(url, params, filename, config) {
|
|||
|
||||
// 通用下载方法
|
||||
export function downloadZip(url, params, filename, config) {
|
||||
const backgroundMode = config && config.background === true
|
||||
const showLoading = !(config && config.showLoading === false) && !backgroundMode
|
||||
const finalAxiosConfig = {
|
||||
transformRequest: [
|
||||
(params) => {
|
||||
return params
|
||||
},
|
||||
],
|
||||
headers: { 'Content-Type': 'application/json', encryptResponse: false },
|
||||
responseType: 'blob',
|
||||
// if background mode and no explicit timeout, extend to 10 minutes
|
||||
timeout: backgroundMode && !(config && typeof config.timeout === 'number') ? 600000 : undefined,
|
||||
...config,
|
||||
}
|
||||
|
||||
if (showLoading) {
|
||||
downloadLoadingInstance = Loading.service({
|
||||
text: '正在下载数据,请稍候',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
} else if (backgroundMode) {
|
||||
Notification.info({
|
||||
title: '已开始生成',
|
||||
message: '批量导出任务已在后台执行,准备就绪后将自动下载。',
|
||||
duration: 4000,
|
||||
})
|
||||
}
|
||||
|
||||
downloadLoadingInstance = Loading.service({
|
||||
text: '正在下载数据,请稍候',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
return service
|
||||
.post(url, params, finalAxiosConfig)
|
||||
.post(url, params, {
|
||||
transformRequest: [
|
||||
(params) => {
|
||||
return params
|
||||
},
|
||||
],
|
||||
headers: { 'Content-Type': 'application/json',encryptResponse: false },
|
||||
responseType: 'blob',
|
||||
...config,
|
||||
})
|
||||
.then(async (data) => {
|
||||
const isBlob = blobValidate(data)
|
||||
if (isBlob) {
|
||||
const blob = new Blob([data])
|
||||
saveAs(blob, filename)
|
||||
if (backgroundMode) {
|
||||
Notification.success({
|
||||
title: '导出完成',
|
||||
message: 'ZIP 文件已准备好,正在开始下载。',
|
||||
duration: 3000,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const resText = await data.text()
|
||||
const rspObj = JSON.parse(resText)
|
||||
|
|
@ -278,20 +256,12 @@ export function downloadZip(url, params, filename, config) {
|
|||
errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||
Message.error(errMsg)
|
||||
}
|
||||
if (showLoading && downloadLoadingInstance) {
|
||||
downloadLoadingInstance.close()
|
||||
}
|
||||
downloadLoadingInstance.close()
|
||||
})
|
||||
.catch((r) => {
|
||||
console.error(r)
|
||||
if (r && r.message && r.message.includes('timeout')) {
|
||||
Message.error('导出超时,请减少选择数量或稍后重试')
|
||||
} else {
|
||||
Message.error('下载文件出现错误,请联系管理员!')
|
||||
}
|
||||
if (showLoading && downloadLoadingInstance) {
|
||||
downloadLoadingInstance.close()
|
||||
}
|
||||
Message.error('下载文件出现错误,请联系管理员!')
|
||||
downloadLoadingInstance.close()
|
||||
})
|
||||
}
|
||||
export default service
|
||||
|
|
|
|||
|
|
@ -37,8 +37,15 @@
|
|||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery" >查询</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery" >重置</el-button>
|
||||
<el-button type="success" icon="el-icon-download" size="mini" @click="exportExcel" :disabled="tableList.length === 0">导出Excel</el-button>
|
||||
<el-button type="success" icon="el-icon-download" size="mini" @click="exportZip" :disabled="tableList.length === 0">批量导出ZIP</el-button>
|
||||
<el-button type="info" icon="el-icon-document-copy" size="mini" @click="showHistoryReportDialog">查看历史报表</el-button>
|
||||
<el-button type="success" icon="el-icon-download" size="mini" @click="exportZip" :disabled="exporting">{{ exporting ? '导出中...' : '批量导出ZIP' }}</el-button>
|
||||
<div v-if="exporting" class="progress-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
|
||||
</div>
|
||||
<div class="progress-text">{{ progress }}% ({{ current }}/{{ totalZip }})</div>
|
||||
<div class="status-text">{{ statusText }}</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
|
@ -568,6 +575,9 @@ import vueEasyPrint from "vue-easy-print";
|
|||
import Treeselect from "@riophae/vue-treeselect";
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
import * as XLSX from 'xlsx';
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'UnreportHome',
|
||||
dicts: ['cost_status'],
|
||||
|
|
@ -664,7 +674,18 @@ export default {
|
|||
proStatus:[
|
||||
{ id: 0, name: '未竣工' },
|
||||
{ id: 1, name: '已竣工' },
|
||||
]
|
||||
],
|
||||
|
||||
|
||||
//导出zip相关参数
|
||||
exporting: false,
|
||||
exportFlag: false,
|
||||
progress: 0,
|
||||
current: 0,
|
||||
totalZip: 0,
|
||||
statusText: '',
|
||||
taskId: null,
|
||||
pollInterval: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
|
@ -815,8 +836,32 @@ export default {
|
|||
this.aform = { status: '2' };
|
||||
},
|
||||
|
||||
|
||||
//********************************************************************** */
|
||||
//批量导出zip
|
||||
exportZip() {
|
||||
// exportZip() {
|
||||
// if (!this.ids.length) {
|
||||
// this.$message.error('请选择要导出的记录')
|
||||
// return false
|
||||
// }
|
||||
// let param = []
|
||||
// this.ids.map(item => {
|
||||
// param.push({ agreementId: item.agreementId,settlementType:item.settlementType })
|
||||
// })
|
||||
// // exportLeaseAll(this.exportParams).then(res => {
|
||||
// // downloadFile({ fileName: `月结明细_${new Date().getTime()}.zip`, fileData: res, fileType: 'application/zip;charset=utf-8' })
|
||||
// // })
|
||||
// this.downloadZip(
|
||||
// 'material/slt_agreement_info/exportUnsettled',
|
||||
// JSON.stringify(param),
|
||||
// `未结算批量明细导出_${new Date().getTime()}.zip`,
|
||||
// { background: true, showLoading: false, timeout: 600000 }
|
||||
// )
|
||||
|
||||
// this.$refs.tableRef.clearSelection()
|
||||
// },
|
||||
|
||||
async exportZip() {
|
||||
if (!this.ids.length) {
|
||||
this.$message.error('请选择要导出的记录')
|
||||
return false
|
||||
|
|
@ -825,19 +870,92 @@ export default {
|
|||
this.ids.map(item => {
|
||||
param.push({ agreementId: item.agreementId,settlementType:item.settlementType })
|
||||
})
|
||||
// exportLeaseAll(this.exportParams).then(res => {
|
||||
// downloadFile({ fileName: `月结明细_${new Date().getTime()}.zip`, fileData: res, fileType: 'application/zip;charset=utf-8' })
|
||||
// })
|
||||
this.downloadZip(
|
||||
'material/slt_agreement_info/exportUnsettled',
|
||||
JSON.stringify(param),
|
||||
`未结算批量明细导出_${new Date().getTime()}.zip`,
|
||||
{ background: true, showLoading: false, timeout: 600000 }
|
||||
)
|
||||
|
||||
try {
|
||||
this.exporting = true
|
||||
this.progress = 0
|
||||
this.current = 0
|
||||
this.totalZip = 0
|
||||
this.statusText = '开始导出...'
|
||||
|
||||
|
||||
const response = await request({
|
||||
url: '/material/slt_agreement_info/exportUnsettled',
|
||||
method: 'post',
|
||||
data: JSON.stringify(param),
|
||||
timeout: 60000 // 设置10分钟超时
|
||||
})
|
||||
const result = response
|
||||
this.taskId = result.taskId
|
||||
this.totalZip = result.total
|
||||
this.statusText = '导出任务已开始...'
|
||||
this.exportFlag = true
|
||||
// 开始轮询进度
|
||||
this.startPolling()
|
||||
} catch (error) {
|
||||
console.error('导出请求失败', error)
|
||||
this.statusText = '导出请求失败'
|
||||
this.exporting = false
|
||||
}
|
||||
this.$refs.tableRef.clearSelection()
|
||||
},
|
||||
|
||||
startPolling() {
|
||||
this.pollInterval = setInterval(async () => {
|
||||
try {
|
||||
const response = await request({
|
||||
url: `/material/slt_agreement_info/exportProgress/${this.taskId}`,
|
||||
method: 'get'
|
||||
})
|
||||
const progress = response
|
||||
|
||||
this.progress = progress.percentage
|
||||
this.current = progress.current
|
||||
|
||||
if (progress.status === 'completed') {
|
||||
this.statusText = '导出完成,开始下载...'
|
||||
clearInterval(this.pollInterval)
|
||||
this.downloadFile()
|
||||
this.exporting = false
|
||||
this.exportFlag = false
|
||||
} else if (progress.status === 'error') {
|
||||
this.statusText = `导出失败: ${progress.errorMsg}`
|
||||
clearInterval(this.pollInterval)
|
||||
this.exporting = false
|
||||
} else {
|
||||
this.statusText = `处理中: ${progress.percentage}%`
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('查询进度失败', error)
|
||||
this.statusText = '查询进度失败'
|
||||
this.exportFlag = false
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
|
||||
downloadFile() {
|
||||
if (this.taskId) {
|
||||
console.log("xxxxxxxxxxtaskId",this.taskId)
|
||||
const param = { taskId: this.taskId }
|
||||
this.downloadZip(
|
||||
'material/slt_agreement_info/downloadExport',
|
||||
JSON.stringify(param),
|
||||
`未结算批量明细导出_${new Date().getTime()}.zip`,
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
if (this.pollInterval) {
|
||||
clearInterval(this.pollInterval)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
//********************************************************************** */
|
||||
|
||||
|
||||
openPrintDialog(row){
|
||||
this.openPrint = true
|
||||
|
|
@ -1593,4 +1711,129 @@ export default {
|
|||
text-align: center;
|
||||
border-left: 1px solid #9c9c9c;
|
||||
}
|
||||
|
||||
//导出zip
|
||||
.progress-container {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background-color: #4CAF50;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
margin-top: 8px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
//
|
||||
|
||||
</style>
|
||||
|
||||
<!-- 为了让CSS生效,需要在组件的style部分添加以下样式 -->
|
||||
<style scoped>
|
||||
/* 优化的进度条样式 */
|
||||
.progress-container.improved {
|
||||
background: #f5f7fa;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
margin-top: 12px;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 10px;
|
||||
background-color: #e4e7ed;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #409eff 0%, #67c23a 100%);
|
||||
border-radius: 5px;
|
||||
transition: width 0.3s ease;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.progress-indicator {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 13px;
|
||||
color: #409eff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 添加一些额外的动画效果 */
|
||||
.progress-fill::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
left: 200%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -250,6 +250,8 @@ export default {
|
|||
keyWord: undefined,
|
||||
proId: undefined,
|
||||
jijuType: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined
|
||||
},
|
||||
deviceRecordList: [], // 二级列表数据
|
||||
dialogDeviceTotal: 0, // 二级总条数
|
||||
|
|
@ -353,6 +355,8 @@ export default {
|
|||
this.dialogDeviceQuery.keyWord = ''
|
||||
this.dialogDeviceQuery.pageNum = 1
|
||||
this.dialogDeviceQuery.pageSize = 10
|
||||
this.dialogDeviceQuery.startTime = this.queryParams.startTime
|
||||
this.dialogDeviceQuery.endTime = this.queryParams.endTime
|
||||
this.deviceRecordList = []
|
||||
this.getDeviceRecords()
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue