feat(short-drama-agent-front): 调整工作台布局与图库瀑布流
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
NSelect,
|
||||
NTag
|
||||
} from 'naive-ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { computed, ref, watch, type Directive } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
Search,
|
||||
@@ -40,7 +40,7 @@ import FormPromptDialog from './components/FormPromptDialog.vue'
|
||||
import FormPromptBatch from './components/FormPromptBatch.vue'
|
||||
import { useKeyframeImpact } from './useKeyframeImpact'
|
||||
import { queryText, referenceLocation, referenceTargets } from '../production/asset-links'
|
||||
import { formCoverAspectRatio, type GalleryLayout } from './layout'
|
||||
import { masonryRowSpan, type GalleryLayout } from './layout'
|
||||
|
||||
/** 形态图库以正式数据库为准;首次读取、手动刷新和自身操作完成后更新,不定时刷新。 */
|
||||
const route = useRoute()
|
||||
@@ -139,6 +139,75 @@ const layout = ref<GalleryLayout>('grid')
|
||||
const module = ref('all')
|
||||
const onlyMissing = ref(false)
|
||||
const onlyStale = ref(false)
|
||||
/** 保存每张卡片的尺寸观察器,切换回网格时及时释放。 */
|
||||
const masonryCardObservers = new WeakMap<HTMLElement, ResizeObserver>()
|
||||
|
||||
/**
|
||||
* 按素材顺序重新计算整个瀑布流的位置。
|
||||
* @param container 瀑布流容器元素
|
||||
*/
|
||||
function syncMasonryLayout(container: HTMLElement) {
|
||||
if (!container.classList.contains('form-image-masonry')) return
|
||||
const styles = getComputedStyle(container)
|
||||
const rowHeight = Number.parseFloat(styles.gridAutoRows)
|
||||
const rowGap = Number.parseFloat(styles.rowGap)
|
||||
const columnCount = Math.max(1, styles.gridTemplateColumns.split(/\s+/).filter(Boolean).length)
|
||||
const nextRows = Array<number>(columnCount).fill(1)
|
||||
const cards = Array.from(container.children).filter(
|
||||
(element): element is HTMLElement =>
|
||||
element instanceof HTMLElement && element.classList.contains('form-image-card')
|
||||
)
|
||||
cards.forEach((card, index) => {
|
||||
const column = index % columnCount
|
||||
const span = masonryRowSpan(card.getBoundingClientRect().height, rowHeight, rowGap)
|
||||
card.style.setProperty('--form-image-column', String(column + 1))
|
||||
const rowStart = nextRows[column] ?? 1
|
||||
card.style.setProperty('--form-image-row-start', String(rowStart))
|
||||
card.style.setProperty('--form-image-row-span', String(span))
|
||||
nextRows[column] = rowStart + span
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始监听瀑布流卡片尺寸变化。
|
||||
* @param element 形态图片卡片元素
|
||||
*/
|
||||
function observeMasonryCard(element: HTMLElement) {
|
||||
if (masonryCardObservers.has(element) || typeof ResizeObserver === 'undefined') return
|
||||
const observer = new ResizeObserver(() => {
|
||||
if (element.parentElement) syncMasonryLayout(element.parentElement)
|
||||
})
|
||||
observer.observe(element)
|
||||
masonryCardObservers.set(element, observer)
|
||||
if (element.parentElement) syncMasonryLayout(element.parentElement)
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止监听瀑布流卡片并清理网格跨度。
|
||||
* @param element 形态图片卡片元素
|
||||
*/
|
||||
function unobserveMasonryCard(element: HTMLElement) {
|
||||
masonryCardObservers.get(element)?.disconnect()
|
||||
masonryCardObservers.delete(element)
|
||||
element.style.removeProperty('--form-image-column')
|
||||
element.style.removeProperty('--form-image-row-start')
|
||||
element.style.removeProperty('--form-image-row-span')
|
||||
}
|
||||
|
||||
/** 让卡片按实际高度占据网格行,保持从左到右的 DOM 排列顺序。 */
|
||||
const vMasonryCard: Directive<HTMLElement, boolean> = {
|
||||
mounted(element, binding) {
|
||||
if (binding.value) observeMasonryCard(element)
|
||||
},
|
||||
updated(element, binding) {
|
||||
if (binding.value === binding.oldValue) return
|
||||
if (binding.value) observeMasonryCard(element)
|
||||
else unobserveMasonryCard(element)
|
||||
},
|
||||
beforeUnmount(element) {
|
||||
unobserveMasonryCard(element)
|
||||
}
|
||||
}
|
||||
const generateOpen = ref(false)
|
||||
const galleryOpen = ref(false)
|
||||
const generateId = ref('')
|
||||
@@ -593,7 +662,7 @@ async function showStaleForms() {
|
||||
<NAlert v-if="query.error.value" role="alert" type="error" :show-icon="false" class="mt-4"
|
||||
>{{ query.error.value }}<span v-if="forms.length"> 当前展示上次读取的数据。</span></NAlert
|
||||
><NAlert v-if="running" role="status" type="info" :show-icon="false" class="mt-4">
|
||||
数据库中仍有排队或生成中的图片,已暂停新的生图操作。长时间无变化时,请先核对后台任务;刷新或关闭页面不会取消任务。
|
||||
数据库中仍有排队或生成中的图片,已暂停新的生图操作。长时间无变化时请先核对任务状态。
|
||||
</NAlert>
|
||||
<p v-if="!query.data.value && query.loading.value" class="py-10 text-sm text-muted" role="status">
|
||||
正在读取形态和已有图片…
|
||||
@@ -605,6 +674,7 @@ async function showStaleForms() {
|
||||
>
|
||||
<article
|
||||
v-for="form in filtered"
|
||||
v-masonry-card="layout === 'masonry'"
|
||||
:key="form.id"
|
||||
class="panel form-image-card"
|
||||
:class="{
|
||||
@@ -612,7 +682,6 @@ async function showStaleForms() {
|
||||
'form-image-card-focused': targetFormId === form.id
|
||||
}"
|
||||
:data-form-id="form.id"
|
||||
:style="{ '--form-image-aspect': formCoverAspectRatio(form) }"
|
||||
>
|
||||
<AssetImage
|
||||
:src="coverImage(form)?.imageUrl"
|
||||
@@ -759,13 +828,21 @@ async function showStaleForms() {
|
||||
@apply pt-3;
|
||||
}
|
||||
.gallery-workspace-page .form-image-masonry {
|
||||
@apply block columns-[260px] gap-x-5;
|
||||
@apply grid grid-cols-[repeat(auto-fill,_minmax(min(260px,_100%),_1fr))] gap-x-5 items-start;
|
||||
grid-auto-flow: row dense;
|
||||
grid-auto-rows: 1px;
|
||||
}
|
||||
.form-image-masonry > .form-image-card {
|
||||
@apply break-inside-avoid mb-5;
|
||||
grid-column-start: var(--form-image-column, auto);
|
||||
grid-row-start: var(--form-image-row-start, auto);
|
||||
grid-row-end: span var(--form-image-row-span, 1);
|
||||
}
|
||||
.form-image-masonry .asset-image {
|
||||
@apply aspect-[var(--form-image-aspect,4/3)];
|
||||
@apply aspect-auto;
|
||||
}
|
||||
.form-image-masonry .asset-image .n-image,
|
||||
.form-image-masonry .asset-image .n-image img {
|
||||
@apply h-auto;
|
||||
}
|
||||
.gallery-sticky-controls > .form-image-filter-region {
|
||||
@apply my-0;
|
||||
|
||||
Reference in New Issue
Block a user