fix: 修复进度监听器泄露问题,添加 nextTick 确保动画先于 IPC 渲染
This commit is contained in:
@@ -1,9 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="pack-progress card">
|
<div class="pack-progress card">
|
||||||
<div class="progress-header">
|
<div class="progress-header">
|
||||||
<div class="spinner" :class="{ active: !result }">
|
<span class="spinner" v-if="!result"><span class="spinner-dot"></span></span>
|
||||||
<div class="spinner-ring"></div>
|
|
||||||
</div>
|
|
||||||
<h3>打包进度</h3>
|
<h3>打包进度</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -11,11 +9,11 @@
|
|||||||
<div
|
<div
|
||||||
class="progress-bar"
|
class="progress-bar"
|
||||||
:class="{ indeterminate: isIndeterminate }"
|
:class="{ indeterminate: isIndeterminate }"
|
||||||
:style="barStyle"
|
:style="!isIndeterminate ? { width: displayPercent } : {}"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="progress.stage" class="progress-info">
|
<div class="progress-info">
|
||||||
<span class="progress-label">{{ stageLabel }}</span>
|
<span class="progress-label">{{ stageLabel }}</span>
|
||||||
<span class="progress-percent">{{ displayPercent }}</span>
|
<span class="progress-percent">{{ displayPercent }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,12 +68,7 @@ const stageLabel = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isIndeterminate = computed(() => {
|
const isIndeterminate = computed(() => {
|
||||||
return ['install', 'download'].includes(props.progress.stage)
|
return ['starting', 'install', 'download'].includes(props.progress.stage)
|
||||||
})
|
|
||||||
|
|
||||||
const barStyle = computed(() => {
|
|
||||||
if (isIndeterminate.value) return {}
|
|
||||||
return { width: Math.round(props.progress.percent) + '%' }
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const displayPercent = computed(() => {
|
const displayPercent = computed(() => {
|
||||||
@@ -102,23 +95,24 @@ const displayPercent = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
width: 20px;
|
display: inline-flex;
|
||||||
height: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner.active .spinner-ring {
|
.spinner-dot {
|
||||||
width: 16px;
|
width: 14px;
|
||||||
height: 16px;
|
height: 14px;
|
||||||
border: 2.5px solid var(--border);
|
border: 2.5px solid var(--border);
|
||||||
border-top-color: var(--primary);
|
border-top-color: var(--primary);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 0.8s linear infinite;
|
animation: spin 0.7s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,18 +126,18 @@ const displayPercent = computed(() => {
|
|||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--primary);
|
background: linear-gradient(90deg, var(--primary), #818cf8);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: width 0.3s ease;
|
transition: width 0.3s ease;
|
||||||
width: 0%;
|
min-width: 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar.indeterminate {
|
.progress-bar.indeterminate {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
animation: indeterminate 1.5s ease-in-out infinite;
|
animation: slide 1.2s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes indeterminate {
|
@keyframes slide {
|
||||||
0% { transform: translateX(-100%); }
|
0% { transform: translateX(-100%); }
|
||||||
50% { transform: translateX(200%); }
|
50% { transform: translateX(200%); }
|
||||||
100% { transform: translateX(400%); }
|
100% { transform: translateX(400%); }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
export const usePackagingStore = defineStore('packaging', {
|
export const usePackagingStore = defineStore('packaging', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -8,39 +9,49 @@ export const usePackagingStore = defineStore('packaging', {
|
|||||||
message: '',
|
message: '',
|
||||||
percent: 0
|
percent: 0
|
||||||
},
|
},
|
||||||
result: null
|
result: null,
|
||||||
|
_cleanup: null
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
startPacking(projectPath, outputPath) {
|
async startPacking(projectPath, outputPath) {
|
||||||
|
this._cleanup?.()
|
||||||
|
|
||||||
this.isPacking = true
|
this.isPacking = true
|
||||||
this.progress = { stage: 'starting', message: '准备开始...', percent: 0 }
|
this.progress = { stage: 'starting', message: '准备开始...', percent: 0 }
|
||||||
this.result = null
|
this.result = null
|
||||||
|
|
||||||
const cleanup = window.api.pack.onProgress((progress) => {
|
this._cleanup = window.api.pack.onProgress((progress) => {
|
||||||
this.progress = progress
|
this.progress = progress
|
||||||
})
|
})
|
||||||
|
|
||||||
window.api.pack.start(projectPath, outputPath).then((result) => {
|
await nextTick()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await window.api.pack.start(projectPath, outputPath)
|
||||||
this.isPacking = false
|
this.isPacking = false
|
||||||
this.result = result
|
this.result = result
|
||||||
cleanup()
|
} catch (err) {
|
||||||
}).catch((err) => {
|
|
||||||
this.isPacking = false
|
this.isPacking = false
|
||||||
this.result = { success: false, error: err.message }
|
this.result = { success: false, error: err.message }
|
||||||
cleanup()
|
}
|
||||||
})
|
this._cleanup?.()
|
||||||
|
this._cleanup = null
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelPacking() {
|
cancelPacking() {
|
||||||
window.api.pack.cancel()
|
window.api.pack.cancel()
|
||||||
this.isPacking = false
|
this.isPacking = false
|
||||||
|
this._cleanup?.()
|
||||||
|
this._cleanup = null
|
||||||
},
|
},
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.isPacking = false
|
this.isPacking = false
|
||||||
this.progress = { stage: '', message: '', percent: 0 }
|
this.progress = { stage: '', message: '', percent: 0 }
|
||||||
this.result = null
|
this.result = null
|
||||||
|
this._cleanup?.()
|
||||||
|
this._cleanup = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user