绘画初步完成所有功能逻辑,右上角时间与收藏筛选待完成
This commit is contained in:
parent
727ecb378b
commit
5a7dab7dc7
|
|
@ -11,7 +11,9 @@ export {}
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
2: typeof import('./src/components/virtual-scroller/VirtualScroller copy 2.vue')['default']
|
||||||
Canvas: typeof import('./src/components/canvas/index.vue')['default']
|
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']
|
DialogBox: typeof import('./src/components/dialogBox/index.vue')['default']
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { generate } from '@/utils/websocket'
|
import { generate } from '@/utils/websocket'
|
||||||
import { useDisplayStore } from '@/stores'
|
import { useDisplayStore } from '@/stores'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
|
|
@ -176,6 +177,10 @@ const props = defineProps({
|
||||||
source: {
|
source: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -236,15 +241,15 @@ const updateCurrentEditingContent = (description) => {
|
||||||
const shapeWord = shapeType === 'circle' ? '圈' : '框'
|
const shapeWord = shapeType === 'circle' ? '圈' : '框'
|
||||||
|
|
||||||
if (selectedReferenceImages.value.length > 0) {
|
if (selectedReferenceImages.value.length > 0) {
|
||||||
const imageIndex = allReferenceImages.value.indexOf(selectedReferenceImages.value[0]) + 1
|
const imageIndex = allReferenceImages.value.indexOf(selectedReferenceImages.value[0]) + 2
|
||||||
const prefix = isFirstShape ? '' : ','
|
const prefix = isFirstShape ? '' : ','
|
||||||
currentEditingContent.value = `${prefix}将${colorName}${shapeWord}内的【XXX】替换为【图${imageIndex}中的${description}】`
|
currentEditingContent.value = `${prefix}将图1${colorName}${shapeWord}内的【XXX】替换为【图${imageIndex}中的${description}】`
|
||||||
} else if (description) {
|
} else if (description) {
|
||||||
const prefix = isFirstShape ? '' : ','
|
const prefix = isFirstShape ? '' : ','
|
||||||
currentEditingContent.value = `${prefix}将${colorName}${shapeWord}内的【XXX】替换为【${description}】`
|
currentEditingContent.value = `${prefix}将图1${colorName}${shapeWord}内的【XXX】替换为【${description}】`
|
||||||
} else {
|
} else {
|
||||||
const prefix = isFirstShape ? '' : ','
|
const prefix = isFirstShape ? '' : ','
|
||||||
currentEditingContent.value = `${prefix}将${colorName}${shapeWord}内的【XXX】替换为【XXX】或【图X中的XXX】`
|
currentEditingContent.value = `${prefix}将图1${colorName}${shapeWord}内的【XXX】替换为【XXX】或【图X中的XXX】`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,7 +286,13 @@ const initCanvas = () => {
|
||||||
const containerHeight = container.clientHeight
|
const containerHeight = container.clientHeight
|
||||||
|
|
||||||
if (currentImage.value) {
|
if (currentImage.value) {
|
||||||
if (currentImage.value.startsWith('data:')) {
|
let imageUrl = currentImage.value
|
||||||
|
|
||||||
|
if (!imageUrl.startsWith('data:')) {
|
||||||
|
imageUrl = imageUrl.replace('https://sxwz.xueai.art', 'https://talkingdraw.xueai.art')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageUrl.startsWith('data:')) {
|
||||||
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)
|
||||||
|
|
@ -291,9 +302,9 @@ const initCanvas = () => {
|
||||||
bgImage.value = img
|
bgImage.value = img
|
||||||
scale.value = imgScale
|
scale.value = imgScale
|
||||||
}
|
}
|
||||||
img.src = currentImage.value
|
img.src = imageUrl
|
||||||
} else {
|
} else {
|
||||||
fetch(currentImage.value)
|
fetch(imageUrl)
|
||||||
.then(res => res.blob())
|
.then(res => res.blob())
|
||||||
.then(blob => {
|
.then(blob => {
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
|
|
@ -548,18 +559,55 @@ const handleSend = async () => {
|
||||||
imgs.push({ name: `image_${index + 2}`, url: img })
|
imgs.push({ name: `image_${index + 2}`, url: img })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const uploadImg = async (imgItem) => {
|
||||||
|
if (!imgItem.url.startsWith('data:') && !imgItem.url.startsWith('blob:')) {
|
||||||
|
return imgItem
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(imgItem.url)
|
||||||
|
const blob = await response.blob()
|
||||||
|
const file = new File([blob], `${imgItem.name}.png`, { type: 'image/png' })
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await request({
|
||||||
|
url: import.meta.env.VITE_API_WORKFLOW_UPLOAD,
|
||||||
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (result.success || result.code === 0) {
|
||||||
|
return { name: imgItem.name, url: result.url }
|
||||||
|
}
|
||||||
|
return imgItem
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上传失败:', error)
|
||||||
|
return imgItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadedImgs = await Promise.all(imgs.map(uploadImg))
|
||||||
|
|
||||||
|
const generateData = {
|
||||||
|
model: 'banana',
|
||||||
|
modelType: 'edit',
|
||||||
|
prompt: inputText.value,
|
||||||
|
proportion: '比例自动'
|
||||||
|
}
|
||||||
const data = {
|
const data = {
|
||||||
AIGC: 'Painting',
|
AIGC: 'Painting',
|
||||||
platform: 'runninghub',
|
platform: 'runninghub',
|
||||||
file_type: 'image',
|
modelName: 'banana',
|
||||||
modelName: 'flux',
|
|
||||||
params: [
|
params: [
|
||||||
{ name: 'prompt', data: inputText.value },
|
{ name: 'prompt', data: inputText.value + '并且去除掉图1中的框' },
|
||||||
{ name: 'quantity', data: 1 },
|
{ name: 'index', data: 1 },
|
||||||
{ name: 'aspect_ratio', data: '16:9' },
|
|
||||||
{ name: 'resolution', data: '1k' }
|
|
||||||
],
|
],
|
||||||
imgs
|
imgs: uploadedImgs,
|
||||||
|
result: JSON.stringify(generateData)
|
||||||
}
|
}
|
||||||
|
|
||||||
emit('send', {
|
emit('send', {
|
||||||
|
|
@ -568,7 +616,7 @@ const handleSend = async () => {
|
||||||
shapes: shapes.value
|
shapes: shapes.value
|
||||||
})
|
})
|
||||||
|
|
||||||
await generate('text', data)
|
await generate('edit', data , generateData, props.type)
|
||||||
handleClose()
|
handleClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -592,7 +640,7 @@ const handleUploadReference = () => {
|
||||||
input.type = 'file'
|
input.type = 'file'
|
||||||
input.accept = 'image/*'
|
input.accept = 'image/*'
|
||||||
input.multiple = true
|
input.multiple = true
|
||||||
input.onchange = (e) => {
|
input.onchange = async (e) => {
|
||||||
const files = Array.from(e.target.files)
|
const files = Array.from(e.target.files)
|
||||||
const remainingSlots = 5 - allReferenceImages.value.length
|
const remainingSlots = 5 - allReferenceImages.value.length
|
||||||
|
|
||||||
|
|
@ -603,13 +651,17 @@ const handleUploadReference = () => {
|
||||||
|
|
||||||
const filesToUpload = files.slice(0, remainingSlots)
|
const filesToUpload = files.slice(0, remainingSlots)
|
||||||
|
|
||||||
filesToUpload.forEach(file => {
|
const readFileAsDataURL = (file) => {
|
||||||
const reader = new FileReader()
|
return new Promise((resolve, reject) => {
|
||||||
reader.onload = (event) => {
|
const reader = new FileReader()
|
||||||
allReferenceImages.value.push(event.target.result)
|
reader.onload = (event) => resolve(event.target.result)
|
||||||
}
|
reader.onerror = (error) => reject(error)
|
||||||
reader.readAsDataURL(file)
|
reader.readAsDataURL(file)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageDataUrls = await Promise.all(filesToUpload.map(readFileAsDataURL))
|
||||||
|
allReferenceImages.value.push(...imageDataUrls)
|
||||||
}
|
}
|
||||||
input.click()
|
input.click()
|
||||||
}
|
}
|
||||||
|
|
@ -818,11 +870,6 @@ const handleBrushConfirm = () => {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
// border-color: #409eff;
|
|
||||||
// color: #409eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
|
|
@ -847,10 +894,6 @@ const handleBrushConfirm = () => {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
// border-color: #409eff;
|
|
||||||
// color: #409eff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -867,6 +910,7 @@ const handleBrushConfirm = () => {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brush-panel {
|
.brush-panel {
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ const props = defineProps({
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1
|
default: 1
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 图片列表,每个元素包含 url 和 uid 属性
|
||||||
|
*/
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
|
|
@ -52,13 +55,39 @@ const uploadurl = import.meta.env.VITE_API_WORKFLOW_UPLOAD
|
||||||
const uploadRef = ref(null)
|
const uploadRef = ref(null)
|
||||||
const imageList = ref([...props.modelValue])
|
const imageList = ref([...props.modelValue])
|
||||||
const localPreviewList = ref([...props.modelValue])
|
const localPreviewList = ref([...props.modelValue])
|
||||||
|
const isUploading = ref(false)
|
||||||
|
|
||||||
watch(() => props.modelValue, (newVal) => {
|
watch(() => props.modelValue, async (newVal) => {
|
||||||
imageList.value = [...newVal]
|
if (isUploading.value) {
|
||||||
if (newVal.length === 0) {
|
return
|
||||||
localPreviewList.value = []
|
|
||||||
}
|
}
|
||||||
}, { deep: true })
|
|
||||||
|
imageList.value = [...newVal]
|
||||||
|
|
||||||
|
const newPreviewList = []
|
||||||
|
for (const img of newVal) {
|
||||||
|
let previewImg = { ...img }
|
||||||
|
if (img.url && !img.url.startsWith('blob:')) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(img.url)
|
||||||
|
const blob = await response.blob()
|
||||||
|
previewImg = {
|
||||||
|
...img,
|
||||||
|
url: URL.createObjectURL(blob),
|
||||||
|
serverUrl: img.url
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to create blob URL:', error)
|
||||||
|
previewImg = {
|
||||||
|
...img,
|
||||||
|
serverUrl: img.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newPreviewList.push(previewImg)
|
||||||
|
}
|
||||||
|
localPreviewList.value = newPreviewList
|
||||||
|
}, { deep: true, immediate: true })
|
||||||
|
|
||||||
const triggerUpload = () => {
|
const triggerUpload = () => {
|
||||||
uploadRef.value.$el.querySelector('input').click()
|
uploadRef.value.$el.querySelector('input').click()
|
||||||
|
|
@ -81,6 +110,8 @@ const beforeUpload = (rawFile) => {
|
||||||
const handleSuccess = (response, uploadFile) => {
|
const handleSuccess = (response, uploadFile) => {
|
||||||
ElMessage.success('上传成功')
|
ElMessage.success('上传成功')
|
||||||
|
|
||||||
|
isUploading.value = true
|
||||||
|
|
||||||
const localUrl = URL.createObjectURL(uploadFile.raw)
|
const localUrl = URL.createObjectURL(uploadFile.raw)
|
||||||
|
|
||||||
const newImage = {
|
const newImage = {
|
||||||
|
|
@ -92,9 +123,14 @@ const handleSuccess = (response, uploadFile) => {
|
||||||
|
|
||||||
const newPreview = {
|
const newPreview = {
|
||||||
uid: uploadFile.uid,
|
uid: uploadFile.uid,
|
||||||
url: localUrl
|
url: localUrl,
|
||||||
|
serverUrl: response.url
|
||||||
}
|
}
|
||||||
localPreviewList.value.push(newPreview)
|
localPreviewList.value.push(newPreview)
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
isUploading.value = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleError = () => {
|
const handleError = () => {
|
||||||
|
|
@ -107,7 +143,7 @@ const handleExceed = () => {
|
||||||
|
|
||||||
const handleDelete = (index) => {
|
const handleDelete = (index) => {
|
||||||
const previewItem = localPreviewList.value[index]
|
const previewItem = localPreviewList.value[index]
|
||||||
if (previewItem && previewItem.url.startsWith('blob:')) {
|
if (previewItem && previewItem.url && previewItem.url.startsWith('blob:')) {
|
||||||
URL.revokeObjectURL(previewItem.url)
|
URL.revokeObjectURL(previewItem.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,10 +153,10 @@ const handleDelete = (index) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleImageClick = (clickedIndex) => {
|
const handleImageClick = (clickedIndex) => {
|
||||||
const clickedImage = imageList.value[clickedIndex]
|
const clickedImage = localPreviewList.value[clickedIndex]
|
||||||
if (!clickedImage) return
|
if (!clickedImage) return
|
||||||
|
|
||||||
const otherImages = imageList.value
|
const otherImages = localPreviewList.value
|
||||||
.filter((_, index) => index !== clickedIndex)
|
.filter((_, index) => index !== clickedIndex)
|
||||||
.map((img, index) => ({
|
.map((img, index) => ({
|
||||||
...img,
|
...img,
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ const autoSizeConfig = computed(() => {
|
||||||
|
|
||||||
const handleStart = async () => {
|
const handleStart = async () => {
|
||||||
const currentType = props.type
|
const currentType = props.type
|
||||||
|
let currentModelType = modelType.value
|
||||||
|
|
||||||
if (!props.isGenerate) {
|
if (!props.isGenerate) {
|
||||||
router.push({ name: 'home', query: { loading: false, Generate: true, type: currentType } })
|
router.push({ name: 'home', query: { loading: false, Generate: true, type: currentType } })
|
||||||
|
|
@ -121,11 +122,19 @@ const handleStart = async () => {
|
||||||
referenceImages.value.forEach((img, index) => {
|
referenceImages.value.forEach((img, index) => {
|
||||||
imgs.push({ name: `image_${index + 1}`, url: img.url })
|
imgs.push({ name: `image_${index + 1}`, url: img.url })
|
||||||
})
|
})
|
||||||
console.log('imgs', imgs)
|
// console.log('imgs', imgs)
|
||||||
|
// 判断视频模式下是否文生视频
|
||||||
|
if (currentType === 'video') {
|
||||||
|
if (imgs.length > 0) {
|
||||||
|
currentModelType = 'image'
|
||||||
|
} else {
|
||||||
|
currentModelType = 'text'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const generateData = {
|
const generateData = {
|
||||||
model: model.value,
|
model: model.value,
|
||||||
modelType: modelType.value,
|
modelType: currentModelType,
|
||||||
prompt: prompt.value,
|
prompt: prompt.value,
|
||||||
proportion: proportion.value,
|
proportion: proportion.value,
|
||||||
referenceImages: referenceImages.value,
|
referenceImages: referenceImages.value,
|
||||||
|
|
@ -138,7 +147,6 @@ const handleStart = async () => {
|
||||||
const data = {
|
const data = {
|
||||||
AIGC: 'Painting',
|
AIGC: 'Painting',
|
||||||
platform: 'runninghub',
|
platform: 'runninghub',
|
||||||
file_type: 'image',
|
|
||||||
modelName: model.value,
|
modelName: model.value,
|
||||||
params: [
|
params: [
|
||||||
{ name: 'prompt', data: prompt.value},
|
{ name: 'prompt', data: prompt.value},
|
||||||
|
|
@ -149,7 +157,7 @@ const handleStart = async () => {
|
||||||
imgs,
|
imgs,
|
||||||
result: JSON.stringify(generateData)
|
result: JSON.stringify(generateData)
|
||||||
}
|
}
|
||||||
await generate(modelType.value, data, generateData, props.type)
|
await generate(currentModelType, data, generateData, currentType)
|
||||||
console.log('生成中', isgerenate.value)
|
console.log('生成中', isgerenate.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,11 +244,12 @@ onMounted(() => {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.scroll-to-bottom-text {
|
.scroll-to-bottom-text {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 2;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
// box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
||||||
background-color: #F8F9FA;
|
background-color: #F8F9FA;
|
||||||
color: #666;
|
color: #666;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
@ -248,6 +257,7 @@ onMounted(() => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
export async function Playload(data,type) {
|
export async function Playload(data,modelType) {
|
||||||
// data = getWidthHeight(data)
|
// data = getWidthHeight(data)
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://resources.xueai.art/AIGC/static/public/Platform/Painting/workflows/${type}/${data.modelName}.json`)
|
const response = await fetch(`https://resources.xueai.art/AIGC/static/public/Platform/Painting/workflows/${modelType}/${data.modelName}.json`)
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
|
|
||||||
const nodeInfoList = []
|
const nodeInfoList = []
|
||||||
|
|
||||||
if (Array.isArray(data.imgs) && data.imgs.length > 0 && type === 'image') {
|
if (Array.isArray(data.imgs) && data.imgs.length > 0 && (modelType === 'image' || modelType === 'edit')) {
|
||||||
for (const key of data.imgs) {
|
for (const key of data.imgs) {
|
||||||
if (json.nodeInfoList[key.name]) {
|
if (json.nodeInfoList[key.name]) {
|
||||||
console.log(key)
|
console.log(key)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import outPlatform from '@/config/index'
|
import outPlatform from '@/config/index'
|
||||||
|
|
||||||
// 处理音频生成任务的数据并返回
|
// 处理音频生成任务的数据并返回
|
||||||
export async function createTask(data, type, taskId, token) {
|
export async function createTask(data, modelType, taskId, token) {
|
||||||
console.log(data, type)
|
console.log(data, modelType)
|
||||||
const payload = await outPlatform[data.platform].Playload(data, type)
|
const payload = await outPlatform[data.platform].Playload(data, modelType)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
AIGC: data.AIGC,
|
AIGC: data.AIGC,
|
||||||
platform: data.platform,
|
platform: data.platform,
|
||||||
prompt: data.prompt,
|
prompt: data.prompt,
|
||||||
taskType: type === 'text' ? 1 : 2,
|
taskType: modelType === 'text' ? 1 : 2,
|
||||||
modelName: data.modelName,
|
modelName: data.modelName,
|
||||||
payload,
|
payload,
|
||||||
taskId,
|
taskId,
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,21 @@
|
||||||
<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 class="title" ref="titleRef" @mouseenter="isHovering = true" @mouseleave="isHovering = false">
|
<div class="prompt-container" ref="promptContainerRef" @mouseenter="isHovering = true" @mouseleave="isHovering = false">
|
||||||
<div class="prompt-wrapper">
|
<div class="prompt-wrapper" ref="promptWrapperRef">
|
||||||
<div class="prompt" ref="nameRef" :class="{ 'expanded': isHovering }">{{ props.item.generateData.prompt || '生成图片' }}</div>
|
<div class="prompt" ref="promptRef" :class="{ 'expanded': isHovering }">
|
||||||
<div class="generate-data" v-show="!isHovering" ref="generateDa taRef" :class="{ 'second-line': shouldShowOnSecondLine }">
|
<span class="prompt-text">{{ props.item.generateData.prompt || '生成图片' }}</span>
|
||||||
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
<div class="generate-data internal" v-show="!isHovering && !showExternalGenerateData">
|
||||||
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
||||||
|
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="generate-data external" v-show="!isHovering && showExternalGenerateData">
|
||||||
|
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
||||||
|
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 加载中 -->
|
<!-- 加载中 -->
|
||||||
|
|
@ -114,45 +120,36 @@ const useUser = useUserStore()
|
||||||
const localCollectStatus = ref({ ...props.item.collectStatus })
|
const localCollectStatus = ref({ ...props.item.collectStatus })
|
||||||
const hoverIndex = ref(-1)
|
const hoverIndex = ref(-1)
|
||||||
const isHovering = ref(false)
|
const isHovering = ref(false)
|
||||||
const shouldShowOnSecondLine = ref(false)
|
const showExternalGenerateData = ref(false)
|
||||||
const nameRef = ref(null)
|
const promptContainerRef = ref(null)
|
||||||
const titleRef = ref(null)
|
const promptWrapperRef = ref(null)
|
||||||
const generateDataRef = ref(null)
|
const promptRef = ref(null)
|
||||||
const wrapperHeight = ref(38.5)
|
|
||||||
|
|
||||||
const checkTextOverflow = () => {
|
const checkTextOverflow = () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (nameRef.value && titleRef.value && generateDataRef.value) {
|
if (promptRef.value && promptWrapperRef.value && promptContainerRef.value) {
|
||||||
const lineHeight = 22.5
|
const lineHeight = 22.5
|
||||||
const padding = 8
|
const padding = 8
|
||||||
const twoLineHeight = 55
|
const twoLineHeight = lineHeight * 2 + padding
|
||||||
const generateDataWidth = generateDataRef.value.offsetWidth + 20
|
|
||||||
|
|
||||||
nameRef.value.style.maxHeight = 'none'
|
promptRef.value.style.maxHeight = 'none'
|
||||||
nameRef.value.style.overflow = 'visible'
|
promptRef.value.style.overflow = 'hidden'
|
||||||
|
|
||||||
const actualHeight = nameRef.value.scrollHeight
|
const actualHeight = promptRef.value.scrollHeight
|
||||||
const lineCount = Math.ceil((actualHeight - padding * 2) / lineHeight)
|
const lineCount = Math.ceil((actualHeight - padding) / lineHeight)
|
||||||
|
|
||||||
if (!isHovering.value) {
|
if (!isHovering.value) {
|
||||||
nameRef.value.style.maxHeight = '55px'
|
promptRef.value.style.maxHeight = `${twoLineHeight}px`
|
||||||
nameRef.value.style.overflow = 'hidden'
|
promptRef.value.style.overflow = 'hidden'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineCount > 1) {
|
showExternalGenerateData.value = lineCount >= 3
|
||||||
shouldShowOnSecondLine.value = true
|
|
||||||
wrapperHeight.value = twoLineHeight
|
|
||||||
} else {
|
|
||||||
const containerWidth = titleRef.value.offsetWidth
|
|
||||||
const promptWidth = nameRef.value.scrollWidth
|
|
||||||
const firstLineRemaining = containerWidth - promptWidth
|
|
||||||
|
|
||||||
if (firstLineRemaining < generateDataWidth) {
|
if (!isHovering.value) {
|
||||||
shouldShowOnSecondLine.value = true
|
if(lineCount >= 3){
|
||||||
wrapperHeight.value = twoLineHeight
|
promptContainerRef.value.style.height = `${twoLineHeight}px`
|
||||||
} else {
|
} else {
|
||||||
shouldShowOnSecondLine.value = false
|
promptContainerRef.value.style.height = `${actualHeight}px`
|
||||||
wrapperHeight.value = Math.max(lineHeight + padding * 2, actualHeight)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,13 +157,17 @@ const checkTextOverflow = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(isHovering, (newVal) => {
|
watch(isHovering, (newVal) => {
|
||||||
if (nameRef.value) {
|
if (promptRef.value) {
|
||||||
|
const lineHeight = 22.5
|
||||||
|
const padding = 8
|
||||||
|
const twoLineHeight = lineHeight * 2 + padding
|
||||||
|
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
nameRef.value.style.maxHeight = 'none'
|
promptRef.value.style.maxHeight = 'none'
|
||||||
nameRef.value.style.overflow = 'visible'
|
promptRef.value.style.overflow = 'visible'
|
||||||
} else {
|
} else {
|
||||||
nameRef.value.style.maxHeight = '55px'
|
promptRef.value.style.maxHeight = `${twoLineHeight}px`
|
||||||
nameRef.value.style.overflow = 'hidden'
|
promptRef.value.style.overflow = 'hidden'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -204,11 +205,19 @@ const AIbrush = (file, index) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const reEdit = () => {
|
const reEdit = () => {
|
||||||
|
if(props.item.generateData?.modelType === 'edit'){
|
||||||
|
ElMessage.error('画笔生成的任务不能重新编辑')
|
||||||
|
return
|
||||||
|
}
|
||||||
useDisplay.setResultData(props.item.generateData)
|
useDisplay.setResultData(props.item.generateData)
|
||||||
useDisplay.fillParamsForEdit()
|
useDisplay.fillParamsForEdit()
|
||||||
}
|
}
|
||||||
|
|
||||||
const againGenerate = () => {
|
const againGenerate = () => {
|
||||||
|
if(props.item.generateData?.modelType === 'edit'){
|
||||||
|
ElMessage.error('画笔生成的任务不能再次生成')
|
||||||
|
return
|
||||||
|
}
|
||||||
useDisplay.setResultData(props.item.generateData)
|
useDisplay.setResultData(props.item.generateData)
|
||||||
useDisplay.triggerGenerateWithResult()
|
useDisplay.triggerGenerateWithResult()
|
||||||
}
|
}
|
||||||
|
|
@ -293,77 +302,97 @@ const addCollection = async (url) => {
|
||||||
height: 350px;
|
height: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title{
|
.prompt-container{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
.prompt-wrapper{
|
.prompt-wrapper{
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 28.5px;
|
width: 100%;
|
||||||
overflow: visible;
|
}
|
||||||
|
|
||||||
|
.prompt{
|
||||||
|
color: #333;
|
||||||
|
font-family: "Microsoft YaHei";
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22.5px;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 8px;
|
||||||
|
background-color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
left: -8px;
|
||||||
|
top: 0;
|
||||||
|
width: 102%;
|
||||||
|
z-index: 5;
|
||||||
|
|
||||||
|
&.expanded{
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.prompt{
|
.prompt-text{
|
||||||
position: absolute;
|
display: inline;
|
||||||
left: 0;
|
color: #333;
|
||||||
top: 0;
|
font-family: "Microsoft YaHei";
|
||||||
color: #333;
|
font-size: 14px;
|
||||||
font-family: "Microsoft YaHei";
|
font-style: normal;
|
||||||
font-size: 14px;
|
font-weight: 400;
|
||||||
font-style: normal;
|
line-height: normal;
|
||||||
font-weight: 400;
|
}
|
||||||
line-height: 22.5px;
|
|
||||||
word-break: break-all;
|
|
||||||
max-height: 60px;
|
|
||||||
overflow: hidden;
|
|
||||||
z-index: 10;
|
|
||||||
padding: 8px;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
&.expanded{
|
.generate-data{
|
||||||
max-height: none;
|
display: inline-flex;
|
||||||
overflow: visible;
|
align-items: center;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
background-color: #fff;
|
||||||
border-radius: 4px;
|
height: 22.5px;
|
||||||
}
|
text-align: center;
|
||||||
}
|
padding: 0 12px;
|
||||||
|
|
||||||
.generate-data{
|
color: #999;
|
||||||
position: absolute;
|
font-family: "Microsoft YaHei";
|
||||||
right: 0;
|
font-size: 14px;
|
||||||
top: 0;
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
|
||||||
|
&.internal{
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
margin-left: 10px;
|
||||||
z-index: 2;
|
|
||||||
background-color: #fff;
|
|
||||||
padding-right: 20px;
|
|
||||||
|
|
||||||
&.second-line{
|
|
||||||
top: auto;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.detailed-data{
|
&.external{
|
||||||
display: inline-flex;
|
position: absolute;
|
||||||
align-items: center;
|
right: -16px;
|
||||||
justify-content: center;
|
bottom: 0;
|
||||||
color: #999;
|
z-index: 6;
|
||||||
font-family: "Microsoft YaHei";
|
// padding-right: 20px;
|
||||||
font-size: 14px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: normal;
|
|
||||||
padding: 0 10px;
|
|
||||||
border-left: 1px solid #999;
|
|
||||||
white-space: nowrap;
|
|
||||||
height: 12px;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.first-detailed-data{
|
.detailed-data{
|
||||||
border-left: none;
|
display: inline-flex;
|
||||||
padding-left: 0;
|
align-items: center;
|
||||||
}
|
justify-content: center;
|
||||||
|
color: #999;
|
||||||
|
font-family: "Microsoft YaHei";
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-left: 1px solid #999;
|
||||||
|
white-space: nowrap;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.first-detailed-data{
|
||||||
|
border-left: none;
|
||||||
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box{
|
.box{
|
||||||
|
|
|
||||||
|
|
@ -1,391 +0,0 @@
|
||||||
<template>
|
|
||||||
<div id="display" class="content-area">
|
|
||||||
<RefreshOverlay :visible="refreshing" />
|
|
||||||
|
|
||||||
<div class="back">
|
|
||||||
<img src="@/assets/display/back.svg" alt="">
|
|
||||||
<span class="title-text">退出</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="props.if" class="btn-container">
|
|
||||||
<div class="btn">
|
|
||||||
<!-- <span class="btn-text">全部</span> -->
|
|
||||||
<img src="@/assets/display/search.svg" alt="">
|
|
||||||
</div>
|
|
||||||
<span class="line"></span>
|
|
||||||
<div class="btn">
|
|
||||||
<Select v-model="selectedTime" :options="timeOptions" width="auto">
|
|
||||||
<template #prefix>
|
|
||||||
<i-ep-Calendar />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #header>
|
|
||||||
<div class="header">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="value1"
|
|
||||||
type="daterange"
|
|
||||||
start-placeholder="Start date"
|
|
||||||
end-placeholder="End date"
|
|
||||||
:size="size"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<span class="line"></span>
|
|
||||||
<div class="btn">
|
|
||||||
<Select v-model="selectedFavorite" :options="favoriteOptions" width="auto" >
|
|
||||||
<template #prefix>
|
|
||||||
<i-ep-Star />
|
|
||||||
</template>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DynamicScroller
|
|
||||||
ref="scrollerRef"
|
|
||||||
v-if="props.if"
|
|
||||||
:items="list"
|
|
||||||
:min-item-size="800"
|
|
||||||
class="scroller"
|
|
||||||
:buffer="50"
|
|
||||||
@scroll="handleScroll"
|
|
||||||
>
|
|
||||||
<template #default="{ item, index, active }">
|
|
||||||
<DynamicScrollerItem
|
|
||||||
:key="item.id"
|
|
||||||
:item="item"
|
|
||||||
:active="active"
|
|
||||||
:index="index"
|
|
||||||
data-index="index"
|
|
||||||
>
|
|
||||||
<Set :key="`${item.id}`" :item="item" />
|
|
||||||
</DynamicScrollerItem>
|
|
||||||
</template>
|
|
||||||
</DynamicScroller>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { useDisplayStore, useParamStore, useUserStore } from '@/stores'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
import Set from './components/set.vue'
|
|
||||||
import RefreshOverlay from './components/RefreshOverlay.vue'
|
|
||||||
import Select from '@/components/Select/index.vue'
|
|
||||||
import { getGenerateHistoryList } from '@/apis/display'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
if: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const useDisplay = useDisplayStore()
|
|
||||||
const useParams = useParamStore()
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const router = useRouter()
|
|
||||||
const refreshing = ref(false)
|
|
||||||
const scrollerRef = ref(null)
|
|
||||||
const isLoadingMore = ref(false)
|
|
||||||
const activeTab = ref('all')
|
|
||||||
const isInitializing = ref(true)
|
|
||||||
let total = 0
|
|
||||||
|
|
||||||
const timeOptions = [
|
|
||||||
{ label: '全部', value: 'all' },
|
|
||||||
{ label: '最近一周', value: 'week' },
|
|
||||||
{ label: '最近一个月', value: 'month' },
|
|
||||||
{ label: '最近三个月', value: 'quarter' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const favoriteOptions = [
|
|
||||||
{ label: '全部', value: 'all' },
|
|
||||||
{ label: '已收藏', value: 'favorite' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const selectedTime = ref('all')
|
|
||||||
const selectedFavorite = ref('all')
|
|
||||||
const { tempList } = storeToRefs(useDisplay)
|
|
||||||
|
|
||||||
// const tempList = ref([
|
|
||||||
// { id: 0, type: 'image', status: 'none', name: '局部重绘', time: '2025-12-01 18:26', files: [] },
|
|
||||||
// { id: 1, type: 'image', status: 'success', name: '局部重绘', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] },
|
|
||||||
// { id: 2, type: 'image', status: 'success', name: '局部重绘', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] },
|
|
||||||
// { id: 3, type: 'image', status: 'success', name: '局部重绘', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] },
|
|
||||||
// { id: 4, type: 'image', status: 'success', name: '局部重绘', time: '2025-12-01 18:26', parentName: '2', parentTime: '2025-12-01 12:26', files: ['https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png', 'https://sxwz.xueai.art/static/png/image4-1-1-BZWQeEAk.png'] }
|
|
||||||
|
|
||||||
|
|
||||||
// ])
|
|
||||||
const activeFilter = ref('all')
|
|
||||||
const list = computed(() => {
|
|
||||||
const data = tempList.value || []
|
|
||||||
if (activeFilter.value === 'all') {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
return data.filter((item) => item.type === activeFilter.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
const page = ref(1)
|
|
||||||
// 筛选列表里的不同生成类型: 图片,视频
|
|
||||||
const toggleDisplay = (newValue, oldValue) => {
|
|
||||||
activeFilter.value = newValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换数据
|
|
||||||
const conversion = (newlist) => {
|
|
||||||
const temp = newlist.data.records.map((item) => {
|
|
||||||
return {
|
|
||||||
id: item.taskId,
|
|
||||||
collection: item.collection,
|
|
||||||
status: 'success',
|
|
||||||
prompt: item.prompt,
|
|
||||||
params: item.params,
|
|
||||||
time: item.createTime,
|
|
||||||
files: [item.fileUrl]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return temp
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取历史列表
|
|
||||||
const fetchHistory= async (isScrollTopLoad = false) => {
|
|
||||||
try {
|
|
||||||
if (isScrollTopLoad) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await getGenerateHistoryList({ userId: userStore.userInfo.id, chargeType: 1 })
|
|
||||||
total = result.data ? result.data.length : 0
|
|
||||||
if (total === 0) {
|
|
||||||
useDisplay.Sender_variant = 'updown'
|
|
||||||
router.push({ name: 'home' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrappedData = {
|
|
||||||
data: {
|
|
||||||
records: result.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const convertedList = conversion(wrappedData)
|
|
||||||
|
|
||||||
const adaptedList = convertedList.map((item, index) => {
|
|
||||||
const originalItem = result.data[index]
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
text: originalItem?.title || item.prompt || '生成图片',
|
|
||||||
name: originalItem?.title || item.prompt || '生成图片',
|
|
||||||
type: 'image',
|
|
||||||
title: originalItem?.title || '生成图片'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!isScrollTopLoad && adaptedList.length > 0) {
|
|
||||||
useDisplay.initHistoryList(adaptedList)
|
|
||||||
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
const scrollToBottomDirect = (force = false) => {
|
|
||||||
if (scrollerRef.value) {
|
|
||||||
const el = scrollerRef.value.$el
|
|
||||||
if (el) {
|
|
||||||
const viewport = el.querySelector('.vue-recycle-scroller__viewport')
|
|
||||||
if (viewport) {
|
|
||||||
console.log('直接滚动 - scrollHeight:', viewport.scrollHeight, 'force:', force)
|
|
||||||
if (force) {
|
|
||||||
viewport.scrollTop = viewport.scrollHeight + 1000
|
|
||||||
} else {
|
|
||||||
viewport.scrollTop = viewport.scrollHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof scrollerRef.value.scrollToItem === 'function') {
|
|
||||||
console.log('直接 scrollToItem')
|
|
||||||
scrollerRef.value.scrollToItem(list.value.length - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < 20; i++) {
|
|
||||||
setTimeout(() => {
|
|
||||||
scrollToBottomDirect(i >= 15)
|
|
||||||
}, 60 * i)
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
scrollToBottomDirect(true)
|
|
||||||
setTimeout(() => {
|
|
||||||
refreshing.value = false
|
|
||||||
isInitializing.value = false
|
|
||||||
useDisplay.scrollToBottom()
|
|
||||||
}, 600)
|
|
||||||
}, 1500)
|
|
||||||
} else {
|
|
||||||
useDisplay.initHistoryList(adaptedList)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取历史失败:', error)
|
|
||||||
ElMessage({
|
|
||||||
message: '获取历史失败',
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取下一页数据
|
|
||||||
const getList = async () => {
|
|
||||||
if (isLoadingMore.value) return
|
|
||||||
isLoadingMore.value = true
|
|
||||||
try {
|
|
||||||
await fetchHistory(true)
|
|
||||||
} finally {
|
|
||||||
isLoadingMore.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理滚动事件
|
|
||||||
const handleScroll = (event) => {
|
|
||||||
if (isInitializing.value) return
|
|
||||||
|
|
||||||
const { scrollTop, scrollHeight, clientHeight } = event.target
|
|
||||||
const distanceToBottom = scrollHeight - scrollTop - clientHeight
|
|
||||||
|
|
||||||
// 临时禁用滚动到顶部获取历史记录
|
|
||||||
// if (scrollTop <= 50 && !isLoadingMore.value) {
|
|
||||||
// getList()
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (distanceToBottom <= 50) {
|
|
||||||
useDisplay.Sender_variant = 'updown'
|
|
||||||
} else if (distanceToBottom >= 350) {
|
|
||||||
useDisplay.Sender_variant = 'default'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
console.log('display 组件已挂载')
|
|
||||||
if (!props.loading) return
|
|
||||||
refreshing.value = true
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
console.log('设置 scrollerRef 到 store')
|
|
||||||
useDisplay.scrollerRef = scrollerRef.value
|
|
||||||
fetchHistory()
|
|
||||||
})
|
|
||||||
|
|
||||||
page.value++
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.content-area {
|
|
||||||
width: 100%;
|
|
||||||
min-width: 750px;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
.back{
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
height: 36px;
|
|
||||||
padding: 10px;
|
|
||||||
position: absolute;
|
|
||||||
left: 30px;
|
|
||||||
top: 22px;
|
|
||||||
z-index: 3;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 10px;
|
|
||||||
// background-color: #FAFBFC;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back:hover{
|
|
||||||
background-color: #e4e7ed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-container{
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
height: auto;
|
|
||||||
padding: 4px;
|
|
||||||
right: 30px;
|
|
||||||
top: 22px;
|
|
||||||
z-index: 3;
|
|
||||||
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: #FAFBFC;
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
.btn{
|
|
||||||
display: flex;
|
|
||||||
padding: 5px;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-text{
|
|
||||||
color: #000;
|
|
||||||
font-family: "Microsoft YaHei";
|
|
||||||
font-size: 14px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line{
|
|
||||||
width: 1px;
|
|
||||||
height: 8px;
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroller {
|
|
||||||
height: 100%;
|
|
||||||
padding: 30px 0px 350px 0px;
|
|
||||||
will-change: scroll-position;
|
|
||||||
-webkit-overflow-scrolling: touch; /* iOS Safari */
|
|
||||||
scroll-behavior: smooth; /* 平滑滚动 */
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: transparent; /* 轨道透明 */
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.option-item) {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.option-item:hover) {
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.option-item.selected) {
|
|
||||||
color: #000F33;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.option-text) {
|
|
||||||
flex: 1;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.option-check) {
|
|
||||||
margin-left: 10px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="display" class="content-area">
|
<div id="display" class="content-area">
|
||||||
<RefreshOverlay :visible="refreshing" />
|
<RefreshOverlay :visible="refreshing" />
|
||||||
<Canvas v-model:visible="canvasVisible" :image="canvasImage" :reference-images="canvasReferenceImages" :source="canvasSource" @send="handleCanvasSend" />
|
<Canvas v-model:visible="canvasVisible" :image="canvasImage" :reference-images="canvasReferenceImages" :source="canvasSource" :type="props.type" @send="handleCanvasSend" />
|
||||||
|
|
||||||
<div class="back" @click="handleExit">
|
<div class="back" @click="handleExit">
|
||||||
<img src="@/assets/display/back.svg" alt="">
|
<img src="@/assets/display/back.svg" alt="">
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"nodeInfoList": {
|
|
||||||
"value_1":{ "nodeId":"20", "fieldName":"value", "fieldValue":"512" },
|
|
||||||
"value_2":{ "nodeId":"21", "fieldName":"value", "fieldValue":"512" },
|
|
||||||
"model_type":{ "nodeId":"18", "fieldName":"model_type", "fieldValue":"pro" },
|
|
||||||
"audio":{ "nodeId":"16", "fieldName":"audio", "fieldValue":"dce37fba29e596ddcd927c4660b4fb47bd3ecdbef4ad242fed72bd860e032b1f.flac" },
|
|
||||||
"image":{ "nodeId":"17", "fieldName":"image", "fieldValue":"d3ee810ce387739e7a99cb3ba87a104e34b0a955149b8525a600867edbab1138.png" }
|
|
||||||
},
|
|
||||||
"workflowId": "2036266399357739009"
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"nodeInfoList": {
|
|
||||||
"text":{ "nodeId":"40", "fieldName":"text", "fieldValue":"深夜,一个美丽的中年中国女人在一边弹吉他一边歌唱,环绕镜头,半身特写,逆光,月光,海风吹拂。场景是海边。" },
|
|
||||||
"audio":{ "nodeId":"39", "fieldName":"audio", "fieldValue":"62e5c0b15854bcac34e9aa0bf9f449767bda9f66047a60abeddbcd47c712ee8d.mp3" },
|
|
||||||
"start_index":{ "nodeId":"58", "fieldName":"start_index", "fieldValue":0 },
|
|
||||||
"duration":{ "nodeId":"58", "fieldName":"duration", "fieldValue":25 },
|
|
||||||
"value_1":{ "nodeId":"55", "fieldName":"value", "fieldValue":1280 },
|
|
||||||
"value_2":{ "nodeId":"56", "fieldName":"value", "fieldValue":720 }
|
|
||||||
},
|
|
||||||
"workflowId": "2036343285949665282"
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"nodeInfoList": {
|
|
||||||
"image":{ "nodeId":"2", "fieldName":"image", "fieldValue":"67bbf03a4ce453557b8c9acf85bd83d3519d3374ef35c54da1084d03f9ac111f.png" },
|
|
||||||
"prompt":{ "nodeId":"3", "fieldName":"prompt", "fieldValue":"一个小女孩在树下吃苹果" },
|
|
||||||
"resolution":{ "nodeId":"3", "fieldName":"resolution", "fieldValue":"540p" },
|
|
||||||
"duration":{ "nodeId":"3", "fieldName":"duration", "fieldValue":5 },
|
|
||||||
"audio":{ "nodeId":"3", "fieldName":"audio", "fieldValue":true }
|
|
||||||
},
|
|
||||||
"workflowId": "2036354451904139265"
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"nodeInfoList": {
|
|
||||||
"style":{ "nodeId":"2", "fieldName":"style", "fieldValue":"general" },
|
|
||||||
"prompt":{ "nodeId":"2", "fieldName":"prompt", "fieldValue":"一个小女孩在树下吃苹果" },
|
|
||||||
"resolution":{ "nodeId":"2", "fieldName":"resolution", "fieldValue":"540p" },
|
|
||||||
"aspect_ratio":{ "nodeId":"2", "fieldName":"aspect_ratio", "fieldValue":"4:3" },
|
|
||||||
"duration":{ "nodeId":"2", "fieldName":"duration", "fieldValue":5 },
|
|
||||||
"audio":{ "nodeId":"2", "fieldName":"audio", "fieldValue":true }
|
|
||||||
},
|
|
||||||
"workflowId": "2036349280088231938"
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue