baodan/frontend/src/components/poster/workspace/PosterActionBar.vue

143 lines
2.8 KiB
Vue
Raw Normal View History

2026-07-30 09:15:07 +08:00
<template>
<footer class="poster-action-bar">
<div class="action-status">
<span class="status-text">{{ completionText }}</span>
<div class="progress-dots">
<span
v-for="item in completionItems"
:key="item.key"
class="dot"
:class="{ done: item.done }"
:title="item.label"
/>
</div>
</div>
<div class="action-buttons">
<el-button
v-if="draft.taskStatus === 'done' && draft.posterUrl"
@click="$emit('download')"
>
<el-icon><Download /></el-icon>
下载海报
</el-button>
<el-button
v-if="draft.taskStatus === 'done'"
2026-07-30 10:30:33 +08:00
@click="$emit('new-poster')"
>
新建海报
</el-button>
<el-button
v-if="draft.taskStatus === 'failed'"
2026-07-30 09:15:07 +08:00
@click="$emit('regenerate')"
>
重新生成
</el-button>
<el-button
type="primary"
:disabled="!canAct"
:loading="isGenerating"
@click="$emit('action')"
>
2026-07-30 10:30:33 +08:00
{{ isGenerating ? '生成中...' : actionLabel }}
2026-07-30 09:15:07 +08:00
</el-button>
</div>
</footer>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { Download } from '@element-plus/icons-vue'
import type { PosterDraft } from '@/composables/usePosterWorkspace'
const props = defineProps<{
draft: PosterDraft
completionText: string
completionItems: Array<{ key: string; label: string; done: boolean }>
actionLabel: string
actionState: string
canGenerate: boolean
}>()
defineEmits<{
action: []
download: []
regenerate: []
2026-07-30 10:30:33 +08:00
'new-poster': []
2026-07-30 09:15:07 +08:00
}>()
const isGenerating = computed(() =>
props.draft.taskStatus === 'submitting' ||
props.draft.taskStatus === 'queued' ||
props.draft.taskStatus === 'generating'
)
const canAct = computed(() => {
if (isGenerating.value) return false
return props.actionState !== 'generating'
})
</script>
<style scoped>
.poster-action-bar {
height: 56px;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
border-top: 1px solid var(--poster-border);
background: #fff;
flex-shrink: 0;
}
.action-status {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.status-text {
font-size: 13px;
color: var(--poster-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.progress-dots {
display: flex;
gap: 6px;
flex-shrink: 0;
}
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #e5e7eb;
transition: background 0.2s;
}
.dot.done {
background: #16a34a;
}
.action-buttons {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
@media (max-width: 767px) {
.status-text {
display: none;
}
.poster-action-bar {
padding: 0 12px;
}
}
</style>