You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
5.3 KiB
Vue

1 month ago
<!-- components/ProgressTracker.vue -->
<template>
<teleport to="body">
<div v-if="visible" class="progress-container" :class="{ minimized: isMinimized }">
<!-- 全尺寸模式 -->
<div v-if="!isMinimized" class="progress-expanded">
<div class="progress-header">
<span>{{ title }}</span>
<button class="btn-close" @click="close">&times;</button>
</div>
<!-- 进度条 -->
<div class="progress-bar-bg">
<div class="progress-bar" :style="{ width: progress + '%' }"></div>
</div>
<!-- 进度消息 + 百分比 -->
<div class="progress-footer">
<div class="progress-message">
<span v-if="progress < 100">🔄{{ progress }} %</span>
<span v-else></span>
{{ message }}
</div>
<button class="btn-minimize" @click="toggleMinimize"></button>
</div>
</div>
<!-- 最小化模式右下角小窗口 -->
<div v-else class="progress-minimized" @click="toggleMinimize">
{{ progress }}%
<button class="btn-close-sm" @click.stop="close">&times;</button>
</div>
</div>
</teleport>
</template>
<script setup>
import { ref, watch } from 'vue'
// Props
const props = defineProps({
modelValue: {
type: Boolean,
default: true
},
initialProgress: {
type: Number,
default: 0
},
title: {
type: String,
default: '上传进度'
},
// ✅ 新增:进度消息
message: {
type: String,
default: ''
}
})
// Emits
const emit = defineEmits(['update:modelValue', 'complete',
'manual-close'])
// 状态
const visible = ref(props.modelValue)
const isMinimized = ref(false)
const progress = computed(() => props.initialProgress)
// 监听 v-model
watch(() => props.modelValue, (val) => {
visible.value = val
if (val && progress.value >= 100) {
close()
}
})
// 监听进度变化:自动关闭
watch(progress, (val) => {
if (val >= 100) {
setTimeout(() => close(), 500)
emit('complete')
}
})
// 控制显隐
function close() {
visible.value = false
emit('update:modelValue', false)
emit('manual-close')
}
function toggleMinimize() {
isMinimized.value = !isMinimized.value
}
// 暴露方法给父组件
defineExpose({
close,
setProgress(newVal) {
progress.value = Math.min(100, Math.max(0, newVal))
},
getProgress() {
return progress.value
},
increase(step = 1) {
progress.value = Math.min(100, progress.value + step)
},
// ✅ 新增:动态设置消息
setMessage(newMsg) {
// 注意prop 是只读的,我们通过 expose 提供方法更新内部状态
// 实际使用中,建议通过 prop 绑定响应式变量,这里 expose 仅为方便
}
})
</script>
<style scoped>
.progress-container {
position: fixed;
bottom: 50px;
right: 20px;
z-index: 3000;
width: 350px;
font-family: Arial, sans-serif;
}
.progress-expanded {
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
overflow: hidden;
}
.progress-header {
padding: 10px 15px;
background: #f5f5f5;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
color: #333;
}
.btn-close,
.btn-close-sm {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #999;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
}
.btn-close:hover,
.btn-close-sm:hover {
color: #ff4d4f;
}
.progress-bar-bg {
height: 6px;
background: #e0e0e0;
border-radius: 3px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: #1890ff;
width: 0;
transition: width 0.3s ease;
}
.progress-footer {
padding: 12px 15px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
color: #555;
}
.progress-message {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #333;
font-size: 13px;
}
.btn-minimize {
background: #f0f0f0;
border: 1px solid #ddd;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.btn-minimize:hover {
background: #e0e0e0;
}
.progress-minimized {
width: 60px;
height: 60px;
background: #1890ff;
color: white;
border-radius: 8px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
cursor: pointer;
position: relative;
}
.progress-minimized .btn-close-sm {
position: absolute;
top: -8px;
right: -8px;
background: white;
border-radius: 50%;
width: 18px;
height: 18px;
line-height: 18px;
font-size: 14px;
}
@media (max-width: 480px) {
.progress-container {
right: 10px;
left: auto;
width: 280px;
}
}
</style>