未结算报表zip导出
This commit is contained in:
parent
61c40efe2b
commit
0d14a3d349
|
|
@ -38,17 +38,52 @@
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery" >重置</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="exportExcel" :disabled="tableList.length === 0">导出Excel</el-button>
|
||||||
<el-button type="info" icon="el-icon-document-copy" size="mini" @click="showHistoryReportDialog">查看历史报表</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>
|
<el-button
|
||||||
<div v-if="exporting" class="progress-container">
|
type="success"
|
||||||
<div class="progress-bar">
|
icon="el-icon-download"
|
||||||
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
|
size="mini"
|
||||||
</div>
|
@click="exportZip"
|
||||||
<div class="progress-text">{{ progress }}% ({{ current }}/{{ totalZip }})</div>
|
:disabled="!canDownload"
|
||||||
<div class="status-text">{{ statusText }}</div>
|
:loading="isExporting">
|
||||||
</div>
|
{{ exportButtonText }}
|
||||||
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 进度条显示区域 -->
|
||||||
|
<div v-if="showProgress" class="progress-container">
|
||||||
|
<el-progress
|
||||||
|
:percentage="progressPercentage"
|
||||||
|
:status="progressStatus"
|
||||||
|
:stroke-width="18"
|
||||||
|
:text-inside="true"
|
||||||
|
:show-text="true">
|
||||||
|
</el-progress>
|
||||||
|
<div class="progress-info">
|
||||||
|
<span class="progress-text">{{ progressText }}</span>
|
||||||
|
<span class="progress-detail">{{ currentItem }}/{{ totalItems }} {{ currentFileName }}</span>
|
||||||
|
<span class="progress-speed" v-if="downloadSpeed > 0">
|
||||||
|
速度: {{ formatBytes(downloadSpeed) }}/s
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="progress-actions" v-if="showProgress">
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
@click="cancelDownload"
|
||||||
|
:disabled="!isExporting"
|
||||||
|
v-if="!downloadComplete">
|
||||||
|
取消导出
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="success"
|
||||||
|
@click="clearProgress"
|
||||||
|
v-if="downloadComplete">
|
||||||
|
完成
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-table v-loading="loading" ref="tableRef" :data="tableList" @selection-change="handleSelectionChange" :max-height="650">
|
<el-table v-loading="loading" ref="tableRef" :data="tableList" @selection-change="handleSelectionChange" :max-height="650">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
type="selection"
|
type="selection"
|
||||||
|
|
@ -680,16 +715,43 @@ export default {
|
||||||
|
|
||||||
|
|
||||||
//导出zip相关参数
|
//导出zip相关参数
|
||||||
exporting: false,
|
isExporting: false,
|
||||||
exportFlag: false,
|
showProgress: false,
|
||||||
progress: 0,
|
downloadComplete: false,
|
||||||
current: 0,
|
progressPercentage: 0,
|
||||||
totalZip: 0,
|
currentItem: 0,
|
||||||
statusText: '',
|
totalItems: 0,
|
||||||
taskId: null,
|
currentFileName: '',
|
||||||
pollInterval: null
|
progressText: '准备导出...',
|
||||||
|
progressStatus: '',
|
||||||
|
downloadSpeed: 0,
|
||||||
|
downloadController: null,
|
||||||
|
retryCount: 0,
|
||||||
|
maxRetries: 3,
|
||||||
|
downloadStartTime: 0,
|
||||||
|
downloadedBytes: 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
/** 是否可以下载 */
|
||||||
|
canDownload() {
|
||||||
|
const can = this.ids.length > 0 && !this.isExporting;
|
||||||
|
console.log('canDownload检查:', {
|
||||||
|
ids: this.ids.length,
|
||||||
|
isExporting: this.isExporting,
|
||||||
|
can: can
|
||||||
|
});
|
||||||
|
return can;
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 下载按钮文字 */
|
||||||
|
exportButtonText() {
|
||||||
|
if (this.isExporting) {
|
||||||
|
return `导出中 ${this.progressPercentage}%`;
|
||||||
|
}
|
||||||
|
return `批量导出ZIP${this.ids.length > 0 ? ` (${this.ids.length})` : ''}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
this.GetUnitData()
|
this.GetUnitData()
|
||||||
this.GetProData()
|
this.GetProData()
|
||||||
|
|
@ -841,113 +903,221 @@ export default {
|
||||||
|
|
||||||
//********************************************************************** */
|
//********************************************************************** */
|
||||||
//批量导出zip
|
//批量导出zip
|
||||||
// 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() {
|
async exportZip() {
|
||||||
if (!this.ids.length) {
|
if (!this.ids.length) {
|
||||||
this.$message.error('请选择要导出的记录')
|
this.$message.error('请选择要导出的记录')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let param = []
|
|
||||||
this.ids.map(item => {
|
|
||||||
param.push({ agreementId: item.agreementId,settlementType:item.settlementType })
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// 确认对话框
|
||||||
try {
|
try {
|
||||||
this.exporting = true
|
await this.$confirm(`确定要导出 ${this.ids.length} 个选中项吗?`, '提示', {
|
||||||
this.progress = 0
|
confirmButtonText: '确定',
|
||||||
this.current = 0
|
cancelButtonText: '取消',
|
||||||
this.totalZip = 0
|
type: 'warning',
|
||||||
this.statusText = '开始导出...'
|
center: true
|
||||||
|
});
|
||||||
|
} catch (cancel) {
|
||||||
const response = await getExportZipUnsettled(param)
|
console.log('用户取消导出');
|
||||||
const result = response
|
return;
|
||||||
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 () => {
|
this.initializeDownload();
|
||||||
try {
|
try {
|
||||||
const param = { taskId: this.taskId }
|
let param = []
|
||||||
const response = await getExportZipProgress(param)
|
this.ids.map(item => {
|
||||||
const progress = response
|
param.push({ agreementId: item.agreementId,settlementType:item.settlementType })
|
||||||
this.progress = progress.percentage
|
})
|
||||||
this.current = progress.current
|
this.totalItems = param.length;
|
||||||
|
|
||||||
if (progress.status === 'completed') {
|
const taskId = crypto.randomUUID()
|
||||||
this.statusText = '导出完成,开始下载...'
|
const payload = {
|
||||||
clearInterval(this.pollInterval)
|
list: param,
|
||||||
this.downloadFile()
|
taskId:taskId,
|
||||||
this.exporting = false
|
zipName: `未结算批量明细导出_${new Date().getTime()}`,
|
||||||
this.exportFlag = false
|
stream: true
|
||||||
} else if (progress.status === 'error') {
|
};
|
||||||
this.statusText = `导出失败: ${progress.errorMsg}`
|
|
||||||
clearInterval(this.pollInterval)
|
// 开始下载
|
||||||
this.exporting = false
|
await this.streamDownload(payload);
|
||||||
} else {
|
this.$refs.tableRef.clearSelection()
|
||||||
this.statusText = `处理中: ${progress.percentage}%`
|
} catch (err) {
|
||||||
}
|
console.error('未结算批量明细导出失败', err);
|
||||||
} catch (error) {
|
this.handleDownloadError(err);
|
||||||
console.error('查询进度失败', error)
|
|
||||||
this.statusText = '查询进度失败'
|
|
||||||
this.exportFlag = false
|
|
||||||
}
|
|
||||||
}, 5000)
|
|
||||||
},
|
|
||||||
|
|
||||||
downloadFile() {
|
|
||||||
if (this.taskId) {
|
|
||||||
console.log("xxxxxxxxxxtaskId",this.taskId)
|
|
||||||
const param = { taskId: this.taskId }
|
|
||||||
this.downloadZip(
|
|
||||||
'material/slt_agreement_info/dlExProgress',
|
|
||||||
JSON.stringify(param),
|
|
||||||
`未结算批量明细导出_${new Date().getTime()}.zip`,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeUnmount() {
|
/** 初始化下载状态 */
|
||||||
if (this.pollInterval) {
|
initializeDownload() {
|
||||||
clearInterval(this.pollInterval)
|
this.isExporting = true;
|
||||||
|
this.showProgress = true;
|
||||||
|
this.downloadComplete = false;
|
||||||
|
this.progressPercentage = 0;
|
||||||
|
this.currentItem = 0;
|
||||||
|
this.totalItems = 0;
|
||||||
|
this.currentFileName = '';
|
||||||
|
this.progressText = '准备导出...';
|
||||||
|
this.progressStatus = null;
|
||||||
|
this.downloadSpeed = 0;
|
||||||
|
this.retryCount = 0;
|
||||||
|
this.downloadedBytes = 0;
|
||||||
|
this.downloadStartTime = Date.now();
|
||||||
|
this.downloadController = new AbortController();
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 流式下载方法 - 使用原始的request方式 */
|
||||||
|
async streamDownload(payload) {
|
||||||
|
try {
|
||||||
|
this.progressText = '正在连接服务器...';
|
||||||
|
|
||||||
|
// 使用原始的request方法
|
||||||
|
const response = await request({
|
||||||
|
url: '/material/slt_agreement_info/dlExProgress',
|
||||||
|
method: 'POST',
|
||||||
|
data: payload,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 0,
|
||||||
|
onDownloadProgress: (progressEvent) => {
|
||||||
|
this.handleDownloadProgress(progressEvent);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建下载链接
|
||||||
|
const blob = new Blob([response]);
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.style.display = 'none';
|
||||||
|
a.href = url;
|
||||||
|
a.download = `${payload.zipName}.zip`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
document.body.removeChild(a);
|
||||||
|
|
||||||
|
// 完成状态
|
||||||
|
this.handleDownloadComplete();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message && error.message.includes('canceled')) {
|
||||||
|
this.progressText = '导出已取消';
|
||||||
|
this.progressPercentage = 0;
|
||||||
|
this.progressStatus = 'exception';
|
||||||
|
} else {
|
||||||
|
console.error('导出失败:', error);
|
||||||
|
this.handleDownloadError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 处理下载进度 */
|
||||||
|
handleDownloadProgress(progressEvent) {
|
||||||
|
if (progressEvent.total) {
|
||||||
|
// ZIP 文件流阶段
|
||||||
|
const percentage = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||||
|
this.progressPercentage = Math.min(percentage, 99);
|
||||||
|
|
||||||
|
const elapsed = Date.now() - this.downloadStartTime;
|
||||||
|
if (elapsed > 0) {
|
||||||
|
this.downloadSpeed = progressEvent.loaded * 1000 / elapsed;
|
||||||
|
this.progressText = `${this.formatBytes(progressEvent.loaded)} / ${this.formatBytes(progressEvent.total)}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 后端处理文件阶段(无进度)
|
||||||
|
|
||||||
|
// ⭐⭐ 1. 下载中保证 currentItem 不超过总数
|
||||||
|
if (this.totalItems > 0) {
|
||||||
|
this.currentItem = Math.min(this.currentItem + 1, this.totalItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⭐ 2. 百分比始终不超过 100
|
||||||
|
this.progressPercentage = Math.min(
|
||||||
|
Math.round((this.currentItem / this.totalItems) * 100),
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
this.progressText = `处理中:${this.currentItem}/${this.totalItems}`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** 处理下载完成 */
|
||||||
|
handleDownloadComplete() {
|
||||||
|
// ⭐⭐ 2. 下载完成时补齐
|
||||||
|
this.currentItem = this.totalItems;
|
||||||
|
this.progressPercentage = 100;
|
||||||
|
|
||||||
|
this.progressText = '导出完成';
|
||||||
|
this.progressStatus = 'success';
|
||||||
|
this.downloadComplete = true;
|
||||||
|
this.isExporting = false;
|
||||||
|
this.downloadSpeed = 0;
|
||||||
|
|
||||||
|
this.$message({
|
||||||
|
message: '导出完成!',
|
||||||
|
type: 'success',
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/** 处理下载错误 */
|
||||||
|
handleDownloadError(error) {
|
||||||
|
console.error('导出失败:', error);
|
||||||
|
|
||||||
|
if (this.retryCount < this.maxRetries) {
|
||||||
|
this.retryCount++;
|
||||||
|
this.progressText = `导出失败,正在重试 (${this.retryCount}/${this.maxRetries})...`;
|
||||||
|
this.progressStatus = 'warning';
|
||||||
|
|
||||||
|
// 3秒后重试
|
||||||
|
setTimeout(() => {
|
||||||
|
this.exportZip();
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
this.progressText = '导出失败,请重试';
|
||||||
|
this.progressStatus = 'exception';
|
||||||
|
this.isExporting = false;
|
||||||
|
this.$message.error('导出失败: ' + (error.message || '未知错误'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 取消下载 */
|
||||||
|
cancelDownload() {
|
||||||
|
if (this.downloadController) {
|
||||||
|
this.downloadController.abort();
|
||||||
|
}
|
||||||
|
this.isExporting = false;
|
||||||
|
this.showProgress = false;
|
||||||
|
this.$message.info('导出已取消');
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 清除进度显示 */
|
||||||
|
clearProgress() {
|
||||||
|
this.showProgress = false;
|
||||||
|
this.downloadComplete = false;
|
||||||
|
this.progressPercentage = 0;
|
||||||
|
this.currentItem = 0;
|
||||||
|
this.totalItems = 0;
|
||||||
|
this.downloadSpeed = 0;
|
||||||
|
this.progressStatus = null;
|
||||||
|
this.currentFileName = '';
|
||||||
|
},
|
||||||
|
/** 格式化字节大小 */
|
||||||
|
formatBytes(bytes, decimals = 2) {
|
||||||
|
if (bytes === 0) return '0 Bytes';
|
||||||
|
const k = 1024;
|
||||||
|
const dm = decimals < 0 ? 0 : decimals;
|
||||||
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||||
|
},
|
||||||
|
|
||||||
//********************************************************************** */
|
//********************************************************************** */
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1707,41 +1877,97 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
//导出zip
|
//导出zip
|
||||||
|
/* 进度条容器样式 */
|
||||||
.progress-container {
|
.progress-container {
|
||||||
margin-top: 20px;
|
margin: 20px 0;
|
||||||
padding: 15px;
|
padding: 20px;
|
||||||
border: 1px solid #e0e0e0;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar {
|
@keyframes fadeIn {
|
||||||
width: 100%;
|
from {
|
||||||
height: 20px;
|
opacity: 0;
|
||||||
background-color: #f0f0f0;
|
transform: translateY(-10px);
|
||||||
border-radius: 10px;
|
}
|
||||||
overflow: hidden;
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-fill {
|
.progress-info {
|
||||||
height: 100%;
|
margin-top: 15px;
|
||||||
background-color: #4CAF50;
|
display: flex;
|
||||||
transition: width 0.3s ease;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-text {
|
.progress-text {
|
||||||
margin-top: 8px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-detail {
|
||||||
|
font-size: 12px;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-text {
|
.progress-speed {
|
||||||
margin-top: 5px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #999;
|
color: #409eff;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-actions {
|
||||||
|
margin-top: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Element UI Progress 自定义样式 */
|
||||||
|
::v-deep .el-progress-bar__inner {
|
||||||
|
background: linear-gradient(90deg, #409eff 0%, #67c23a 100%);
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-progress-bar__innerText {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式调整 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.progress-container {
|
||||||
|
padding: 15px;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-detail,
|
||||||
|
.progress-speed {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button--mini {
|
||||||
|
min-width: 80px;
|
||||||
|
padding: 5px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
button:disabled {
|
button:disabled {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
|
@ -1749,85 +1975,3 @@ button:disabled {
|
||||||
//
|
//
|
||||||
|
|
||||||
</style>
|
</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>
|
|
||||||
Loading…
Reference in New Issue