mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-05 15:40:10 +00:00
Queue media preview (#449)
* output url * Basic image previews * Split out task item component * Move task actions to context menu * simplify * Move spinner * Lift context menu to tab scope * Better tag * Fix placeholder style * nit * Correctly handle cancelled * nit * Split out result item as separate component * nit * Fix center crop * nit * Simplify task item * Flat list * Show prompt id * Make image draggable * Disable preview for dragging * Fix key * Correctly handle task in expanded view * Add preview
This commit is contained in:
@@ -1,125 +1,82 @@
|
||||
<template>
|
||||
<DataTable
|
||||
v-if="tasks.length > 0"
|
||||
:value="tasks"
|
||||
dataKey="promptId"
|
||||
class="queue-table"
|
||||
>
|
||||
<Column header="STATUS">
|
||||
<template #body="{ data }">
|
||||
<Tag :severity="taskTagSeverity(data.displayStatus)">
|
||||
{{ data.displayStatus.toUpperCase() }}
|
||||
</Tag>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="TIME" :pt="{ root: { class: 'queue-time-cell' } }">
|
||||
<template #body="{ data }">
|
||||
<div v-if="data.isHistory" class="queue-time-cell-content">
|
||||
{{ formatTime(data.executionTimeInSeconds) }}
|
||||
</div>
|
||||
<div v-else-if="data.isRunning" class="queue-time-cell-content">
|
||||
<i class="pi pi-spin pi-spinner"></i>
|
||||
</div>
|
||||
<div v-else class="queue-time-cell-content">...</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column
|
||||
:pt="{
|
||||
headerCell: {
|
||||
class: 'queue-tool-header-cell'
|
||||
},
|
||||
bodyCell: {
|
||||
class: 'queue-tool-body-cell'
|
||||
}
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<Toast />
|
||||
<ConfirmPopup />
|
||||
<Button
|
||||
icon="pi pi-trash"
|
||||
text
|
||||
severity="primary"
|
||||
@click="confirmRemoveAll($event)"
|
||||
<SideBarTabTemplate :title="$t('sideToolbar.queue')">
|
||||
<template #tool-buttons>
|
||||
<Button
|
||||
:icon="isExpanded ? 'pi pi-chevron-up' : 'pi pi-chevron-down'"
|
||||
text
|
||||
severity="secondary"
|
||||
@click="isExpanded = !isExpanded"
|
||||
class="toggle-expanded-button"
|
||||
v-tooltip="$t('sideToolbar.queueTab.showFlatList')"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-trash"
|
||||
text
|
||||
severity="primary"
|
||||
@click="confirmRemoveAll($event)"
|
||||
class="clear-all-button"
|
||||
/>
|
||||
</template>
|
||||
<template #body>
|
||||
<div v-if="tasks.length > 0" class="queue-grid">
|
||||
<TaskItem
|
||||
v-for="task in tasks"
|
||||
:key="task.key"
|
||||
:task="task"
|
||||
:isFlatTask="isExpanded"
|
||||
@contextmenu="handleContextMenu"
|
||||
/>
|
||||
</template>
|
||||
<template #body="{ data }">
|
||||
<Button
|
||||
icon="pi pi-file-export"
|
||||
text
|
||||
severity="primary"
|
||||
@click="data.loadWorkflow()"
|
||||
</div>
|
||||
<div v-else>
|
||||
<NoResultsPlaceholder
|
||||
icon="pi pi-info-circle"
|
||||
:title="$t('noTasksFound')"
|
||||
:message="$t('noTasksFoundMessage')"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-times"
|
||||
text
|
||||
severity="secondary"
|
||||
@click="removeTask(data)"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
<div v-else>
|
||||
<NoResultsPlaceholder
|
||||
icon="pi pi-info-circle"
|
||||
:title="$t('noTasksFound')"
|
||||
:message="$t('noTasksFoundMessage')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</SideBarTabTemplate>
|
||||
<Toast />
|
||||
<ConfirmPopup />
|
||||
<ContextMenu ref="menu" :model="menuItems" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Column from 'primevue/column'
|
||||
import Tag from 'primevue/tag'
|
||||
import Button from 'primevue/button'
|
||||
import ConfirmPopup from 'primevue/confirmpopup'
|
||||
import Toast from 'primevue/toast'
|
||||
import ContextMenu from 'primevue/contextmenu'
|
||||
import TaskItem from './queue/TaskItem.vue'
|
||||
import SideBarTabTemplate from './SidebarTabTemplate.vue'
|
||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import { useConfirm } from 'primevue/useconfirm'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import {
|
||||
TaskItemDisplayStatus,
|
||||
TaskItemImpl,
|
||||
useQueueStore
|
||||
} from '@/stores/queueStore'
|
||||
import { computed, onMounted, onUnmounted } from 'vue'
|
||||
import { TaskItemImpl, useQueueStore } from '@/stores/queueStore'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { type MenuItem } from 'primevue/menuitem'
|
||||
import { api } from '@/scripts/api'
|
||||
|
||||
const confirm = useConfirm()
|
||||
const toast = useToast()
|
||||
const queueStore = useQueueStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
const tasks = computed(() =>
|
||||
isExpanded.value ? queueStore.flatTasks : queueStore.tasks
|
||||
)
|
||||
|
||||
const tasks = computed(() => queueStore.tasks)
|
||||
const taskTagSeverity = (status: TaskItemDisplayStatus) => {
|
||||
switch (status) {
|
||||
case TaskItemDisplayStatus.Pending:
|
||||
return 'secondary'
|
||||
case TaskItemDisplayStatus.Running:
|
||||
return 'info'
|
||||
case TaskItemDisplayStatus.Completed:
|
||||
return 'success'
|
||||
case TaskItemDisplayStatus.Failed:
|
||||
return 'danger'
|
||||
case TaskItemDisplayStatus.Cancelled:
|
||||
return 'warning'
|
||||
}
|
||||
}
|
||||
const formatTime = (time?: number) => {
|
||||
if (time === undefined) {
|
||||
return ''
|
||||
}
|
||||
return `${time.toFixed(2)}s`
|
||||
}
|
||||
const removeTask = (task: TaskItemImpl) => {
|
||||
if (task.isRunning) {
|
||||
api.interrupt()
|
||||
}
|
||||
queueStore.delete(task)
|
||||
}
|
||||
|
||||
const removeAllTasks = async () => {
|
||||
await queueStore.clear()
|
||||
}
|
||||
|
||||
const confirmRemoveAll = (event) => {
|
||||
confirm.require({
|
||||
target: event.currentTarget,
|
||||
@@ -147,30 +104,45 @@ const confirmRemoveAll = (event) => {
|
||||
}
|
||||
|
||||
const onStatus = () => queueStore.update()
|
||||
|
||||
const menu = ref(null)
|
||||
const menuTargetTask = ref<TaskItemImpl | null>(null)
|
||||
const menuItems = computed<MenuItem[]>(() => {
|
||||
return [
|
||||
{
|
||||
label: t('delete'),
|
||||
icon: 'pi pi-trash',
|
||||
command: () => removeTask(menuTargetTask.value!)
|
||||
},
|
||||
{
|
||||
label: t('loadWorkflow'),
|
||||
icon: 'pi pi-file-export',
|
||||
command: () => menuTargetTask.value?.loadWorkflow()
|
||||
}
|
||||
]
|
||||
})
|
||||
const handleContextMenu = ({ task, event }) => {
|
||||
menuTargetTask.value = task
|
||||
menu.value.show(event)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
api.addEventListener('status', onStatus)
|
||||
|
||||
queueStore.update()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
api.removeEventListener('status', onStatus)
|
||||
})
|
||||
|
||||
const isExpanded = ref(false)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.queue-tool-header-cell {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.queue-tool-body-cell {
|
||||
display: table-cell;
|
||||
text-align: right !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.queue-time-cell-content {
|
||||
width: fit-content;
|
||||
.queue-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
padding: 0.5rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user