|
@@ -17,15 +17,58 @@
|
|
|
v-for="(progress, index) in progressList"
|
|
|
:key="index"
|
|
|
class="progress-item"
|
|
|
+ :class="{ 'completed': progress.value >= 100, 'error': progress.status === '错误' }"
|
|
|
>
|
|
|
+ <!-- 主进度项 -->
|
|
|
<div class="progress-info">
|
|
|
- <span class="progress-title">{{ progress.title }}</span>
|
|
|
- <span class="progress-percentage">{{ progress.value }}%</span>
|
|
|
+ <div class="progress-title-wrapper">
|
|
|
+ <span class="progress-icon">{{ getProgressIcon(progress) }}</span>
|
|
|
+ <span class="progress-title">{{ progress.title }}</span>
|
|
|
+ </div>
|
|
|
+ <span class="progress-percentage" :class="getProgressClass(progress)">{{ progress.value }}%</span>
|
|
|
</div>
|
|
|
<div class="progress-bar-container">
|
|
|
- <div class="progress-bar" :style="{ width: progress.value + '%' }"></div>
|
|
|
+ <div class="progress-bar" :style="{ width: progress.value + '%' }" :class="getProgressBarClass(progress)"></div>
|
|
|
</div>
|
|
|
<div class="progress-status">{{ progress.status }}</div>
|
|
|
+
|
|
|
+ <!-- 子进度项 -->
|
|
|
+ <div v-if="progress.subTasks && progress.subTasks.length > 0" class="sub-progress-list">
|
|
|
+ <div
|
|
|
+ v-for="(subTask, subIndex) in progress.subTasks"
|
|
|
+ :key="subIndex"
|
|
|
+ class="sub-progress-item"
|
|
|
+ :class="{ 'completed': subTask.value >= 100, 'active': subTask.isActive }"
|
|
|
+ >
|
|
|
+ <div class="sub-progress-info">
|
|
|
+ <span class="sub-progress-icon">{{ getSubProgressIcon(subTask) }}</span>
|
|
|
+ <span class="sub-progress-title">{{ subTask.title }}</span>
|
|
|
+ <span class="sub-progress-percentage">{{ subTask.value }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="sub-progress-bar-container">
|
|
|
+ <div class="sub-progress-bar" :style="{ width: subTask.value + '%' }"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 子子进度项 -->
|
|
|
+ <div v-if="subTask.subSubTasks && subTask.subSubTasks.length > 0" class="sub-sub-progress-list">
|
|
|
+ <div
|
|
|
+ v-for="(subSubTask, subSubIndex) in subTask.subSubTasks"
|
|
|
+ :key="subSubIndex"
|
|
|
+ class="sub-sub-progress-item"
|
|
|
+ :class="{ 'completed': subSubTask.value >= 100, 'active': subSubTask.isActive }"
|
|
|
+ >
|
|
|
+ <div class="sub-sub-progress-info">
|
|
|
+ <span class="sub-sub-progress-icon">{{ getSubSubProgressIcon(subSubTask) }}</span>
|
|
|
+ <span class="sub-sub-progress-title">{{ subSubTask.title }}</span>
|
|
|
+ <span class="sub-sub-progress-percentage">{{ subSubTask.value }}%</span>
|
|
|
+ </div>
|
|
|
+ <div class="sub-sub-progress-bar-container">
|
|
|
+ <div class="sub-sub-progress-bar" :style="{ width: subSubTask.value + '%' }"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -1449,30 +1492,295 @@ const handleStepClick = (step) => {
|
|
|
// 显示进度条弹窗
|
|
|
showProgressModal.value = true;
|
|
|
|
|
|
- // 创建新的进度项
|
|
|
- const newProgress = {
|
|
|
- id: Date.now(),
|
|
|
- title: step.description || '处理步骤',
|
|
|
- value: 0,
|
|
|
- status: '进行中...'
|
|
|
- };
|
|
|
+ // 创建模拟任务数据
|
|
|
+ const mockTasks = [
|
|
|
+ {
|
|
|
+ id: Date.now(),
|
|
|
+ title: '数据分析任务',
|
|
|
+ value: 0,
|
|
|
+ status: '准备中...',
|
|
|
+ subTasks: [
|
|
|
+ {
|
|
|
+ title: '数据收集',
|
|
|
+ value: 0,
|
|
|
+ isActive: true,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '连接数据源', value: 0, isActive: true },
|
|
|
+ { title: '验证数据格式', value: 0, isActive: false },
|
|
|
+ { title: '下载数据文件', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '数据清洗',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '检测异常值', value: 0, isActive: false },
|
|
|
+ { title: '处理缺失值', value: 0, isActive: false },
|
|
|
+ { title: '数据标准化', value: 0, isActive: false },
|
|
|
+ { title: '去重处理', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '特征提取',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '特征选择', value: 0, isActive: false },
|
|
|
+ { title: '特征工程', value: 0, isActive: false },
|
|
|
+ { title: '降维处理', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '模型训练',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '参数初始化', value: 0, isActive: false },
|
|
|
+ { title: '训练迭代', value: 0, isActive: false },
|
|
|
+ { title: '模型验证', value: 0, isActive: false },
|
|
|
+ { title: '超参数调优', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '结果验证',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '交叉验证', value: 0, isActive: false },
|
|
|
+ { title: '性能评估', value: 0, isActive: false },
|
|
|
+ { title: '结果输出', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: Date.now() + 1,
|
|
|
+ title: '报告生成任务',
|
|
|
+ value: 0,
|
|
|
+ status: '等待中...',
|
|
|
+ subTasks: [
|
|
|
+ {
|
|
|
+ title: '模板加载',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '读取模板文件', value: 0, isActive: false },
|
|
|
+ { title: '解析模板结构', value: 0, isActive: false },
|
|
|
+ { title: '初始化变量', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '数据填充',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '获取分析结果', value: 0, isActive: false },
|
|
|
+ { title: '格式化数据', value: 0, isActive: false },
|
|
|
+ { title: '填充模板', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '图表生成',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '创建统计图表', value: 0, isActive: false },
|
|
|
+ { title: '生成趋势图', value: 0, isActive: false },
|
|
|
+ { title: '制作对比图', value: 0, isActive: false },
|
|
|
+ { title: '优化图表样式', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '格式化处理',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '调整页面布局', value: 0, isActive: false },
|
|
|
+ { title: '设置字体样式', value: 0, isActive: false },
|
|
|
+ { title: '添加页眉页脚', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '文件导出',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '生成PDF文件', value: 0, isActive: false },
|
|
|
+ { title: '创建Excel文件', value: 0, isActive: false },
|
|
|
+ { title: '压缩文件包', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: Date.now() + 2,
|
|
|
+ title: '系统优化任务',
|
|
|
+ value: 0,
|
|
|
+ status: '等待中...',
|
|
|
+ subTasks: [
|
|
|
+ {
|
|
|
+ title: '性能检测',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: 'CPU使用率检测', value: 0, isActive: false },
|
|
|
+ { title: '内存占用分析', value: 0, isActive: false },
|
|
|
+ { title: '磁盘IO监控', value: 0, isActive: false },
|
|
|
+ { title: '网络延迟测试', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '缓存清理',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '清理临时文件', value: 0, isActive: false },
|
|
|
+ { title: '清空浏览器缓存', value: 0, isActive: false },
|
|
|
+ { title: '清理系统缓存', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '数据库优化',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '索引重建', value: 0, isActive: false },
|
|
|
+ { title: '查询优化', value: 0, isActive: false },
|
|
|
+ { title: '数据压缩', value: 0, isActive: false },
|
|
|
+ { title: '备份清理', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '内存整理',
|
|
|
+ value: 0,
|
|
|
+ isActive: false,
|
|
|
+ subSubTasks: [
|
|
|
+ { title: '垃圾回收', value: 0, isActive: false },
|
|
|
+ { title: '内存碎片整理', value: 0, isActive: false },
|
|
|
+ { title: '进程优化', value: 0, isActive: false }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ];
|
|
|
|
|
|
// 添加到进度列表
|
|
|
- progressList.value.push(newProgress);
|
|
|
+ progressList.value = mockTasks;
|
|
|
+
|
|
|
+ // 开始模拟进度
|
|
|
+ simulateProgress();
|
|
|
+};
|
|
|
+
|
|
|
+// 模拟复杂的三级进度处理
|
|
|
+const simulateProgress = () => {
|
|
|
+ let currentTaskIndex = 0;
|
|
|
+ let currentSubTaskIndex = 0;
|
|
|
+ let currentSubSubTaskIndex = 0;
|
|
|
|
|
|
- // 模拟进度增长
|
|
|
const progressInterval = setInterval(() => {
|
|
|
- const progressIndex = progressList.value.findIndex(p => p.id === newProgress.id);
|
|
|
- if (progressIndex !== -1) {
|
|
|
- progressList.value[progressIndex].value += 5;
|
|
|
+ if (currentTaskIndex >= progressList.value.length) {
|
|
|
+ clearInterval(progressInterval);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentTask = progressList.value[currentTaskIndex];
|
|
|
+ const currentSubTask = currentTask.subTasks[currentSubTaskIndex];
|
|
|
+
|
|
|
+ if (!currentSubTask) return;
|
|
|
+
|
|
|
+ const currentSubSubTask = currentSubTask.subSubTasks[currentSubSubTaskIndex];
|
|
|
+
|
|
|
+ // 更新当前子子任务状态
|
|
|
+ if (currentSubSubTask) {
|
|
|
+ currentSubSubTask.isActive = true;
|
|
|
+ currentSubSubTask.value = Math.min(currentSubSubTask.value + Math.random() * 20 + 10, 100);
|
|
|
|
|
|
- if (progressList.value[progressIndex].value >= 100) {
|
|
|
- clearInterval(progressInterval);
|
|
|
- progressList.value[progressIndex].status = '完成';
|
|
|
- progressList.value[progressIndex].value = 100;
|
|
|
+ // 更新子任务进度
|
|
|
+ const completedSubSubTasks = currentSubTask.subSubTasks.filter(subSub => subSub.value >= 100).length;
|
|
|
+ const totalSubSubTasks = currentSubTask.subSubTasks.length;
|
|
|
+ currentSubTask.value = Math.floor((completedSubSubTasks / totalSubSubTasks) * 100);
|
|
|
+
|
|
|
+ // 更新主任务进度
|
|
|
+ const completedSubTasks = currentTask.subTasks.filter(sub => sub.value >= 100).length;
|
|
|
+ const totalSubTasks = currentTask.subTasks.length;
|
|
|
+ currentTask.value = Math.floor((completedSubTasks / totalSubTasks) * 100);
|
|
|
+
|
|
|
+ // 更新主任务状态
|
|
|
+ if (currentTask.value === 0) {
|
|
|
+ currentTask.status = '准备中...';
|
|
|
+ } else if (currentTask.value < 100) {
|
|
|
+ currentTask.status = `进行中... (${completedSubTasks}/${totalSubTasks})`;
|
|
|
+ } else {
|
|
|
+ currentTask.status = '已完成';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 子子任务完成,移动到下一个
|
|
|
+ if (currentSubSubTask.value >= 100) {
|
|
|
+ currentSubSubTask.isActive = false;
|
|
|
+ currentSubSubTaskIndex++;
|
|
|
+
|
|
|
+ // 当前子任务的所有子子任务完成,移动到下一个子任务
|
|
|
+ if (currentSubSubTaskIndex >= currentSubTask.subSubTasks.length) {
|
|
|
+ currentSubTask.isActive = false;
|
|
|
+ currentSubTaskIndex++;
|
|
|
+ currentSubSubTaskIndex = 0;
|
|
|
+
|
|
|
+ // 开始下一个子任务
|
|
|
+ if (currentSubTaskIndex < currentTask.subTasks.length) {
|
|
|
+ currentTask.subTasks[currentSubTaskIndex].isActive = true;
|
|
|
+ } else {
|
|
|
+ // 当前任务的所有子任务完成,移动到下一个主任务
|
|
|
+ currentTaskIndex++;
|
|
|
+ currentSubTaskIndex = 0;
|
|
|
+
|
|
|
+ // 开始下一个任务
|
|
|
+ if (currentTaskIndex < progressList.value.length) {
|
|
|
+ progressList.value[currentTaskIndex].status = '准备中...';
|
|
|
+ progressList.value[currentTaskIndex].subTasks[0].isActive = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }, 100);
|
|
|
+ }, 150);
|
|
|
+};
|
|
|
+
|
|
|
+// 获取进度图标
|
|
|
+const getProgressIcon = (progress) => {
|
|
|
+ if (progress.status === '错误') return '❌';
|
|
|
+ if (progress.value >= 100) return '✅';
|
|
|
+ if (progress.value > 0) return '🔄';
|
|
|
+ return '⏳';
|
|
|
+};
|
|
|
+
|
|
|
+// 获取子进度图标
|
|
|
+const getSubProgressIcon = (subTask) => {
|
|
|
+ if (subTask.value >= 100) return '✓';
|
|
|
+ if (subTask.isActive) return '▶';
|
|
|
+ return '○';
|
|
|
+};
|
|
|
+
|
|
|
+// 获取子子进度图标
|
|
|
+const getSubSubProgressIcon = (subSubTask) => {
|
|
|
+ if (subSubTask.value >= 100) return '●';
|
|
|
+ if (subSubTask.isActive) return '▸';
|
|
|
+ return '◦';
|
|
|
+};
|
|
|
+
|
|
|
+// 获取进度样式类
|
|
|
+const getProgressClass = (progress) => {
|
|
|
+ if (progress.status === '错误') return 'error';
|
|
|
+ if (progress.value >= 100) return 'completed';
|
|
|
+ if (progress.value > 0) return 'active';
|
|
|
+ return 'pending';
|
|
|
+};
|
|
|
+
|
|
|
+// 获取进度条样式类
|
|
|
+const getProgressBarClass = (progress) => {
|
|
|
+ if (progress.status === '错误') return 'error-bar';
|
|
|
+ if (progress.value >= 100) return 'completed-bar';
|
|
|
+ return 'active-bar';
|
|
|
};
|
|
|
|
|
|
// 关闭进度弹窗
|
|
@@ -3297,26 +3605,70 @@ const updateCollapsedState = () => {
|
|
|
background-color: #f5f5f5;
|
|
|
}
|
|
|
|
|
|
-/* 进度条弹窗样式 */
|
|
|
+/* 进度条弹窗样式 - 优化版本 */
|
|
|
.progress-modal-overlay {
|
|
|
position: fixed;
|
|
|
- bottom: 200px;
|
|
|
- right: 20px;
|
|
|
+ bottom: 180px;
|
|
|
+ right: 5%;
|
|
|
width: auto;
|
|
|
height: auto;
|
|
|
background-color: transparent;
|
|
|
display: block;
|
|
|
- z-index: 9999;
|
|
|
+ z-index: 9998;
|
|
|
+ animation: slideInUp 0.3s ease-out;
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式定位 - 确保在不同屏幕尺寸下都在任务按钮上方 */
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .progress-modal-overlay {
|
|
|
+ bottom: 160px;
|
|
|
+ right: 5%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 480px) {
|
|
|
+ .progress-modal-overlay {
|
|
|
+ bottom: 140px;
|
|
|
+ right: 15%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 大屏幕优化 */
|
|
|
+@media (min-width: 1200px) {
|
|
|
+ .progress-modal-overlay {
|
|
|
+ bottom: 200px;
|
|
|
+ right: 5%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 超宽屏幕优化 */
|
|
|
+@media (min-width: 1920px) {
|
|
|
+ .progress-modal-overlay {
|
|
|
+ bottom: 220px;
|
|
|
+ right: 5%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes slideInUp {
|
|
|
+ from {
|
|
|
+ transform: translateY(100%);
|
|
|
+ opacity: 0;
|
|
|
+ }
|
|
|
+ to {
|
|
|
+ transform: translateY(0);
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.progress-modal {
|
|
|
- background: white;
|
|
|
+ background: #ffffff;
|
|
|
border-radius: 12px;
|
|
|
- padding: 20px;
|
|
|
- min-width: 280px;
|
|
|
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
|
- text-align: center;
|
|
|
- border: 1px solid #e8e8e8;
|
|
|
+ padding: 24px;
|
|
|
+ min-width: 320px;
|
|
|
+ max-width: 400px;
|
|
|
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
|
|
|
+ text-align: left;
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
}
|
|
|
|
|
|
.progress-modal-header {
|
|
@@ -3324,94 +3676,486 @@ const updateCollapsedState = () => {
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
margin-bottom: 20px;
|
|
|
+ padding-bottom: 12px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
}
|
|
|
|
|
|
.progress-modal-header h3 {
|
|
|
margin: 0;
|
|
|
- color: #333;
|
|
|
+ color: #1a1a1a;
|
|
|
font-size: 18px;
|
|
|
font-weight: 600;
|
|
|
+ background: linear-gradient(135deg, #1890ff, #722ed1);
|
|
|
+ -webkit-background-clip: text;
|
|
|
+ -webkit-text-fill-color: transparent;
|
|
|
+ background-clip: text;
|
|
|
}
|
|
|
|
|
|
.close-btn {
|
|
|
- background: none;
|
|
|
+ background: rgba(0, 0, 0, 0.05);
|
|
|
border: none;
|
|
|
- font-size: 24px;
|
|
|
- color: #999;
|
|
|
+ font-size: 18px;
|
|
|
+ color: #666;
|
|
|
cursor: pointer;
|
|
|
padding: 0;
|
|
|
- width: 24px;
|
|
|
- height: 24px;
|
|
|
+ width: 28px;
|
|
|
+ height: 28px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
border-radius: 50%;
|
|
|
transition: all 0.2s ease;
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
|
|
|
.close-btn:hover {
|
|
|
- background-color: #f5f5f5;
|
|
|
- color: #666;
|
|
|
+ background: rgba(255, 0, 0, 0.1);
|
|
|
+ color: #ff4d4f;
|
|
|
+ transform: scale(1.1);
|
|
|
}
|
|
|
|
|
|
.progress-list {
|
|
|
- max-height: 300px;
|
|
|
+ max-height: 350px;
|
|
|
overflow-y: auto;
|
|
|
+ scrollbar-width: thin;
|
|
|
+ scrollbar-color: #d9d9d9 transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-list::-webkit-scrollbar {
|
|
|
+ width: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-list::-webkit-scrollbar-track {
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-list::-webkit-scrollbar-thumb {
|
|
|
+ background: #d9d9d9;
|
|
|
+ border-radius: 2px;
|
|
|
}
|
|
|
|
|
|
.progress-item {
|
|
|
margin-bottom: 16px;
|
|
|
- padding: 12px;
|
|
|
- border: 1px solid #f0f0f0;
|
|
|
- border-radius: 8px;
|
|
|
- background-color: #fafafa;
|
|
|
+ padding: 16px;
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.06);
|
|
|
+ border-radius: 12px;
|
|
|
+ background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%);
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-item::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ height: 2px;
|
|
|
+ background: linear-gradient(90deg, #1890ff, #722ed1);
|
|
|
+ opacity: 0.8;
|
|
|
}
|
|
|
|
|
|
.progress-item:last-child {
|
|
|
margin-bottom: 0;
|
|
|
}
|
|
|
|
|
|
+.progress-item:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
.progress-info {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
- margin-bottom: 8px;
|
|
|
+ margin-bottom: 12px;
|
|
|
}
|
|
|
|
|
|
.progress-title {
|
|
|
font-size: 14px;
|
|
|
- color: #333;
|
|
|
+ color: #262626;
|
|
|
font-weight: 500;
|
|
|
+ flex: 1;
|
|
|
+ margin-right: 12px;
|
|
|
}
|
|
|
|
|
|
.progress-percentage {
|
|
|
- font-size: 12px;
|
|
|
- color: #666;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #1890ff;
|
|
|
font-weight: 600;
|
|
|
+ background: rgba(24, 144, 255, 0.1);
|
|
|
+ padding: 2px 8px;
|
|
|
+ border-radius: 12px;
|
|
|
+ min-width: 45px;
|
|
|
+ text-align: center;
|
|
|
}
|
|
|
|
|
|
.progress-bar-container {
|
|
|
width: 100%;
|
|
|
- height: 6px;
|
|
|
- background-color: #e8e8e8;
|
|
|
- border-radius: 3px;
|
|
|
+ height: 8px;
|
|
|
+ background: linear-gradient(90deg, #f0f0f0, #e8e8e8);
|
|
|
+ border-radius: 4px;
|
|
|
overflow: hidden;
|
|
|
- margin-bottom: 8px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ position: relative;
|
|
|
}
|
|
|
|
|
|
.progress-bar {
|
|
|
height: 100%;
|
|
|
- background: linear-gradient(90deg, #1890ff, #40a9ff);
|
|
|
- border-radius: 3px;
|
|
|
- transition: width 0.1s ease;
|
|
|
+ background: linear-gradient(90deg, #1890ff 0%, #40a9ff 50%, #722ed1 100%);
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-bar::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
|
|
+ animation: shimmer 2s infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes shimmer {
|
|
|
+ 0% { transform: translateX(-100%); }
|
|
|
+ 100% { transform: translateX(100%); }
|
|
|
}
|
|
|
|
|
|
.progress-status {
|
|
|
font-size: 12px;
|
|
|
+ color: #8c8c8c;
|
|
|
+ text-align: right;
|
|
|
+ font-style: italic;
|
|
|
+}
|
|
|
+
|
|
|
+/* 进度项状态样式 */
|
|
|
+.progress-item.completed {
|
|
|
+ background: linear-gradient(135deg, #f6ffed 0%, #f0f9ff 100%);
|
|
|
+ border-color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-item.error {
|
|
|
+ background: linear-gradient(135deg, #fff2f0 0%, #fff1f0 100%);
|
|
|
+ border-color: #ff4d4f;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-title-wrapper {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-icon {
|
|
|
+ font-size: 16px;
|
|
|
+ min-width: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 进度百分比状态样式 */
|
|
|
+.progress-percentage.completed {
|
|
|
+ background: rgba(82, 196, 26, 0.1);
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-percentage.error {
|
|
|
+ background: rgba(255, 77, 79, 0.1);
|
|
|
+ color: #ff4d4f;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-percentage.active {
|
|
|
+ background: rgba(24, 144, 255, 0.1);
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 进度条状态样式 */
|
|
|
+.progress-bar.completed-bar {
|
|
|
+ background: linear-gradient(90deg, #52c41a 0%, #73d13d 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.progress-bar.error-bar {
|
|
|
+ background: linear-gradient(90deg, #ff4d4f 0%, #ff7875 100%);
|
|
|
+}
|
|
|
+
|
|
|
+/* 子进度项样式 */
|
|
|
+.sub-progress-list {
|
|
|
+ margin-top: 12px;
|
|
|
+ padding-left: 16px;
|
|
|
+ border-left: 2px solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ padding: 8px 12px;
|
|
|
+ background: rgba(0, 0, 0, 0.02);
|
|
|
+ border-radius: 6px;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.active {
|
|
|
+ background: rgba(24, 144, 255, 0.05);
|
|
|
+ border-color: rgba(24, 144, 255, 0.2);
|
|
|
+ transform: translateX(4px);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.completed {
|
|
|
+ background: rgba(82, 196, 26, 0.05);
|
|
|
+ border-color: rgba(82, 196, 26, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-icon {
|
|
|
+ font-size: 12px;
|
|
|
+ min-width: 16px;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.active .sub-progress-icon {
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.completed .sub-progress-icon {
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-title {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #595959;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.active .sub-progress-title {
|
|
|
+ color: #1890ff;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.completed .sub-progress-title {
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-percentage {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #8c8c8c;
|
|
|
+ font-weight: 600;
|
|
|
+ min-width: 35px;
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.active .sub-progress-percentage {
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.completed .sub-progress-percentage {
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-bar-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 4px;
|
|
|
+ background: #f5f5f5;
|
|
|
+ border-radius: 2px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-bar {
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(90deg, #d9d9d9 0%, #bfbfbf 100%);
|
|
|
+ border-radius: 2px;
|
|
|
+ transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.active .sub-progress-bar {
|
|
|
+ background: linear-gradient(90deg, #1890ff 0%, #40a9ff 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.completed .sub-progress-bar {
|
|
|
+ background: linear-gradient(90deg, #52c41a 0%, #73d13d 100%);
|
|
|
+}
|
|
|
+
|
|
|
+/* 子子进度项样式 */
|
|
|
+.sub-sub-progress-list {
|
|
|
+ margin-top: 8px;
|
|
|
+ padding-left: 12px;
|
|
|
+ border-left: 1px solid #e8e8e8;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item {
|
|
|
+ margin-bottom: 6px;
|
|
|
+ padding: 6px 8px;
|
|
|
+ background: rgba(0, 0, 0, 0.01);
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ transition: all 0.15s ease;
|
|
|
+ font-size: 11px;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.active {
|
|
|
+ background: rgba(24, 144, 255, 0.03);
|
|
|
+ border-color: rgba(24, 144, 255, 0.15);
|
|
|
+ transform: translateX(2px);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.completed {
|
|
|
+ background: rgba(82, 196, 26, 0.03);
|
|
|
+ border-color: rgba(82, 196, 26, 0.15);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ margin-bottom: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-icon {
|
|
|
+ font-size: 10px;
|
|
|
+ min-width: 12px;
|
|
|
color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.active .sub-sub-progress-icon {
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.completed .sub-sub-progress-icon {
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-title {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #8c8c8c;
|
|
|
+ flex: 1;
|
|
|
+ line-height: 1.2;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.active .sub-sub-progress-title {
|
|
|
+ color: #1890ff;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.completed .sub-sub-progress-title {
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-percentage {
|
|
|
+ font-size: 10px;
|
|
|
+ color: #bfbfbf;
|
|
|
+ font-weight: 600;
|
|
|
+ min-width: 30px;
|
|
|
text-align: right;
|
|
|
}
|
|
|
|
|
|
+.sub-sub-progress-item.active .sub-sub-progress-percentage {
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.completed .sub-sub-progress-percentage {
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-bar-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 3px;
|
|
|
+ background: #f8f8f8;
|
|
|
+ border-radius: 1.5px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-bar {
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(90deg, #e8e8e8 0%, #d9d9d9 100%);
|
|
|
+ border-radius: 1.5px;
|
|
|
+ transition: width 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.active .sub-sub-progress-bar {
|
|
|
+ background: linear-gradient(90deg, #69c0ff 0%, #91d5ff 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.completed .sub-sub-progress-bar {
|
|
|
+ background: linear-gradient(90deg, #95de64 0%, #b7eb8f 100%);
|
|
|
+}
|
|
|
+
|
|
|
+/* 层级缩进视觉效果 */
|
|
|
+.progress-item {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-item::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ height: 2px;
|
|
|
+ background: linear-gradient(90deg, #1890ff, #722ed1);
|
|
|
+ opacity: 0.8;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: -16px;
|
|
|
+ top: 50%;
|
|
|
+ width: 8px;
|
|
|
+ height: 1px;
|
|
|
+ background: #d9d9d9;
|
|
|
+ transform: translateY(-50%);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.active::before {
|
|
|
+ background: #1890ff;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-progress-item.completed::before {
|
|
|
+ background: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: -12px;
|
|
|
+ top: 50%;
|
|
|
+ width: 6px;
|
|
|
+ height: 1px;
|
|
|
+ background: #e8e8e8;
|
|
|
+ transform: translateY(-50%);
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.active::before {
|
|
|
+ background: #69c0ff;
|
|
|
+}
|
|
|
+
|
|
|
+.sub-sub-progress-item.completed::before {
|
|
|
+ background: #95de64;
|
|
|
+}
|
|
|
+
|
|
|
.dropdown-item i {
|
|
|
margin-right: 8px;
|
|
|
font-size: 14px;
|