chore: 代码格式化统一(空格、换行、属性排序、LF规范化)
This commit is contained in:
parent
b964c826ce
commit
e98ff3a2c4
@ -7,7 +7,8 @@
|
|||||||
"Bash(git -C \"D:/WebUI/Kexue/操作平台/AI_Painting_V2.0\" log --oneline --all -- src/stores/display.js)",
|
"Bash(git -C \"D:/WebUI/Kexue/操作平台/AI_Painting_V2.0\" log --oneline --all -- src/stores/display.js)",
|
||||||
"Bash(git -C \"D:/WebUI/Kexue/操作平台/AI_Painting_V2.0\" log --oneline --all -- src/components/dialogBox/index.vue)",
|
"Bash(git -C \"D:/WebUI/Kexue/操作平台/AI_Painting_V2.0\" log --oneline --all -- src/components/dialogBox/index.vue)",
|
||||||
"Bash(git -C \"D:/WebUI/Kexue/操作平台/AI_Painting_V2.0\" log --all --oneline --follow -p -- src/stores/display.js)",
|
"Bash(git -C \"D:/WebUI/Kexue/操作平台/AI_Painting_V2.0\" log --all --oneline --follow -p -- src/stores/display.js)",
|
||||||
"Bash(git -C \"D:/WebUI/Kexue/操作平台/AI_Painting_V2.0\" log --all --oneline -p -- src/components/dialogBox/index.vue)"
|
"Bash(git -C \"D:/WebUI/Kexue/操作平台/AI_Painting_V2.0\" log --all --oneline -p -- src/components/dialogBox/index.vue)",
|
||||||
|
"Bash(npx eslint *)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,5 +43,5 @@ export const getUserInfo = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const checkUsertoken = () => {
|
export const checkUsertoken = () => {
|
||||||
return service.post(`/login/validateToken`)
|
return service.post('/login/validateToken')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="custom-popover" ref="popoverRef">
|
<div ref="popoverRef" class="custom-popover">
|
||||||
<div class="popover-trigger" ref="triggerRef" @click.stop="togglePopover">
|
<div ref="triggerRef" class="popover-trigger" @click.stop="togglePopover">
|
||||||
<slot name="reference" />
|
<slot name="reference" />
|
||||||
</div>
|
</div>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
@ -143,10 +143,10 @@ const handleClickOutside = (e) => {
|
|||||||
const contentEl = contentRef.value
|
const contentEl = contentRef.value
|
||||||
|
|
||||||
if (
|
if (
|
||||||
triggerEl &&
|
triggerEl
|
||||||
!triggerEl.contains(e.target) &&
|
&& !triggerEl.contains(e.target)
|
||||||
contentEl &&
|
&& contentEl
|
||||||
!contentEl.contains(e.target)
|
&& !contentEl.contains(e.target)
|
||||||
) {
|
) {
|
||||||
visible.value = false
|
visible.value = false
|
||||||
window.__currentOpenPopoverId__ = null
|
window.__currentOpenPopoverId__ = null
|
||||||
|
|||||||
@ -32,8 +32,8 @@
|
|||||||
ref="editableDivRef"
|
ref="editableDivRef"
|
||||||
contenteditable="true"
|
contenteditable="true"
|
||||||
class="custom-textarea"
|
class="custom-textarea"
|
||||||
@input="handleInput"
|
|
||||||
:data-placeholder="!inputText ? '请输入提示词或使用圆形/矩形工具' : ''"
|
:data-placeholder="!inputText ? '请输入提示词或使用圆形/矩形工具' : ''"
|
||||||
|
@input="handleInput"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -114,8 +114,8 @@
|
|||||||
ref="brushTextareaRef"
|
ref="brushTextareaRef"
|
||||||
contenteditable="true"
|
contenteditable="true"
|
||||||
class="brush-textarea"
|
class="brush-textarea"
|
||||||
@input="handleBrushInput"
|
|
||||||
:data-placeholder="!currentShapeDescription ? '请输入描述...' : ''"
|
:data-placeholder="!currentShapeDescription ? '请输入描述...' : ''"
|
||||||
|
@input="handleBrushInput"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -160,10 +160,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { generate } from '@/utils/taskPolling'
|
|
||||||
import { useDisplayStore } from '@/stores'
|
import { useDisplayStore } from '@/stores'
|
||||||
import request from '@/utils/request'
|
|
||||||
import { getModelId } from '@/utils/modelApi'
|
import { getModelId } from '@/utils/modelApi'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import { generate } from '@/utils/taskPolling'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@ -268,7 +268,7 @@ watch(() => props.visible, (newVal) => {
|
|||||||
historyIndex.value = -1
|
historyIndex.value = -1
|
||||||
promptHistory.value = []
|
promptHistory.value = []
|
||||||
promptHistoryIndex.value = -1
|
promptHistoryIndex.value = -1
|
||||||
allReferenceImages.value = props.referenceImages.map(img => img.url || img)
|
allReferenceImages.value = props.referenceImages.map((img) => img.url || img)
|
||||||
brushPanelVisible.value = false
|
brushPanelVisible.value = false
|
||||||
isPanelOpen.value = false
|
isPanelOpen.value = false
|
||||||
currentShapeDescription.value = ''
|
currentShapeDescription.value = ''
|
||||||
@ -310,8 +310,8 @@ const initCanvas = () => {
|
|||||||
img.src = imageUrl
|
img.src = imageUrl
|
||||||
} else {
|
} else {
|
||||||
fetch(imageUrl)
|
fetch(imageUrl)
|
||||||
.then(res => res.blob())
|
.then((res) => res.blob())
|
||||||
.then(blob => {
|
.then((blob) => {
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
const imgScale = Math.min(containerWidth / img.width, containerHeight / img.height)
|
const imgScale = Math.min(containerWidth / img.width, containerHeight / img.height)
|
||||||
@ -378,7 +378,7 @@ const handleMouseMove = (e) => {
|
|||||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||||
ctx.drawImage(bgImage.value, 0, 0)
|
ctx.drawImage(bgImage.value, 0, 0)
|
||||||
|
|
||||||
shapes.value.forEach(shape => {
|
shapes.value.forEach((shape) => {
|
||||||
drawShape(ctx, shape)
|
drawShape(ctx, shape)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -396,7 +396,7 @@ const handleMouseMove = (e) => {
|
|||||||
ctx.fillStyle = '#fff'
|
ctx.fillStyle = '#fff'
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
|
|
||||||
shapes.value.forEach(shape => {
|
shapes.value.forEach((shape) => {
|
||||||
drawShape(ctx, shape)
|
drawShape(ctx, shape)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -431,8 +431,8 @@ const handleMouseUp = (e) => {
|
|||||||
type: currentShape.value,
|
type: currentShape.value,
|
||||||
startX: startX.value,
|
startX: startX.value,
|
||||||
startY: startY.value,
|
startY: startY.value,
|
||||||
endX: endX,
|
endX,
|
||||||
endY: endY,
|
endY,
|
||||||
color: shapeColors[colorIndex],
|
color: shapeColors[colorIndex],
|
||||||
description: '',
|
description: '',
|
||||||
referenceImages: []
|
referenceImages: []
|
||||||
@ -515,7 +515,7 @@ const redrawCanvas = () => {
|
|||||||
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
shapes.value.forEach(shape => {
|
shapes.value.forEach((shape) => {
|
||||||
drawShape(ctx, shape)
|
drawShape(ctx, shape)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -530,7 +530,7 @@ const drawShape = (ctx, shape) => {
|
|||||||
ctx.strokeRect(shape.startX, shape.startY, width, height)
|
ctx.strokeRect(shape.startX, shape.startY, width, height)
|
||||||
} else if (shape.type === 'circle') {
|
} else if (shape.type === 'circle') {
|
||||||
const radius = Math.sqrt(
|
const radius = Math.sqrt(
|
||||||
Math.pow(shape.endX - shape.startX, 2) + Math.pow(shape.endY - shape.startY, 2)
|
(shape.endX - shape.startX) ** 2 + (shape.endY - shape.startY) ** 2
|
||||||
)
|
)
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.arc(shape.startX, shape.startY, radius, 0, Math.PI * 2)
|
ctx.arc(shape.startX, shape.startY, radius, 0, Math.PI * 2)
|
||||||
@ -743,9 +743,9 @@ const handleSend = async () => {
|
|||||||
modelId,
|
modelId,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
params: [
|
params: [
|
||||||
{ name: 'prompt', data: inputText.value + '并且去除掉图1中的框' },
|
{ name: 'prompt', data: `${inputText.value}并且去除掉图1中的框` },
|
||||||
{ name: 'index', data: 1 },
|
{ name: 'index', data: 1 },
|
||||||
{ name: 'proportion', data: proportion?.aspectRatio || '4:3' },
|
{ name: 'proportion', data: proportion?.aspectRatio || '4:3' }
|
||||||
],
|
],
|
||||||
imgs: uploadedImgs,
|
imgs: uploadedImgs,
|
||||||
request: JSON.stringify(generateData)
|
request: JSON.stringify(generateData)
|
||||||
|
|||||||
@ -11,8 +11,8 @@
|
|||||||
<div v-show="showUploader" class="upload-img-container">
|
<div v-show="showUploader" class="upload-img-container">
|
||||||
<div class="reference-diagram">
|
<div class="reference-diagram">
|
||||||
<component
|
<component
|
||||||
v-if="platform.ImageUploader"
|
|
||||||
:is="platform.ImageUploader"
|
:is="platform.ImageUploader"
|
||||||
|
v-if="platform.ImageUploader"
|
||||||
ref="referenceDiagramRef"
|
ref="referenceDiagramRef"
|
||||||
v-model="referenceImages"
|
v-model="referenceImages"
|
||||||
v-bind="uploaderBindings"
|
v-bind="uploaderBindings"
|
||||||
@ -34,11 +34,11 @@
|
|||||||
<div class="prefix-self-wrap">
|
<div class="prefix-self-wrap">
|
||||||
<component
|
<component
|
||||||
:is="platform.ModelSelector"
|
:is="platform.ModelSelector"
|
||||||
:modelValue="platform.model.value"
|
:model-value="platform.model.value"
|
||||||
@update:modelValue="platform.model.value = $event"
|
:type-value="platform.modelType.value"
|
||||||
:typeValue="platform.modelType.value"
|
|
||||||
@update:typeValue="platform.modelType.value = $event"
|
|
||||||
v-bind="(platform.modelSelectorProps && platform.modelSelectorProps()) || {}"
|
v-bind="(platform.modelSelectorProps && platform.modelSelectorProps()) || {}"
|
||||||
|
@update:model-value="platform.model.value = $event"
|
||||||
|
@update:type-value="platform.modelType.value = $event"
|
||||||
/>
|
/>
|
||||||
<template v-for="ctrl in visibleControls" :key="ctrl.name">
|
<template v-for="ctrl in visibleControls" :key="ctrl.name">
|
||||||
<component
|
<component
|
||||||
@ -62,18 +62,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Sender>
|
</Sender>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Sender } from 'vue-element-plus-x'
|
import { Sender } from 'vue-element-plus-x'
|
||||||
import { useDisplayStore } from '@/stores'
|
|
||||||
import { generate } from '@/utils/taskPolling'
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { createPlatform } from '@/platforms/registry.js'
|
import { createPlatform } from '@/platforms/registry.js'
|
||||||
|
import { useDisplayStore } from '@/stores'
|
||||||
import { getModelId } from '@/utils/modelApi'
|
import { getModelId } from '@/utils/modelApi'
|
||||||
|
import { generate } from '@/utils/taskPolling'
|
||||||
|
|
||||||
// 确保平台包被加载(触发自注册)
|
// 确保平台包被加载(触发自注册)
|
||||||
import '@/platforms/painting/index.js'
|
import '@/platforms/painting/index.js'
|
||||||
@ -82,7 +81,7 @@ import '@/platforms/video/index.js'
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isGenerate: { type: Boolean, default: false },
|
isGenerate: { type: Boolean, default: false },
|
||||||
generate: { type: Boolean, default: false },
|
generate: { type: Boolean, default: false },
|
||||||
type: { type: String, default: 'Painting' },
|
type: { type: String, default: 'Painting' }
|
||||||
})
|
})
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -99,7 +98,7 @@ const getCurrentConfig = () => {
|
|||||||
|
|
||||||
const visibleControls = computed(() => {
|
const visibleControls = computed(() => {
|
||||||
const config = getCurrentConfig()
|
const config = getCurrentConfig()
|
||||||
return platform.value.controls.filter(c => c.show(config))
|
return platform.value.controls.filter((c) => c.show(config))
|
||||||
})
|
})
|
||||||
|
|
||||||
const showUploader = computed(() => {
|
const showUploader = computed(() => {
|
||||||
@ -148,7 +147,7 @@ const handleStart = async () => {
|
|||||||
modelType: p.modelType.value,
|
modelType: p.modelType.value,
|
||||||
prompt: prompt.value,
|
prompt: prompt.value,
|
||||||
referenceImages: referenceImages.value,
|
referenceImages: referenceImages.value,
|
||||||
modelParams: body,
|
modelParams: body
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
@ -157,7 +156,7 @@ const handleStart = async () => {
|
|||||||
modelName: p.model.value,
|
modelName: p.model.value,
|
||||||
modelId: modelId || '',
|
modelId: modelId || '',
|
||||||
body,
|
body,
|
||||||
request: JSON.stringify(generateData),
|
request: JSON.stringify(generateData)
|
||||||
}
|
}
|
||||||
|
|
||||||
await generate(data, generateData)
|
await generate(data, generateData)
|
||||||
@ -190,7 +189,7 @@ watch(
|
|||||||
async ([newModel, newModelType]) => {
|
async ([newModel, newModelType]) => {
|
||||||
if (!newModel) return
|
if (!newModel) return
|
||||||
await platform.value.loadConfig(newModel, newModelType)
|
await platform.value.loadConfig(newModel, newModelType)
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 平台切换 → 设置默认模型 + 预加载模型列表
|
// 平台切换 → 设置默认模型 + 预加载模型列表
|
||||||
|
|||||||
@ -100,6 +100,8 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['scroll', 'scroll-start', 'scroll-end', 'visible-change'])
|
||||||
|
|
||||||
const computedData = computed(() => {
|
const computedData = computed(() => {
|
||||||
return props.data.length > 0 ? props.data : props.items
|
return props.data.length > 0 ? props.data : props.items
|
||||||
})
|
})
|
||||||
@ -114,8 +116,6 @@ const computedBuffer = computed(() => {
|
|||||||
return props.buffer !== 3 ? props.buffer : props.bufferSize
|
return props.buffer !== 3 ? props.buffer : props.bufferSize
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['scroll', 'scroll-start', 'scroll-end', 'visible-change'])
|
|
||||||
|
|
||||||
const containerRef = ref(null)
|
const containerRef = ref(null)
|
||||||
const wrapperRef = ref(null)
|
const wrapperRef = ref(null)
|
||||||
const renderContainerRef = ref(null)
|
const renderContainerRef = ref(null)
|
||||||
@ -275,7 +275,7 @@ const bottomPlaceholderStyle = computed(() => ({
|
|||||||
top: 0,
|
top: 0,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: `${props.bottomPlaceholderHeight}px`,
|
height: `${props.bottomPlaceholderHeight}px`,
|
||||||
transform: `translateY(0px)`,
|
transform: 'translateY(0px)',
|
||||||
zIndex: 1
|
zIndex: 1
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ const setupResizeObserver = () => {
|
|||||||
|
|
||||||
resizeObserver.value = new ResizeObserver((entries) => {
|
resizeObserver.value = new ResizeObserver((entries) => {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const index = parseInt(entry.target.dataset.index, 10)
|
const index = Number.parseInt(entry.target.dataset.index, 10)
|
||||||
if (!isNaN(index)) {
|
if (!isNaN(index)) {
|
||||||
measureItem(index, entry.target)
|
measureItem(index, entry.target)
|
||||||
}
|
}
|
||||||
@ -529,7 +529,7 @@ const cleanupExtraItems = (currentVisibleItems) => {
|
|||||||
if (!renderContainerRef.value || !currentVisibleItems.length) return
|
if (!renderContainerRef.value || !currentVisibleItems.length) return
|
||||||
|
|
||||||
// 构建当前应该可见的索引集合
|
// 构建当前应该可见的索引集合
|
||||||
const visibleIndices = new Set(currentVisibleItems.map(item => item.index))
|
const visibleIndices = new Set(currentVisibleItems.map((item) => item.index))
|
||||||
|
|
||||||
// 直接获取 render-container 内所有实际渲染的 .virtual-scroller-item 元素
|
// 直接获取 render-container 内所有实际渲染的 .virtual-scroller-item 元素
|
||||||
const renderedItems = renderContainerRef.value.querySelectorAll('.virtual-scroller-item')
|
const renderedItems = renderContainerRef.value.querySelectorAll('.virtual-scroller-item')
|
||||||
@ -537,7 +537,7 @@ const cleanupExtraItems = (currentVisibleItems) => {
|
|||||||
const toRemove = []
|
const toRemove = []
|
||||||
|
|
||||||
for (const el of renderedItems) {
|
for (const el of renderedItems) {
|
||||||
const dataIndex = parseInt(el.getAttribute('data-index'), 10)
|
const dataIndex = Number.parseInt(el.getAttribute('data-index'), 10)
|
||||||
|
|
||||||
// 如果元素的 data-index 不在可见范围内,标记为删除
|
// 如果元素的 data-index 不在可见范围内,标记为删除
|
||||||
if (!isNaN(dataIndex) && !visibleIndices.has(dataIndex)) {
|
if (!isNaN(dataIndex) && !visibleIndices.has(dataIndex)) {
|
||||||
@ -551,7 +551,7 @@ const cleanupExtraItems = (currentVisibleItems) => {
|
|||||||
el.parentNode.removeChild(el)
|
el.parentNode.removeChild(el)
|
||||||
}
|
}
|
||||||
// 同步清理 itemRefs Map
|
// 同步清理 itemRefs Map
|
||||||
const index = parseInt(el.getAttribute('data-index'), 10)
|
const index = Number.parseInt(el.getAttribute('data-index'), 10)
|
||||||
if (!isNaN(index)) {
|
if (!isNaN(index)) {
|
||||||
itemRefs.delete(index)
|
itemRefs.delete(index)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,6 +99,8 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['scroll', 'scroll-start', 'scroll-end', 'visible-change'])
|
||||||
|
|
||||||
const computedData = computed(() => {
|
const computedData = computed(() => {
|
||||||
return props.data.length > 0 ? props.data : props.items
|
return props.data.length > 0 ? props.data : props.items
|
||||||
})
|
})
|
||||||
@ -113,8 +115,6 @@ const computedBuffer = computed(() => {
|
|||||||
return props.buffer !== 3 ? props.buffer : props.bufferSize
|
return props.buffer !== 3 ? props.buffer : props.bufferSize
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['scroll', 'scroll-start', 'scroll-end', 'visible-change'])
|
|
||||||
|
|
||||||
const containerRef = ref(null)
|
const containerRef = ref(null)
|
||||||
const wrapperRef = ref(null)
|
const wrapperRef = ref(null)
|
||||||
const renderContainerRef = ref(null)
|
const renderContainerRef = ref(null)
|
||||||
@ -269,7 +269,7 @@ const bottomPlaceholderStyle = computed(() => ({
|
|||||||
top: 0,
|
top: 0,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: `${props.bottomPlaceholderHeight}px`,
|
height: `${props.bottomPlaceholderHeight}px`,
|
||||||
transform: `translateY(0px)`,
|
transform: 'translateY(0px)',
|
||||||
zIndex: 1
|
zIndex: 1
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -347,7 +347,7 @@ const setupResizeObserver = () => {
|
|||||||
|
|
||||||
resizeObserver.value = new ResizeObserver((entries) => {
|
resizeObserver.value = new ResizeObserver((entries) => {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const index = parseInt(entry.target.dataset.index, 10)
|
const index = Number.parseInt(entry.target.dataset.index, 10)
|
||||||
if (!isNaN(index)) {
|
if (!isNaN(index)) {
|
||||||
measureItem(index, entry.target)
|
measureItem(index, entry.target)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,194 +1,194 @@
|
|||||||
window.TEST_DATA = [
|
window.TEST_DATA = [
|
||||||
{
|
{
|
||||||
"id": "839217090555557410",
|
id: '839217090555557410',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/27/69ef43d23888d39e4ed1d062.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/27/69ef43d23888d39e4ed1d062.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "839211834861958673",
|
id: '839211834861958673',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/27/69ef3eed3888d39e4ed1d061.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/27/69ef3eed3888d39e4ed1d061.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "BananaPro"
|
model: 'BananaPro'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "839209605929121287",
|
id: '839209605929121287',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/27/69ef3cd93888d39e4ed1d060.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/27/69ef3cd93888d39e4ed1d060.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "837370053866304564",
|
id: '837370053866304564',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/22/69e88ba23888d39e4ed1d05c.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/22/69e88ba23888d39e4ed1d05c.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "837360015437214709",
|
id: '837360015437214709',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/22/69e882493888d39e4ed1d05b.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/22/69e882493888d39e4ed1d05b.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "836534979084169461",
|
id: '836534979084169461',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/20/69e581e93888d39e4ed1d025.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/20/69e581e93888d39e4ed1d025.png',
|
||||||
"prompt": "<div data-v-43afc57f=\"\" class=\"prompt-container\" style=\"width: 1118px; font-size: 14px; background-color: rgb(255, 255, 255); height: 39px;\"><div data-v-43afc57f=\"\" class=\"prompt-wrapper\" style=\"width: 1118px;\"><div data-v-43afc57f=\"\" class=\"prompt expanded\" style=\"max-height: none; overflow: visible;\"><span data-v-43afc57f=\"\" class=\"prompt-text\">将图1红色框内的【苹果】替换为【火龙果】</span></div><div><span data-v-43afc57f=\"\" class=\"prompt-text\"><br></span></div></div></div><div data-v-43afc57f=\"\" class=\"box success-box\" style=\"width: 1118px; font-size: 14px; background-color: rgb(255, 255, 255);\"><div data-v-43afc57f=\"\" class=\"one-box\"></div></div>",
|
prompt: '<div data-v-43afc57f="" class="prompt-container" style="width: 1118px; font-size: 14px; background-color: rgb(255, 255, 255); height: 39px;"><div data-v-43afc57f="" class="prompt-wrapper" style="width: 1118px;"><div data-v-43afc57f="" class="prompt expanded" style="max-height: none; overflow: visible;"><span data-v-43afc57f="" class="prompt-text">将图1红色框内的【苹果】替换为【火龙果】</span></div><div><span data-v-43afc57f="" class="prompt-text"><br></span></div></div></div><div data-v-43afc57f="" class="box success-box" style="width: 1118px; font-size: 14px; background-color: rgb(255, 255, 255);"><div data-v-43afc57f="" class="one-box"></div></div>',
|
||||||
"model": "banana"
|
model: 'banana'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "835464458670191734",
|
id: '835464458670191734',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/17/69e19ce93888d39e4ed1cfaf.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/17/69e19ce93888d39e4ed1cfaf.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "835463648116749398",
|
id: '835463648116749398',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/17/69e19c273888d39e4ed1cfac.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/17/69e19c273888d39e4ed1cfac.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "835463392293565520",
|
id: '835463392293565520',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/17/69e19bea3888d39e4ed1cfab.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/17/69e19bea3888d39e4ed1cfab.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "832562717234575283",
|
id: '832562717234575283',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf66.png,http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf67.png,http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf68.png,http://test.xueai.art/file/2026/4/9/69d70e743888d39e4ed1cf69.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf66.png,http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf67.png,http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf68.png,http://test.xueai.art/file/2026/4/9/69d70e743888d39e4ed1cf69.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "839217090555557410",
|
id: '839217090555557410',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/27/69ef43d23888d39e4ed1d062.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/27/69ef43d23888d39e4ed1d062.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "839211834861958673",
|
id: '839211834861958673',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/27/69ef3eed3888d39e4ed1d061.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/27/69ef3eed3888d39e4ed1d061.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "BananaPro"
|
model: 'BananaPro'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "839209605929121287",
|
id: '839209605929121287',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/27/69ef3cd93888d39e4ed1d060.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/27/69ef3cd93888d39e4ed1d060.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "837370053866304564",
|
id: '837370053866304564',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/22/69e88ba23888d39e4ed1d05c.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/22/69e88ba23888d39e4ed1d05c.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "837360015437214709",
|
id: '837360015437214709',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/22/69e882493888d39e4ed1d05b.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/22/69e882493888d39e4ed1d05b.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "836534979084169461",
|
id: '836534979084169461',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/20/69e581e93888d39e4ed1d025.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/20/69e581e93888d39e4ed1d025.png',
|
||||||
"prompt": "<div data-v-43afc57f=\"\" class=\"prompt-container\" style=\"width: 1118px; font-size: 14px; background-color: rgb(255, 255, 255); height: 39px;\"><div data-v-43afc57f=\"\" class=\"prompt-wrapper\" style=\"width: 1118px;\"><div data-v-43afc57f=\"\" class=\"prompt expanded\" style=\"max-height: none; overflow: visible;\"><span data-v-43afc57f=\"\" class=\"prompt-text\">将图1红色框内的【苹果】替换为【火龙果】</span></div><div><span data-v-43afc57f=\"\" class=\"prompt-text\"><br></span></div></div></div><div data-v-43afc57f=\"\" class=\"box success-box\" style=\"width: 1118px; font-size: 14px; background-color: rgb(255, 255, 255);\"><div data-v-43afc57f=\"\" class=\"one-box\"></div></div>",
|
prompt: '<div data-v-43afc57f="" class="prompt-container" style="width: 1118px; font-size: 14px; background-color: rgb(255, 255, 255); height: 39px;"><div data-v-43afc57f="" class="prompt-wrapper" style="width: 1118px;"><div data-v-43afc57f="" class="prompt expanded" style="max-height: none; overflow: visible;"><span data-v-43afc57f="" class="prompt-text">将图1红色框内的【苹果】替换为【火龙果】</span></div><div><span data-v-43afc57f="" class="prompt-text"><br></span></div></div></div><div data-v-43afc57f="" class="box success-box" style="width: 1118px; font-size: 14px; background-color: rgb(255, 255, 255);"><div data-v-43afc57f="" class="one-box"></div></div>',
|
||||||
"model": "banana"
|
model: 'banana'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "835464458670191734",
|
id: '835464458670191734',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/17/69e19ce93888d39e4ed1cfaf.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/17/69e19ce93888d39e4ed1cfaf.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "835463648116749398",
|
id: '835463648116749398',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/17/69e19c273888d39e4ed1cfac.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/17/69e19c273888d39e4ed1cfac.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "835463392293565520",
|
id: '835463392293565520',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/17/69e19bea3888d39e4ed1cfab.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/17/69e19bea3888d39e4ed1cfab.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "832562717234575283",
|
id: '832562717234575283',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf66.png,http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf67.png,http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf68.png,http://test.xueai.art/file/2026/4/9/69d70e743888d39e4ed1cf69.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf66.png,http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf67.png,http://test.xueai.art/file/2026/4/9/69d70e733888d39e4ed1cf68.png,http://test.xueai.art/file/2026/4/9/69d70e743888d39e4ed1cf69.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "830138209152283808",
|
id: '830138209152283808',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/2/69ce3c743888d39e4ed1cf4f.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/2/69ce3c743888d39e4ed1cf4f.png',
|
||||||
"prompt": "将图1红色框内的【苹果】替换为【火龙果】",
|
prompt: '将图1红色框内的【苹果】替换为【火龙果】',
|
||||||
"model": "banana"
|
model: 'banana'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "830136945106498711",
|
id: '830136945106498711',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/2/69ce3b463888d39e4ed1cf4e.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/2/69ce3b463888d39e4ed1cf4e.png',
|
||||||
"prompt": "一个女孩在树下吃苹果",
|
prompt: '一个女孩在树下吃苹果',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "830083758811001839",
|
id: '830083758811001839',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/4/2/69ce09be3888d39e4ed1cf48.png",
|
fileUrl: 'http://test.xueai.art/file/2026/4/2/69ce09be3888d39e4ed1cf48.png',
|
||||||
"prompt": "一个女孩在树下吃苹果",
|
prompt: '一个女孩在树下吃苹果',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829393290267734386",
|
id: '829393290267734386',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/31/69cb86b13888d39e4ed1cf32.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/31/69cb86b13888d39e4ed1cf32.png',
|
||||||
"prompt": "将图1红色框内的【女孩】替换为【图2中的女孩】",
|
prompt: '将图1红色框内的【女孩】替换为【图2中的女孩】',
|
||||||
"model": "banana"
|
model: 'banana'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829389466203337022",
|
id: '829389466203337022',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/31/69cb83223888d39e4ed1cf31.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/31/69cb83223888d39e4ed1cf31.png',
|
||||||
"prompt": "将图1红色框内的【<span style=\"font-size: 14px;\">女孩</span>】替换为【图2中的女孩】",
|
prompt: '将图1红色框内的【<span style="font-size: 14px;">女孩</span>】替换为【图2中的女孩】',
|
||||||
"model": "banana"
|
model: 'banana'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829388114303660338",
|
id: '829388114303660338',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/31/69cb81df3888d39e4ed1cf30.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/31/69cb81df3888d39e4ed1cf30.png',
|
||||||
"prompt": "将图1红色框内的【女孩】替换为【男孩】",
|
prompt: '将图1红色框内的【女孩】替换为【男孩】',
|
||||||
"model": "banana"
|
model: 'banana'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829381253919682782",
|
id: '829381253919682782',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/31/69cb7b7c3888d39e4ed1cf2d.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/31/69cb7b7c3888d39e4ed1cf2d.png',
|
||||||
"prompt": "将图1红色框内的【女孩】替换为【图2中的男孩】",
|
prompt: '将图1红色框内的【女孩】替换为【图2中的男孩】',
|
||||||
"model": "banana"
|
model: 'banana'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829324561060212843",
|
id: '829324561060212843',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/31/69cb46af3888d39e4ed1cf29.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/31/69cb46af3888d39e4ed1cf29.png',
|
||||||
"prompt": "一个女孩在树下吃苹果",
|
prompt: '一个女孩在树下吃苹果',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829319226454978647",
|
id: '829319226454978647',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/31/69cb41b73888d39e4ed1cf25.png,http://test.xueai.art/file/2026/3/31/69cb41b73888d39e4ed1cf26.png,http://test.xueai.art/file/2026/3/31/69cb41b73888d39e4ed1cf27.png,http://test.xueai.art/file/2026/3/31/69cb41b73888d39e4ed1cf28.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/31/69cb41b73888d39e4ed1cf25.png,http://test.xueai.art/file/2026/3/31/69cb41b73888d39e4ed1cf26.png,http://test.xueai.art/file/2026/3/31/69cb41b73888d39e4ed1cf27.png,http://test.xueai.art/file/2026/3/31/69cb41b73888d39e4ed1cf28.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829317957644464188",
|
id: '829317957644464188',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/31/69cb40893888d39e4ed1cf20.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/31/69cb40893888d39e4ed1cf20.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829305227994738709",
|
id: '829305227994738709',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/31/69cb34ae3888d39e4ed1cf1e.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/31/69cb34ae3888d39e4ed1cf1e.png',
|
||||||
"prompt": "这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格",
|
prompt: '这是一幅治愈写实风格的插画,画面中展现了一处城市石砌民居的顶层露台。石砌民居带有木质门、铁栅栏窗台、锈色金属屋顶,空调外机挂在墙边,金属防护栏环绕四周,外置楼梯延伸下来。露台上摆放着竹制躺椅和木质桌椅,旁边还有小凳。彩色水桶,一个是红色,一个是蓝色,放置在角落,花盆里栽种着金桔,水仙花、蝴蝶兰,整齐排列,大量花卉在露台上盛放。露台旁有一棵枝叶繁茂、遮天蔽日的大树,绿意浓郁,与建筑共生。周围还摆放着一些绿植盆栽。远处是城市的高楼,整个场景被绿意环绕,明亮柔和的光线洒下,充满了都市生活与自然融合感的顶层露台风情。Moebius (Jean Giraud)风格',
|
||||||
"model": "flux"
|
model: 'flux'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "829068575099597628",
|
id: '829068575099597628',
|
||||||
"fileUrl": "http://test.xueai.art/file/2026/3/30/69ca58473888d39e0bb8728b.png",
|
fileUrl: 'http://test.xueai.art/file/2026/3/30/69ca58473888d39e0bb8728b.png',
|
||||||
"prompt": "",
|
prompt: '',
|
||||||
"model": ""
|
model: ''
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label>W</label>
|
<label>W</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
|
||||||
v-model.number="localWidth"
|
v-model.number="localWidth"
|
||||||
|
type="number"
|
||||||
:min="minW"
|
:min="minW"
|
||||||
:max="maxW"
|
:max="maxW"
|
||||||
@input="onWidthChange"
|
@input="onWidthChange"
|
||||||
@ -21,8 +21,8 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label>H</label>
|
<label>H</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
|
||||||
v-model.number="localHeight"
|
v-model.number="localHeight"
|
||||||
|
type="number"
|
||||||
:min="minH"
|
:min="minH"
|
||||||
:max="maxH"
|
:max="maxH"
|
||||||
@input="onHeightChange"
|
@input="onHeightChange"
|
||||||
@ -41,9 +41,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Popover from '@/components/Popover/index.vue'
|
|
||||||
import lockIcon from '@/assets/dialog/lock.svg'
|
import lockIcon from '@/assets/dialog/lock.svg'
|
||||||
import lockNoIcon from '@/assets/dialog/lockNo.svg'
|
import lockNoIcon from '@/assets/dialog/lockNo.svg'
|
||||||
|
import Popover from '@/components/Popover/index.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
width: { type: Number, default: 1024 },
|
width: { type: Number, default: 1024 },
|
||||||
@ -51,7 +51,7 @@ const props = defineProps({
|
|||||||
minW: { type: Number, default: 256 },
|
minW: { type: Number, default: 256 },
|
||||||
maxW: { type: Number, default: 6197 },
|
maxW: { type: Number, default: 6197 },
|
||||||
minH: { type: Number, default: 256 },
|
minH: { type: Number, default: 256 },
|
||||||
maxH: { type: Number, default: 4096 },
|
maxH: { type: Number, default: 4096 }
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['update:width', 'update:height'])
|
const emit = defineEmits(['update:width', 'update:height'])
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
<div class="size-inputs">
|
<div class="size-inputs">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label>W</label>
|
<label>W</label>
|
||||||
<input type="number" v-model.number="width" @input="updateWidth" :disabled="isLocked">
|
<input v-model.number="width" type="number" :disabled="isLocked" @input="updateWidth">
|
||||||
</div>
|
</div>
|
||||||
<div class="lock-icon" :class="{ locked: isLocked }" @click="toggleLock">
|
<div class="lock-icon" :class="{ locked: isLocked }" @click="toggleLock">
|
||||||
<img :src="isLocked ? lockIcon : lockNoIcon" alt="约束比例">
|
<img :src="isLocked ? lockIcon : lockNoIcon" alt="约束比例">
|
||||||
@ -45,7 +45,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label>H</label>
|
<label>H</label>
|
||||||
<input type="number" v-model.number="height" @input="updateHeight" :disabled="isLocked">
|
<input v-model.number="height" type="number" :disabled="isLocked" @input="updateHeight">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -60,9 +60,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Popover from '@/components/Popover/index.vue'
|
|
||||||
import lockIcon from '@/assets/dialog/lock.svg'
|
import lockIcon from '@/assets/dialog/lock.svg'
|
||||||
import lockNoIcon from '@/assets/dialog/lockNo.svg'
|
import lockNoIcon from '@/assets/dialog/lockNo.svg'
|
||||||
|
import Popover from '@/components/Popover/index.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@ -95,7 +95,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
allowCustom: {
|
allowCustom: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true
|
||||||
},
|
},
|
||||||
resolutionOptions: {
|
resolutionOptions: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -461,5 +461,4 @@ watch(() => [props.modelValue, props.resolution], () => {
|
|||||||
background: #f5f6f7;
|
background: #f5f6f7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -19,8 +19,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-upload
|
<el-upload
|
||||||
ref="uploadRef"
|
|
||||||
v-show="false"
|
v-show="false"
|
||||||
|
ref="uploadRef"
|
||||||
:action="uploadurl"
|
:action="uploadurl"
|
||||||
multiple
|
multiple
|
||||||
:limit="limit"
|
:limit="limit"
|
||||||
|
|||||||
@ -15,10 +15,9 @@
|
|||||||
import Select from '@/components/Select/index.vue'
|
import Select from '@/components/Select/index.vue'
|
||||||
import { fetchPlatformModels, getPlatformCode } from '@/utils/modelApi'
|
import { fetchPlatformModels, getPlatformCode } from '@/utils/modelApi'
|
||||||
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: { type: String, default: 'Flux 2' },
|
modelValue: { type: String, default: 'Flux 2' },
|
||||||
typeValue: { type: String, default: 'text' },
|
typeValue: { type: String, default: 'text' }
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'update:typeValue'])
|
const emit = defineEmits(['update:modelValue', 'update:typeValue'])
|
||||||
@ -28,7 +27,7 @@ const platformModels = ref([])
|
|||||||
const categoryMap = [
|
const categoryMap = [
|
||||||
{ tag: 'text', label: '生成模型', inputType: 'text' },
|
{ tag: 'text', label: '生成模型', inputType: 'text' },
|
||||||
{ tag: 'edit', label: '编辑模型', inputType: 'image' },
|
{ tag: 'edit', label: '编辑模型', inputType: 'image' },
|
||||||
{ tag: 'vision', label: '视觉理解模型', inputType: 'vision' },
|
{ tag: 'vision', label: '视觉理解模型', inputType: 'vision' }
|
||||||
]
|
]
|
||||||
|
|
||||||
function parseValue(encoded) {
|
function parseValue(encoded) {
|
||||||
@ -44,14 +43,14 @@ function encodeValue(tag, modelName) {
|
|||||||
|
|
||||||
function findTagForModel(modelName) {
|
function findTagForModel(modelName) {
|
||||||
for (const cat of categoryMap) {
|
for (const cat of categoryMap) {
|
||||||
const model = platformModels.value.find(m => (m.display_name || m.name) === modelName && m.tags?.includes(cat.tag))
|
const model = platformModels.value.find((m) => (m.display_name || m.name) === modelName && m.tags?.includes(cat.tag))
|
||||||
if (model) return cat.tag
|
if (model) return cat.tag
|
||||||
}
|
}
|
||||||
return 'text'
|
return 'text'
|
||||||
}
|
}
|
||||||
|
|
||||||
function tagToInputType(tag) {
|
function tagToInputType(tag) {
|
||||||
const cat = categoryMap.find(c => c.tag === tag)
|
const cat = categoryMap.find((c) => c.tag === tag)
|
||||||
return cat?.inputType || 'text'
|
return cat?.inputType || 'text'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ const selectValue = computed({
|
|||||||
if (!parsed) return
|
if (!parsed) return
|
||||||
emit('update:modelValue', parsed.modelName)
|
emit('update:modelValue', parsed.modelName)
|
||||||
emit('update:typeValue', tagToInputType(parsed.tag))
|
emit('update:typeValue', tagToInputType(parsed.tag))
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 从 API 加载模型列表
|
// 从 API 加载模型列表
|
||||||
@ -89,26 +88,26 @@ const modelGroups = computed(() => {
|
|||||||
if (models.length === 0) return []
|
if (models.length === 0) return []
|
||||||
|
|
||||||
return categoryMap
|
return categoryMap
|
||||||
.filter(cat => models.some(m => m.tags?.includes(cat.tag)))
|
.filter((cat) => models.some((m) => m.tags?.includes(cat.tag)))
|
||||||
.map(cat => ({
|
.map((cat) => ({
|
||||||
label: cat.label,
|
label: cat.label,
|
||||||
options: models
|
options: models
|
||||||
.filter(m => m.tags?.includes(cat.tag))
|
.filter((m) => m.tags?.includes(cat.tag))
|
||||||
.map(m => ({
|
.map((m) => ({
|
||||||
value: `${cat.tag}::${m.display_name || m.name}`,
|
value: `${cat.tag}::${m.display_name || m.name}`,
|
||||||
label: m.display_name || m.name,
|
label: m.display_name || m.name,
|
||||||
disabled: m.disabled || false,
|
disabled: m.disabled || false
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })),
|
.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }))
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
// 模型列表加载后自动纠正不可用模型
|
// 模型列表加载后自动纠正不可用模型
|
||||||
watch(platformModels, (models) => {
|
watch(platformModels, (models) => {
|
||||||
if (models.length === 0) return
|
if (models.length === 0) return
|
||||||
const currentModel = models.find(m => (m.display_name || m.name) === props.modelValue || m.id === props.modelValue)
|
const currentModel = models.find((m) => (m.display_name || m.name) === props.modelValue || m.id === props.modelValue)
|
||||||
if (!currentModel || currentModel.disabled) {
|
if (!currentModel || currentModel.disabled) {
|
||||||
const firstEnabled = models.find(m => !m.disabled)
|
const firstEnabled = models.find((m) => !m.disabled)
|
||||||
if (firstEnabled) {
|
if (firstEnabled) {
|
||||||
emit('update:modelValue', firstEnabled.display_name || firstEnabled.name)
|
emit('update:modelValue', firstEnabled.display_name || firstEnabled.name)
|
||||||
emit('update:typeValue', tagToInputType(findTagForModel(firstEnabled.display_name || firstEnabled.name)))
|
emit('update:typeValue', tagToInputType(findTagForModel(firstEnabled.display_name || firstEnabled.name)))
|
||||||
@ -121,9 +120,9 @@ watch(() => props.modelValue, (newValue) => {
|
|||||||
if (!newValue) return
|
if (!newValue) return
|
||||||
const models = platformModels.value
|
const models = platformModels.value
|
||||||
if (models.length === 0) return
|
if (models.length === 0) return
|
||||||
const currentModel = models.find(m => (m.display_name || m.name) === newValue)
|
const currentModel = models.find((m) => (m.display_name || m.name) === newValue)
|
||||||
if (currentModel && currentModel.disabled) {
|
if (currentModel && currentModel.disabled) {
|
||||||
const firstEnabled = models.find(m => !m.disabled)
|
const firstEnabled = models.find((m) => !m.disabled)
|
||||||
if (firstEnabled) {
|
if (firstEnabled) {
|
||||||
emit('update:modelValue', firstEnabled.display_name || firstEnabled.name)
|
emit('update:modelValue', firstEnabled.display_name || firstEnabled.name)
|
||||||
emit('update:typeValue', tagToInputType(findTagForModel(firstEnabled.display_name || firstEnabled.name)))
|
emit('update:typeValue', tagToInputType(findTagForModel(firstEnabled.display_name || firstEnabled.name)))
|
||||||
|
|||||||
@ -18,12 +18,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Select from '@/components/Select/index.vue'
|
|
||||||
|
|
||||||
import videoPattern2 from '@/assets/dialog/videoPattern2.svg'
|
import videoPattern2 from '@/assets/dialog/videoPattern2.svg'
|
||||||
|
|
||||||
import videoPattern4 from '@/assets/dialog/videoPattern4.svg'
|
import videoPattern4 from '@/assets/dialog/videoPattern4.svg'
|
||||||
import videoPattern5 from '@/assets/dialog/videoPattern5.svg'
|
import videoPattern5 from '@/assets/dialog/videoPattern5.svg'
|
||||||
import videoPattern6 from '@/assets/dialog/videoPattern6.svg'
|
import videoPattern6 from '@/assets/dialog/videoPattern6.svg'
|
||||||
|
import Select from '@/components/Select/index.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@ -49,7 +49,7 @@ const quantityOptions = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const selectedIcon = computed(() => {
|
const selectedIcon = computed(() => {
|
||||||
const option = quantityOptions.find(opt => opt.value === quantity.value)
|
const option = quantityOptions.find((opt) => opt.value === quantity.value)
|
||||||
return option ? option.icon : videoPattern1
|
return option ? option.icon : videoPattern1
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -25,9 +25,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-upload
|
<el-upload
|
||||||
v-for="i in maxImages"
|
v-for="i in maxImages"
|
||||||
|
v-show="false"
|
||||||
:key="i"
|
:key="i"
|
||||||
:ref="el => setUploadRef(el, i - 1)"
|
:ref="el => setUploadRef(el, i - 1)"
|
||||||
v-show="false"
|
|
||||||
:action="uploadurl"
|
:action="uploadurl"
|
||||||
:limit="1"
|
:limit="1"
|
||||||
:before-upload="beforeUpload"
|
:before-upload="beforeUpload"
|
||||||
|
|||||||
@ -44,15 +44,14 @@ const fetchConfig = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fetchConfig()
|
fetchConfig()
|
||||||
|
|
||||||
watch(() => videoConfig.value, (newConfig) => {
|
watch(() => videoConfig.value, (newConfig) => {
|
||||||
const models = newConfig[props.videoPattern] || []
|
const models = newConfig[props.videoPattern] || []
|
||||||
if (models.length > 0) {
|
if (models.length > 0) {
|
||||||
const enabledModels = models.filter(m => !m.disabled)
|
const enabledModels = models.filter((m) => !m.disabled)
|
||||||
if (enabledModels.length > 0) {
|
if (enabledModels.length > 0) {
|
||||||
const currentModelExists = enabledModels.find(m => m.value === props.modelValue)
|
const currentModelExists = enabledModels.find((m) => m.value === props.modelValue)
|
||||||
if (!currentModelExists) {
|
if (!currentModelExists) {
|
||||||
model.value = enabledModels[0].value
|
model.value = enabledModels[0].value
|
||||||
}
|
}
|
||||||
@ -60,7 +59,6 @@ watch(() => videoConfig.value, (newConfig) => {
|
|||||||
}
|
}
|
||||||
}, { deep: true })
|
}, { deep: true })
|
||||||
|
|
||||||
|
|
||||||
const model = computed({
|
const model = computed({
|
||||||
get: () => props.modelValue,
|
get: () => props.modelValue,
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
@ -91,9 +89,9 @@ const getModelType = (value) => {
|
|||||||
watch(() => props.videoPattern, (newPattern) => {
|
watch(() => props.videoPattern, (newPattern) => {
|
||||||
const models = videoConfig.value[newPattern] || []
|
const models = videoConfig.value[newPattern] || []
|
||||||
if (models.length > 0) {
|
if (models.length > 0) {
|
||||||
const enabledModels = models.filter(m => !m.disabled)
|
const enabledModels = models.filter((m) => !m.disabled)
|
||||||
if (enabledModels.length > 0) {
|
if (enabledModels.length > 0) {
|
||||||
const currentModelExists = enabledModels.find(m => m.value === props.modelValue)
|
const currentModelExists = enabledModels.find((m) => m.value === props.modelValue)
|
||||||
if (!currentModelExists) {
|
if (!currentModelExists) {
|
||||||
model.value = enabledModels[0].value
|
model.value = enabledModels[0].value
|
||||||
}
|
}
|
||||||
@ -103,9 +101,9 @@ watch(() => props.videoPattern, (newPattern) => {
|
|||||||
|
|
||||||
watch(() => props.modelValue, (newValue) => {
|
watch(() => props.modelValue, (newValue) => {
|
||||||
const models = videoConfig.value[props.videoPattern] || []
|
const models = videoConfig.value[props.videoPattern] || []
|
||||||
const currentModel = models.find(m => m.value === newValue)
|
const currentModel = models.find((m) => m.value === newValue)
|
||||||
if (currentModel && currentModel.disabled) {
|
if (currentModel && currentModel.disabled) {
|
||||||
const enabledModels = models.filter(m => !m.disabled)
|
const enabledModels = models.filter((m) => !m.disabled)
|
||||||
if (enabledModels.length > 0) {
|
if (enabledModels.length > 0) {
|
||||||
model.value = enabledModels[0].value
|
model.value = enabledModels[0].value
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import { useDisplayStore, useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import { getToken, setToken } from '@/utils/auth'
|
import { getToken, setToken } from '@/utils/auth'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
|
|||||||
@ -28,7 +28,7 @@ const DisplayStoreSetup = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateItemToSuccess = (taskId, fileUrls) => {
|
const updateItemToSuccess = (taskId, fileUrls) => {
|
||||||
const index = tempList.value.findIndex(item => item.id === taskId)
|
const index = tempList.value.findIndex((item) => item.id === taskId)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
tempList.value[index].status = 'success'
|
tempList.value[index].status = 'success'
|
||||||
tempList.value[index].files = Array.isArray(fileUrls) ? fileUrls : [fileUrls]
|
tempList.value[index].files = Array.isArray(fileUrls) ? fileUrls : [fileUrls]
|
||||||
@ -56,7 +56,7 @@ const DisplayStoreSetup = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deleteHistoryItem = (id) => {
|
const deleteHistoryItem = (id) => {
|
||||||
const index = tempList.value.findIndex(item => item.id === id)
|
const index = tempList.value.findIndex((item) => item.id === id)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
tempList.value.splice(index, 1)
|
tempList.value.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
const ParamStoreSetup = () => {
|
const ParamStoreSetup = () => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export async function generateFilename(url, prefix = 'image') {
|
|||||||
|
|
||||||
// 如果URL中没有文件名或扩展名,根据类型生成
|
// 如果URL中没有文件名或扩展名,根据类型生成
|
||||||
if (!filename || !filename.includes('.')) {
|
if (!filename || !filename.includes('.')) {
|
||||||
const timestamp = new Date().getTime()
|
const timestamp = Date.now()
|
||||||
// 根据URL内容推断文件类型,否则默认为png
|
// 根据URL内容推断文件类型,否则默认为png
|
||||||
const extension = url.includes('.jpg') || url.includes('.jpeg')
|
const extension = url.includes('.jpg') || url.includes('.jpeg')
|
||||||
? '.jpg'
|
? '.jpg'
|
||||||
@ -21,7 +21,7 @@ export async function generateFilename(url, prefix = 'image') {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('URL解析失败:', error)
|
console.error('URL解析失败:', error)
|
||||||
// 如果URL解析失败,生成默认文件名
|
// 如果URL解析失败,生成默认文件名
|
||||||
const timestamp = new Date().getTime()
|
const timestamp = Date.now()
|
||||||
return `${prefix}_${timestamp}.png`
|
return `${prefix}_${timestamp}.png`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="width: 100%;display: flex;justify-content: center;align-items: center;transform: rotate(180deg);">
|
<div style="width: 100%;display: flex;justify-content: center;align-items: center;transform: rotate(180deg);">
|
||||||
<div class="primary-box" :class="{ 'none-primary-box': props.item.status === 'none' }">
|
<div class="primary-box" :class="{ 'none-primary-box': props.item.status === 'none' }">
|
||||||
|
<div ref="promptContainerRef" class="prompt-container">
|
||||||
<div class="prompt-container" ref="promptContainerRef">
|
<div ref="promptWrapperRef" class="prompt-wrapper">
|
||||||
<div class="prompt-wrapper" ref="promptWrapperRef">
|
<div ref="promptRef" class="prompt" :class="{ expanded: isHovering }" @mouseenter="isHovering = true" @mouseleave="isHovering = false">
|
||||||
<div class="prompt" ref="promptRef" :class="{ 'expanded': isHovering }" @mouseenter="isHovering = true" @mouseleave="isHovering = false">
|
|
||||||
<span class="prompt-text">
|
<span class="prompt-text">
|
||||||
{{ props.item.generateData.prompt || '生成图片' }}
|
{{ props.item.generateData.prompt || '生成图片' }}
|
||||||
<i-ep-DocumentCopy class="Copy" @click.stop="copyPrompt" />
|
<i-ep-DocumentCopy class="Copy" @click.stop="copyPrompt" />
|
||||||
</span>
|
</span>
|
||||||
<div class="generate-data internal" v-show="!isHovering && !showExternalGenerateData">
|
<div v-show="!isHovering && !showExternalGenerateData" class="generate-data internal">
|
||||||
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
||||||
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="generate-data external" v-show="!isHovering && showExternalGenerateData">
|
<div v-show="!isHovering && showExternalGenerateData" class="generate-data external">
|
||||||
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
||||||
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,7 +48,7 @@
|
|||||||
|
|
||||||
<!-- 已完成 图片 -->
|
<!-- 已完成 图片 -->
|
||||||
<div v-if="props.item.status === 'success' && props.item.type === 'Painting'" class="box success-box">
|
<div v-if="props.item.status === 'success' && props.item.type === 'Painting'" class="box success-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">
|
<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" /> -->
|
||||||
<Img :src="file" alt="index" class="img" />
|
<Img :src="file" alt="index" class="img" />
|
||||||
|
|
||||||
@ -65,7 +64,7 @@
|
|||||||
placement="top"
|
placement="top"
|
||||||
:hide-after="0"
|
:hide-after="0"
|
||||||
>
|
>
|
||||||
<div @click.stop="AIbrush(file, index)" class="bottom-brush">
|
<div class="bottom-brush" @click.stop="AIbrush(file, index)">
|
||||||
<img :src="brush" />
|
<img :src="brush" />
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
@ -74,7 +73,7 @@
|
|||||||
|
|
||||||
<!-- 已完成 视频 -->
|
<!-- 已完成 视频 -->
|
||||||
<div v-if="props.item.status === 'success' && props.item.type === 'Video'" class="box success-box">
|
<div v-if="props.item.status === 'success' && props.item.type === 'Video'" class="box success-box">
|
||||||
<div class="one-box" :class="{ 'collected': isCollected(props.item.files[0]) }" @mouseenter="hoverIndex = 0" @mouseleave="hoverIndex = -1">
|
<div class="one-box" :class="{ collected: isCollected(props.item.files[0]) }" @mouseenter="hoverIndex = 0" @mouseleave="hoverIndex = -1">
|
||||||
<!-- <img :src="file" alt="index" class="img" /> -->
|
<!-- <img :src="file" alt="index" class="img" /> -->
|
||||||
<video :src="props.item.files[0]" class="video" controls playsinline />
|
<video :src="props.item.files[0]" class="video" controls playsinline />
|
||||||
|
|
||||||
@ -97,16 +96,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { cancelOrCollect, deleteGenerateHistory } from '@/apis/display'
|
||||||
|
import againGenerateIcon from '@/assets/display/againGenerate.svg'
|
||||||
import brush from '@/assets/display/brush.svg'
|
import brush from '@/assets/display/brush.svg'
|
||||||
import collectionIcon from '@/assets/display/collection.svg'
|
|
||||||
import collectionActiveIcon from '@/assets/display/collection-active.svg'
|
import collectionActiveIcon from '@/assets/display/collection-active.svg'
|
||||||
|
import collectionIcon from '@/assets/display/collection.svg'
|
||||||
|
import deleteImageIcon from '@/assets/display/deleteImage.svg'
|
||||||
|
import reEditIcon from '@/assets/display/reEdit.svg'
|
||||||
|
import Img from '@/components/Img/index.vue'
|
||||||
import { useDisplayStore, useParamStore, useUserStore } from '@/stores'
|
import { useDisplayStore, useParamStore, useUserStore } from '@/stores'
|
||||||
import { downloadImage } from '@/utils/downloadImage.js'
|
import { downloadImage } from '@/utils/downloadImage.js'
|
||||||
import reEditIcon from '@/assets/display/reEdit.svg'
|
|
||||||
import againGenerateIcon from '@/assets/display/againGenerate.svg'
|
|
||||||
import deleteImageIcon from '@/assets/display/deleteImage.svg'
|
|
||||||
import Img from '@/components/Img/index.vue'
|
|
||||||
import { cancelOrCollect, deleteGenerateHistory } from '@/apis/display'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
@ -276,7 +275,7 @@ const addCollection = async (url) => {
|
|||||||
const res = await cancelOrCollect({
|
const res = await cancelOrCollect({
|
||||||
taskId: props.item.id,
|
taskId: props.item.id,
|
||||||
userId: useUser.userInfo.id,
|
userId: useUser.userInfo.id,
|
||||||
url: url,
|
url
|
||||||
})
|
})
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
ElMessage.success(res.message || '操作成功')
|
ElMessage.success(res.message || '操作成功')
|
||||||
|
|||||||
@ -43,8 +43,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VirtualScroller
|
<VirtualScroller
|
||||||
ref="scrollerRef"
|
|
||||||
v-if="props.if"
|
v-if="props.if"
|
||||||
|
ref="scrollerRef"
|
||||||
:items="list"
|
:items="list"
|
||||||
key-field="id"
|
key-field="id"
|
||||||
:estimated-height="300"
|
:estimated-height="300"
|
||||||
@ -66,17 +66,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useDisplayStore, useParamStore, useUserStore } from '@/stores'
|
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import Set from './components/set.vue'
|
import { useRouter } from 'vue-router'
|
||||||
import RefreshOverlay from './components/RefreshOverlay.vue'
|
import { requestTaskHistory } from '@/apis/display'
|
||||||
|
import Canvas from '@/components/canvas/index.vue'
|
||||||
import Select from '@/components/Select/index.vue'
|
import Select from '@/components/Select/index.vue'
|
||||||
import { VirtualScroller } from '@/components/virtual-scroller'
|
import { VirtualScroller } from '@/components/virtual-scroller'
|
||||||
import Canvas from '@/components/canvas/index.vue'
|
import { useDisplayStore, useParamStore, useUserStore } from '@/stores'
|
||||||
import { requestTaskHistory } from '@/apis/display'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { getChargeType } from '@/utils/taskPolling'
|
|
||||||
import { getPlatformCode } from '@/utils/modelApi'
|
import { getPlatformCode } from '@/utils/modelApi'
|
||||||
|
import { getChargeType } from '@/utils/taskPolling'
|
||||||
|
import RefreshOverlay from './components/RefreshOverlay.vue'
|
||||||
|
import Set from './components/set.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
if: {
|
if: {
|
||||||
@ -138,7 +138,7 @@ const toggleDisplay = (newValue, oldValue) => {
|
|||||||
const conversion = (newlist) => {
|
const conversion = (newlist) => {
|
||||||
const temp = newlist.map((item) => {
|
const temp = newlist.map((item) => {
|
||||||
// 从 outputs 扁平数组提取 URL
|
// 从 outputs 扁平数组提取 URL
|
||||||
const files = item.outputs?.map(o => o.url) || []
|
const files = item.outputs?.map((o) => o.url) || []
|
||||||
const request = item.request || {}
|
const request = item.request || {}
|
||||||
const generateData = {
|
const generateData = {
|
||||||
model: item.model_name || '',
|
model: item.model_name || '',
|
||||||
@ -152,7 +152,7 @@ const conversion = (newlist) => {
|
|||||||
customHight: request.customHight,
|
customHight: request.customHight,
|
||||||
duration: request.duration || '',
|
duration: request.duration || '',
|
||||||
videoPattern: request.videoPattern || '',
|
videoPattern: request.videoPattern || '',
|
||||||
modelParams: { ...request },
|
modelParams: { ...request }
|
||||||
}
|
}
|
||||||
// 将 API status 映射为 UI 展示状态
|
// 将 API status 映射为 UI 展示状态
|
||||||
let uiStatus = 'success'
|
let uiStatus = 'success'
|
||||||
@ -168,7 +168,7 @@ const conversion = (newlist) => {
|
|||||||
generateData,
|
generateData,
|
||||||
time: item.created_at || '',
|
time: item.created_at || '',
|
||||||
files,
|
files,
|
||||||
collectStatus: item.collectStatus || {},
|
collectStatus: item.collectStatus || {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return temp
|
return temp
|
||||||
@ -235,7 +235,6 @@ const fetchHistory = async (isLoadMore = false) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasMoreData.value = dataList.length === 10
|
hasMoreData.value = dataList.length === 10
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取历史失败:', error)
|
console.error('获取历史失败:', error)
|
||||||
ElMessage({
|
ElMessage({
|
||||||
@ -380,5 +379,4 @@ onBeforeUnmount(() => {
|
|||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, onMounted, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import display from './display/index.vue'
|
|
||||||
import { useDisplayStore } from '@/stores'
|
import { useDisplayStore } from '@/stores'
|
||||||
|
import display from './display/index.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const useDisplay = useDisplayStore()
|
const useDisplay = useDisplayStore()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user