虚拟滚动未做完,新加收藏显示逻辑

This commit is contained in:
王佑琳 2026-03-27 15:27:46 +08:00
parent 70529ccd47
commit 57c8863113
9 changed files with 394 additions and 595 deletions

1
components.d.ts vendored
View File

@ -12,6 +12,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
Canvas: typeof import('./src/components/canvas/index.vue')['default']
copy: typeof import('./src/components/virtual-scroller/VirtualScroller copy.vue')['default']
DialogBox: typeof import('./src/components/dialogBox/index.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']

344
out.txt

File diff suppressed because one or more lines are too long

View File

@ -7,5 +7,5 @@ export function getGenerateHistoryList(query) {
// 取消或收藏
export function cancelOrCollect(query) {
return service.post('/collect/toggle', query)
return service.post('/collect/toggle', null, { params: query })
}

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.1494 2.5C13.039 2.5 14.4998 3.96763 14.5 5.88477C14.5 7.06494 13.9819 8.17537 12.9678 9.4209C11.9479 10.6736 10.4752 12.006 8.64648 13.6387C8.63893 13.6454 8.63116 13.652 8.62402 13.6592L8.35547 13.9307C8.15984 14.1281 7.84016 14.1281 7.64453 13.9307L7.37598 13.6592L7.35352 13.6387L6.05078 12.4668C4.81731 11.3444 3.79719 10.3604 3.03223 9.4209C2.01813 8.17537 1.5 7.06494 1.5 5.88477C1.50024 3.9673 2.9594 2.5 4.84863 2.5C5.88613 2.5 6.93824 2.99756 7.61523 3.80469C7.71024 3.91796 7.85021 3.9834 7.99805 3.9834C8.14588 3.9834 8.28586 3.91796 8.38086 3.80469C9.05777 2.99766 10.1098 2.5 11.1494 2.5Z" fill="#FF4D4F"/>
</svg>

After

Width:  |  Height:  |  Size: 736 B

View File

@ -13,8 +13,10 @@
ref="renderContainerRef"
class="virtual-scroller-render-container"
:style="renderContainerStyle"
@scroll.passive="handleScroll"
@wheel="handleWheel"
>
<div class="virtual-scroller-spacer" :style="{ height: `${totalSize}px` }"></div>
<div class="virtual-scroller-spacer" :style="{ height: `${totalHeight}px` }"></div>
<div
class="virtual-scroller-bottom-placeholder"
:style="bottomPlaceholderStyle"
@ -22,18 +24,18 @@
<slot name="bottom-placeholder" />
</div>
<div
v-for="renderItem in pool"
:key="renderItem.nr.key"
:ref="el => setItemRef(el, renderItem.nr.key)"
v-for="renderItem in visibleItems"
:key="renderItem.key"
:ref="el => setItemRef(el, renderItem.key)"
class="virtual-scroller-item"
:style="getItemStyle(renderItem)"
:data-index="renderItem.nr.index"
:data-key="renderItem.nr.key"
:data-index="renderItem.index"
:data-key="renderItem.key"
>
<slot
name="default"
:item="renderItem.item"
:index="renderItem.nr.index"
:index="renderItem.index"
/>
</div>
</div>
@ -42,7 +44,7 @@
</template>
<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, shallowReactive, watch, markRaw } from 'vue'
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
const props = defineProps({
data: {
@ -56,15 +58,11 @@ const props = defineProps({
},
estimatedHeight: {
type: Number,
default: null
},
minItemSize: {
type: [Number, String],
default: null
default: 100
},
buffer: {
type: Number,
default: 200
default: 3
},
height: {
type: [String, Number],
@ -85,38 +83,23 @@ const props = defineProps({
}
})
const emit = defineEmits(['scroll', 'scroll-start', 'scroll-end', 'resize', 'update'])
const emit = defineEmits(['scroll', 'scroll-start', 'scroll-end'])
const containerRef = ref(null)
const wrapperRef = ref(null)
const renderContainerRef = ref(null)
const itemRefs = new Map()
const itemHeights = ref(new Map())
const resizeObserver = ref(null)
const scrollTop = ref(0)
const isScrolling = ref(false)
const scrollTimeout = ref(null)
const isInitialized = ref(false)
const pendingScrollToBottom = ref(false)
let uid = 0
const pool = ref([])
const viewMap = new Map()
const unusedViews = new Map()
const itemSizeMap = ref(new Map())
const totalSize = ref(0)
let $_startIndex = 0
let $_endIndex = 0
let $_scrollDirty = false
let $_lastUpdateScrollPosition = 0
const containerStyle = computed(() => {
return {
height: '100%',
width: '100%',
position: 'relative'
}
})
const containerStyle = computed(() => ({
height: '100%',
width: '100%',
position: 'relative'
}))
const getItemKey = (item, index) => {
if (typeof props.itemKey === 'function') {
@ -128,38 +111,153 @@ const getItemKey = (item, index) => {
return index
}
const minSize = computed(() => {
if (props.minItemSize) {
return typeof props.minItemSize === 'string' ? parseInt(props.minItemSize, 10) : props.minItemSize
const totalHeight = computed(() => {
let height = props.bottomPlaceholderHeight
const len = props.data.length
for (let i = 0; i < len; i++) {
const key = getItemKey(props.data[i], i)
const cachedHeight = itemHeights.value.get(key)
height += cachedHeight !== undefined ? cachedHeight : props.estimatedHeight
}
return props.estimatedHeight || 50
return height
})
const sizes = computed(() => {
const sizesMap = {
'-1': { accumulator: 0 }
}
const items = props.data
let accumulator = 0
let computedMinSize = 10000
const getItemPosition = (index) => {
let offset = 0
for (let i = 0, l = items.length; i < l; i++) {
const key = getItemKey(items[i], i)
let size = itemSizeMap.value.get(key)
if (size === undefined) {
size = minSize.value
}
if (size < computedMinSize) {
computedMinSize = size
}
accumulator += size
sizesMap[i] = { accumulator, size }
for (let i = 0; i < index; i++) {
const key = getItemKey(props.data[i], i)
const cachedHeight = itemHeights.value.get(key)
offset += cachedHeight !== undefined ? cachedHeight : props.estimatedHeight
}
return sizesMap
const key = getItemKey(props.data[index], index)
const height = itemHeights.value.get(key) ?? props.estimatedHeight
return { offset, height }
}
const containerHeight = computed(() => {
if (!renderContainerRef.value) return 0
return renderContainerRef.value.clientHeight
})
const visibleRange = computed(() => {
if (props.data.length === 0) {
return { start: 0, end: 0, offset: 0 }
}
const el = renderContainerRef.value
if (!el) {
const count = props.data.length
return {
start: Math.max(0, count - 10),
end: count - 1,
offset: 0
}
}
const currentScrollTop = el.scrollTop
const viewportHeight = containerHeight.value || el.clientHeight || 600
const bufferCount = props.buffer
const count = props.data.length
const extraBuffer = Math.ceil(viewportHeight / props.estimatedHeight)
const placeholderHeight = props.bottomPlaceholderHeight
const scrollHeight = el.scrollHeight
const maxScrollTop = Math.max(0, scrollHeight - viewportHeight)
const normalizedScrollTop = Math.min(Math.max(0, currentScrollTop), maxScrollTop)
const visibleStart = Math.max(0, normalizedScrollTop)
const visibleEnd = visibleStart + viewportHeight
let startIndex = 0
let endIndex = count - 1
let startOffset = 0
let currentOffset = 0
for (let i = 0; i < count; i++) {
const key = getItemKey(props.data[i], i)
const height = itemHeights.value.get(key) ?? props.estimatedHeight
const itemEnd = currentOffset + height
if (itemEnd > visibleStart) {
startIndex = Math.max(0, i - bufferCount - extraBuffer)
break
}
currentOffset += height
}
startOffset = 0
for (let i = 0; i < startIndex; i++) {
const key = getItemKey(props.data[i], i)
startOffset += itemHeights.value.get(key) ?? props.estimatedHeight
}
currentOffset = startOffset
for (let i = startIndex; i < count; i++) {
const key = getItemKey(props.data[i], i)
const height = itemHeights.value.get(key) ?? props.estimatedHeight
if (currentOffset > visibleEnd) {
endIndex = Math.min(count - 1, i - 1 + bufferCount + extraBuffer)
break
}
currentOffset += height
endIndex = i
}
return { start: startIndex, end: endIndex, offset: startOffset }
})
const visibleItems = computed(() => {
const { start, end, offset } = visibleRange.value
const items = []
if (!props.data || props.data.length === 0) {
return []
}
const safeStart = Math.max(0, start)
const safeEnd = Math.min(props.data.length - 1, end)
if (safeStart > safeEnd) {
return []
}
let currentOffset = offset
const seenKeys = new Set()
for (let i = safeStart; i <= safeEnd; i++) {
const item = props.data[i]
if (!item) continue
const key = getItemKey(item, i)
if (seenKeys.has(key)) continue
seenKeys.add(key)
const cachedHeight = itemHeights.value.get(key)
const height = cachedHeight !== undefined ? cachedHeight : props.estimatedHeight
items.push({
item,
index: i,
key,
offset: currentOffset,
height
})
currentOffset += height
}
return items
})
const wrapperStyle = computed(() => ({
@ -194,21 +292,18 @@ const bottomPlaceholderStyle = computed(() => ({
top: 0,
width: '100%',
height: `${props.bottomPlaceholderHeight}px`,
transform: `translateY(0px)`,
zIndex: 1
}))
const getItemStyle = (renderItem) => {
return {
position: 'absolute',
left: 0,
right: 0,
top: 0,
width: '100%',
transform: `translateY(${renderItem.position}px)`,
willChange: 'transform'
}
}
const getItemStyle = (renderItem) => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
width: '100%',
transform: `translateY(${renderItem.offset}px)`,
willChange: 'transform'
})
const setItemRef = (el, key) => {
if (el) {
@ -218,182 +313,6 @@ const setItemRef = (el, key) => {
}
}
const addView = (index, item, key) => {
const nr = markRaw({
id: uid++,
index,
used: true,
key
})
const view = shallowReactive({
item,
position: 0,
nr
})
pool.value.push(view)
return view
}
const unuseView = (view) => {
view.nr.used = false
view.position = -9999
}
const getScroll = () => {
const el = renderContainerRef.value
if (!el) return { start: 0, end: 0 }
return {
start: el.scrollTop,
end: el.scrollTop + el.clientHeight
}
}
const updateVisibleItems = (checkItem = false, checkPositionDiff = false) => {
const items = props.data
const count = items.length
const sizesMap = sizes.value
const keyField = typeof props.itemKey === 'string' ? props.itemKey : 'id'
if (!count) {
pool.value = []
viewMap.clear()
totalSize.value = props.bottomPlaceholderHeight
return { continuous: true }
}
const el = renderContainerRef.value
if (!el) return { continuous: true }
const scrollHeight = el.scrollHeight
const scrollTop = el.scrollTop
const clientHeight = el.clientHeight
const visualScrollStart = scrollHeight - scrollTop - clientHeight
const visualScrollEnd = scrollHeight - scrollTop
if (checkPositionDiff) {
let positionDiff = visualScrollStart - $_lastUpdateScrollPosition
if (positionDiff < 0) positionDiff = -positionDiff
if (positionDiff < minSize.value) {
return { continuous: true }
}
}
$_lastUpdateScrollPosition = visualScrollStart
const buffer = props.buffer
let scrollStart = visualScrollStart - buffer
let scrollEnd = visualScrollEnd + buffer
scrollStart -= props.bottomPlaceholderHeight
scrollEnd += props.bottomPlaceholderHeight
scrollStart = Math.max(0, scrollStart)
let startIndex = 0
let endIndex = count - 1
let newTotalSize = 0
let a = 0
let b = count - 1
let i = ~~(count / 2)
let oldI
do {
oldI = i
const h = sizesMap[i]?.accumulator || 0
if (h < scrollStart) {
a = i
} else if (i < count - 1 && (sizesMap[i + 1]?.accumulator || 0) > scrollStart) {
b = i
}
i = ~~((a + b) / 2)
} while (i !== oldI)
i < 0 && (i = 0)
startIndex = i
newTotalSize = sizesMap[count - 1]?.accumulator || count * minSize.value
for (endIndex = i; endIndex < count && (sizesMap[endIndex]?.accumulator || 0) < scrollEnd; endIndex++);
if (endIndex === -1) {
endIndex = count - 1
} else {
endIndex++
endIndex > count && (endIndex = count)
}
totalSize.value = newTotalSize + props.bottomPlaceholderHeight
const continuous = startIndex <= $_endIndex && endIndex >= $_startIndex
if (continuous) {
for (let j = 0, l = pool.value.length; j < l; j++) {
const view = pool.value[j]
if (view.nr.used) {
if (checkItem) {
const newKey = getItemKey(view.item, view.nr.index)
let newIndex = -1
for (let k = 0; k < count; k++) {
if (getItemKey(items[k], k) === newKey) {
newIndex = k
break
}
}
view.nr.index = newIndex
}
if (
view.nr.index == null ||
view.nr.index < startIndex ||
view.nr.index >= endIndex
) {
unuseView(view)
}
}
}
}
for (let j = startIndex; j < endIndex; j++) {
const item = items[j]
if (!item) continue
const key = getItemKey(item, j)
let view = viewMap.get(key)
if (!view) {
view = addView(j, item, key)
viewMap.set(key, view)
} else {
if (!view.nr.used) {
view.nr.used = true
}
}
view.item = item
view.nr.index = j
view.nr.key = key
const prevSize = j > 0 ? sizesMap[j - 1] : { accumulator: 0 }
view.position = (prevSize?.accumulator || 0) + props.bottomPlaceholderHeight
}
pool.value = pool.value.filter(v => v.nr.used)
$_startIndex = startIndex
$_endIndex = endIndex
emit('update', startIndex, endIndex)
nextTick(() => {
observeVisibleItems()
})
return { continuous }
}
const measureItem = (key, element) => {
if (!element) return
@ -403,11 +322,11 @@ const measureItem = (key, element) => {
const height = targetElement.getBoundingClientRect().height
if (height > 0) {
const currentSize = itemSizeMap.value.get(key)
if (currentSize !== height) {
const newSizes = new Map(itemSizeMap.value)
newSizes.set(key, height)
itemSizeMap.value = newSizes
const cachedHeight = itemHeights.value.get(key)
if (cachedHeight !== height) {
const newHeights = new Map(itemHeights.value)
newHeights.set(key, height)
itemHeights.value = newHeights
}
}
}
@ -418,22 +337,12 @@ const setupResizeObserver = () => {
}
resizeObserver.value = new ResizeObserver((entries) => {
let hasChanges = false
for (const entry of entries) {
const key = entry.target.dataset.key
if (key !== undefined) {
const oldSize = itemSizeMap.value.get(key)
measureItem(key, entry.target)
const newSize = itemSizeMap.value.get(key)
if (oldSize !== newSize) {
hasChanges = true
}
}
}
if (hasChanges) {
updateVisibleItems(false)
}
})
}
@ -452,17 +361,15 @@ const handleWheel = (event) => {
}
const handleScroll = (event) => {
if (!$_scrollDirty) {
$_scrollDirty = true
requestAnimationFrame(() => {
$_scrollDirty = false
updateVisibleItems(false, true)
})
}
const target = event.target
scrollTop.value = target.scrollTop
const scrollHeight = target.scrollHeight
const currentScrollTop = target.scrollTop
const viewportHeight = target.clientHeight
const visualScrollBottom = scrollHeight - currentScrollTop - viewportHeight
scrollTop.value = visualScrollBottom
isScrolling.value = true
if (scrollTimeout.value) {
@ -474,22 +381,23 @@ const handleScroll = (event) => {
}, 150)
const st = target.scrollTop
const scrollHeight = target.scrollHeight
const clientHeight = target.clientHeight
const sh = target.scrollHeight
const ch = target.clientHeight
const distanceToContainerTop = st
const distanceToContainerBottom = scrollHeight - st - clientHeight
const distanceToContainerBottom = sh - st - ch
const distanceToPageTop = distanceToContainerBottom
const distanceToPageBottom = distanceToContainerTop
const isAtPageTop = distanceToPageTop <= 0
const isAtPageBottom = distanceToPageBottom <= 0
const threshold = 5
const isAtPageTop = distanceToPageTop <= threshold
const isAtPageBottom = distanceToPageBottom <= threshold
emit('scroll', {
target,
scrollTop: st,
scrollTop: visualScrollBottom,
scrollHeight,
clientHeight,
clientHeight: ch,
distanceToPageTop,
distanceToPageBottom,
isAtPageTop,
@ -508,20 +416,19 @@ const handleScroll = (event) => {
const scrollToIndex = (index, behavior = 'auto') => {
if (!renderContainerRef.value || index < 0 || index >= props.data.length) return
const sizesMap = sizes.value
const offset = index > 0 ? (sizesMap[index - 1]?.accumulator || 0) : 0
const position = getItemPosition(index)
const el = renderContainerRef.value
const scrollHeight = el.scrollHeight
const targetScrollTop = scrollHeight - position.offset - props.bottomPlaceholderHeight - el.clientHeight
renderContainerRef.value.scrollTo({
top: offset,
el.scrollTo({
top: targetScrollTop,
behavior
})
}
const scrollToBottom = (behavior = 'smooth') => {
if (!renderContainerRef.value) {
pendingScrollToBottom.value = true
return
}
if (!renderContainerRef.value) return
requestAnimationFrame(() => {
if (!renderContainerRef.value) return
@ -550,33 +457,26 @@ const scrollToTop = (behavior = 'smooth') => {
const getScrollElement = () => renderContainerRef.value
const getVisibleIndices = () => {
const indices = []
for (let i = $_startIndex; i < $_endIndex; i++) {
indices.push(i)
}
return indices
const { start, end } = visibleRange.value
return Array.from({ length: end - start + 1 }, (_, i) => start + i)
}
const resetMeasurements = () => {
itemSizeMap.value = new Map()
itemHeights.value = new Map()
itemRefs.clear()
pool.value = []
viewMap.clear()
scrollTop.value = 0
$_startIndex = 0
$_endIndex = 0
}
const isAtPageBottom = () => {
if (!renderContainerRef.value) return false
const { scrollTop } = renderContainerRef.value
return scrollTop <= 0
return scrollTop <= 5
}
const isAtPageTop = () => {
if (!renderContainerRef.value) return false
const { scrollTop, scrollHeight, clientHeight } = renderContainerRef.value
return scrollHeight - scrollTop - clientHeight <= 0
return scrollHeight - scrollTop - clientHeight <= 5
}
const observeVisibleItems = () => {
@ -591,34 +491,62 @@ const observeVisibleItems = () => {
}
}
watch(() => props.data, () => {
itemSizeMap.value = new Map()
pool.value = []
viewMap.clear()
itemRefs.clear()
$_startIndex = 0
$_endIndex = 0
watch(() => props.data, (newData, oldData) => {
const newLength = newData?.length || 0
const oldLength = oldData?.length || 0
nextTick(() => {
updateVisibleItems(true)
})
if (newLength > oldLength) {
nextTick(() => {
observeVisibleItems()
})
} else if (newLength < oldLength) {
itemHeights.value = new Map()
itemRefs.clear()
nextTick(() => {
observeVisibleItems()
})
} else {
const oldKeys = new Set()
const newKeys = new Set()
for (let i = 0; i < oldLength; i++) {
oldKeys.add(getItemKey(oldData[i], i))
}
for (let i = 0; i < newLength; i++) {
newKeys.add(getItemKey(newData[i], i))
}
let hasChanges = false
for (const key of newKeys) {
if (!oldKeys.has(key)) {
hasChanges = true
break
}
}
if (hasChanges) {
itemHeights.value = new Map()
itemRefs.clear()
nextTick(() => {
observeVisibleItems()
})
}
}
}, { deep: true })
watch(sizes, () => {
updateVisibleItems(false)
watch(visibleItems, () => {
nextTick(() => {
observeVisibleItems()
})
}, { deep: true })
onMounted(() => {
setupResizeObserver()
isInitialized.value = true
nextTick(() => {
if (pendingScrollToBottom.value) {
pendingScrollToBottom.value = false
scrollToBottom()
}
updateVisibleItems(true)
observeVisibleItems()
})
})
@ -632,7 +560,6 @@ onBeforeUnmount(() => {
}
itemRefs.clear()
viewMap.clear()
})
defineExpose({
@ -654,12 +581,6 @@ defineExpose({
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
.virtual-scroller-wrapper {
contain: content;
}
@ -669,12 +590,14 @@ defineExpose({
width: 100%;
}
.virtual-scroller-placeholder {
width: 100%;
}
.virtual-scroller-render-container {
contain: layout style;
&::-webkit-scrollbar {
// transform: rotate(180deg);
display: none;
width: 0;
height: 0;
}
}
.virtual-scroller-item {

View File

@ -136,7 +136,6 @@ export async function generate(type, data) {
socket.onclose = async (event) => {
console.log('WebSocket已关闭:', event)
useDisplay.isSubGerenate = false
// 清理心跳定时器
if (heartbeatInterval) {
clearInterval(heartbeatInterval)
}
@ -160,8 +159,6 @@ export async function generate(type, data) {
} else {
websocketError(event.code, event.reason)
}
// clearInterval(progressInterval.value)
// 清理心跳定时器
if (heartbeatInterval) {
clearInterval(heartbeatInterval)
}

View File

@ -51,14 +51,14 @@
<!-- 已完成 -->
<div v-if="props.item.status === 'success'" class="box success-box">
<div v-for="(file, index) in props.item.files" :key="index" class="one-box">
<div v-for="(file, index) in props.item.files" :key="index" class="one-box" :class="{ 'collected': isCollected(file) }" @mouseenter="hoverIndex = index" @mouseleave="hoverIndex = -1">
<!-- <img :src="file" alt="index" class="img" /> -->
<Img :src="file" alt="index" class="img" />
<div class="left-top">
<div class="left-top-btn" @click="downloadImage(file, 'image')"><img src="@/assets/display/download.svg" /></div>
<span class="line" />
<div class="left-top-btn" @click="addCollection(file)"><img src="@/assets/display/collection.svg" /></div>
<div v-show="hoverIndex === index" class="left-top-btn download-btn" @click="downloadImage(file, 'image')"><img src="@/assets/display/download.svg" /></div>
<span v-if="hoverIndex === index" class="line" />
<div class="left-top-btn collect-btn" @click="addCollection(file)"><img :src="isCollected(file) ? collectionActiveIcon : collectionIcon" /></div>
</div>
<el-tooltip
@ -86,6 +86,8 @@
<script setup>
import brush from '@/assets/display/brush.svg'
import collectionIcon from '@/assets/display/collection.svg'
import collectionActiveIcon from '@/assets/display/collection-active.svg'
import { useDisplayStore, useParamStore, useUserStore } from '@/stores'
import { downloadImage } from '@/utils/downloadImage.js'
import reEditIcon from '@/assets/display/reEdit.svg'
@ -107,6 +109,13 @@ const useDisplay = useDisplayStore()
const useParams = useParamStore()
const useUser = useUserStore()
const localCollectStatus = ref({ ...props.item.collectStatus })
const hoverIndex = ref(-1)
const isCollected = (url) => {
return localCollectStatus.value[url] === true
}
const generateStatusText = computed(() => {
if (props.item.status === 'generate') {
return '正在生成中...'
@ -161,6 +170,7 @@ const addCollection = async (url) => {
})
if (res.success) {
ElMessage.success(res.message || '操作成功')
localCollectStatus.value[url] = !localCollectStatus.value[url]
} else {
ElMessage.error(res.message || '操作失败')
}
@ -346,6 +356,11 @@ const addCollection = async (url) => {
display:flex
}
}
.one-box.collected{
.left-top{
display:flex
}
}
.success-box{
display: grid;
grid-template-columns: repeat(4, 1fr);

View File

@ -143,7 +143,8 @@ const conversion = (newlist) => {
prompt: item.prompt,
params: item.params,
time: item.createTime,
files: files
files: files,
collectStatus: item.collectStatus || {}
}
})
return temp

View File

@ -10,33 +10,10 @@ const loading = computed(() => route.query.loading ? false : (route.path === '/h
const Generate = computed(() => route.query.Generate || false)
const type = computed(() => route.query.type || 'painting')
console.log(type.value)
const canvasVisible = ref(false)
const canvasImage = ref('')
const canvasReferenceImages = ref([])
const canvasSource = ref('')
const handleOpenCanvas = (data) => {
canvasImage.value = data.mainImage?.url || data.mainImage || ''
canvasReferenceImages.value = data.referenceImages || []
canvasSource.value = data.source || 'uploader'
canvasVisible.value = true
}
const handleCanvasSend = (data) => {
console.log('Canvas send:', data)
}
</script>
<template>
<div class="app-container">
<Canvas
v-model:visible="canvasVisible"
:image="canvasImage"
:reference-images="canvasReferenceImages"
:source="canvasSource"
@send="handleCanvasSend"
/>
<dialogBox :is-generate="shouldShowDisplay" :type="type" :generate="Generate" :loading="loading" @open-canvas="handleOpenCanvas" />
<display :if="shouldShowDisplay" :type="type" :loading="loading" />