From c9ed299ef62d4032c674f73e594c8b6cb0924fa1 Mon Sep 17 00:00:00 2001 From: WangLeo <690854599@qq.com> Date: Tue, 10 Mar 2026 18:09:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 4 +- components.d.ts | 2 + src/assets/dialog/ArrowDown.svg | 4 + src/components/UploadPicture.vue | 16 +- src/components/canvas/index.vue | 704 +++++++++++++++--- .../dialogBox/imageUploader/index.vue | 231 ++++++ src/components/dialogBox/index.vue | 182 ++--- src/components/dialogBox/model/index.vue | 32 +- src/config/runninghub/index.js | 89 ++- src/stores/display.js | 2 + src/utils/createTask.js | 2 +- src/utils/websocket.js | 10 +- src/views/home/display/components/set.vue | 6 +- src/views/home/display/index.vue | 389 ---------- 14 files changed, 1011 insertions(+), 662 deletions(-) create mode 100644 src/assets/dialog/ArrowDown.svg create mode 100644 src/components/dialogBox/imageUploader/index.vue delete mode 100644 src/views/home/display/index.vue diff --git a/.env.development b/.env.development index ed78b96..b91e7f1 100644 --- a/.env.development +++ b/.env.development @@ -11,8 +11,8 @@ VITE_API_PAY_PREFIX = '/pay' VITE_API_PAY_TARGET = 'http://test.xueai.art' # http://43.248.133.202 test.xueai.art # 任务处理模块 -VITE_API_WORKFLOW_UPLOAD = 'http://aipaint.xueai.art/aigc/workflow/file/upload' # https://sxwz.xueai.art/workflow https://designtools.xueai.art/workflow -VITE_API_WORKFLOW_WS = 'wss://aipaint.xueai.art/testworkflow' +VITE_API_WORKFLOW_UPLOAD = 'http://43.248.97.19:4000/aigc/workflow/file/upload' # https://sxwz.xueai.art/workflow https://designtools.xueai.art/workflow +VITE_API_WORKFLOW_WS = 'ws://43.248.97.19:4000/testworkflow' # 是否开启开发者工具 VITE_OPEN_DEVTOOLS = false diff --git a/components.d.ts b/components.d.ts index 3ef0a65..9020d24 100644 --- a/components.d.ts +++ b/components.d.ts @@ -61,7 +61,9 @@ declare module 'vue' { IEpStarFilled: typeof import('~icons/ep/star-filled')['default'] IEpTop: typeof import('~icons/ep/top')['default'] IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default'] + ImageUploader: typeof import('./src/components/dialogBox/imageUploader/index.vue')['default'] Img: typeof import('./src/components/Img/index.vue')['default'] + 'Index(1)': typeof import('./src/components/canvas/index(1).vue')['default'] Library: typeof import('./src/components/Library/index.vue')['default'] Model: typeof import('./src/components/dialogBox/model/index.vue')['default'] ModelParam: typeof import('./src/components/AIgenerate/components/modelParam.vue')['default'] diff --git a/src/assets/dialog/ArrowDown.svg b/src/assets/dialog/ArrowDown.svg new file mode 100644 index 0000000..6185b85 --- /dev/null +++ b/src/assets/dialog/ArrowDown.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/UploadPicture.vue b/src/components/UploadPicture.vue index 0fdc5d5..423b562 100644 --- a/src/components/UploadPicture.vue +++ b/src/components/UploadPicture.vue @@ -15,24 +15,11 @@ >
- -
- - - - - -
diff --git a/src/components/dialogBox/index.vue b/src/components/dialogBox/index.vue index 7c5b12f..5cb96f5 100644 --- a/src/components/dialogBox/index.vue +++ b/src/components/dialogBox/index.vue @@ -3,23 +3,20 @@
AI绘画2026
-
-
回到底部
+
+
回到底部
+ +
+
+ +
+
@@ -60,8 +44,9 @@ import Proportion from './proportion/index.vue' import Quantity from './quantity/index.vue' import Model from './model/index.vue' +import ImageUploader from './imageUploader/index.vue' import { Sender } from 'vue-element-plus-x' -import { useDisplayStore, useParamStore } from '@/stores' +import { useDisplayStore } from '@/stores' import { generate } from '@/utils/websocket' import { useRouter } from 'vue-router' @@ -77,19 +62,17 @@ const props = defineProps({ }) const router = useRouter() const useDisplay = useDisplayStore() -const useParams = useParamStore() -const uploadurl = import.meta.env.VITE_API_WORKFLOW_UPLOAD -const uploadRef = ref(null) +const type = ref('image') + +const prompt = ref('一个女孩在树下吃苹果') const model = ref('flux') -const proportion = ref('9:16') +const proportion = ref('4:3') const quantity = ref(1) const resolution = ref('1k') const promptPlaceholder = '描述你想生成的画面和动作。' -const prompt = ref('一个女孩在树下吃苹果') -const imageurl = ref('') -const imageurlShow = ref('') +const referenceImages = ref([]) const isgerenate = ref(false) const autoSizeConfig = computed(() => { @@ -100,61 +83,6 @@ const autoSizeConfig = computed(() => { } }) -// 处理图片选择 -const handleSelect = async (url) => { - imageurlShow.value = url - // 从URL获取图片Blob对象 - const initialFile = await fetch(url) - const blob = await initialFile.blob() - - // 创建文件对象 - const file = new File([blob], `selected_image_${Date.now()}.jpg`, { type: blob.type }) - file.uid = genFileId() - console.log('file', file) - - uploadRef.value.clearFiles() // 如果上传数量为1且已存在图片,则清除已存在的图 - - // 获取当前文件列表 - uploadRef.value.handleStart(file) - uploadRef.value.submit() -} - -// 检查文件类型和大小 -const beforeUpload = (rawFile) => { - console.log('beforeUpload', rawFile) - const allowedTypes = ['image/jpeg', 'image/png'] - - // 检查文件类型 - if (!allowedTypes.includes(rawFile.type)) { - // eslint-disable-next-line no-undef - ElMessage.error('不支持的文件类型,请上传 JPG、PNG 格式的图片') - return false - } - - // 检查文件大小(限制为2MB) - if (rawFile.size / 1024 / 1024 > 10) { - // eslint-disable-next-line no-undef - ElMessage.error('图片大小不能超过 10MB') - return false - } -} - -// 成功上传后,将文件信息保存到 state 中 -const SuccessSet = (response) => { - console.log('上传成功', response) - // eslint-disable-next-line no-undef - ElMessage.success('上传成功') - imageurl.value = response.url -} - -// 错误处理 -const Errors = (error) => { - console.log('上传失败', error) - // eslint-disable-next-line no-undef - ElMessage.error('上传失败,请重新选择图片') - imageurlShow.value = '' -} - const handleStart = async () => { if (!props.isGenerate) { router.push({ name: 'home', query: { loading: false, Generate: true } }) @@ -166,17 +94,25 @@ const handleStart = async () => { } isgerenate.value = true console.log('生成开始', isgerenate.value) + const imgs = [] + referenceImages.value.forEach((img, index) => { + imgs.push({ name: `image_${index + 1}`, data: img.url }) + }) + const data = { AIGC: 'Painting', platform: 'runninghub', file_type: 'image', modelName: model.value, - prompt: prompt.value, - quantity: quantity.value, - aspect_ratio: proportion.value, - resolution: resolution.value, + params: [ + { name: 'prompt', data: prompt.value}, + { name: 'quantity', data: quantity.value}, + { name: 'aspect_ratio', data: proportion.value}, + { name: 'resolution', data: resolution.value}, + ], + imgs } - await generate('text', data) + await generate(type.value, data) console.log('生成中', isgerenate.value) } @@ -193,24 +129,14 @@ const handleScrollToBottom = () => { watch(() => useDisplay.isSubGerenate, (newValue) => { console.log('生成状态', newValue) - if (!newValue) { - console.log('生成完成', isgerenate.value) - isgerenate.value = useDisplay.isSubGerenate - } + isgerenate.value = newValue + // handleScrollToBottom() +}, { immediate: true }) + +watch(() => type.value, (newValue) => { + console.log('type.value', newValue) }) -watch(() => useParams.AIvideoImage, (newValue) => { - if (newValue) { - if (newValue.url === imageurl.value) return - console.log('图片选择成功,打开视频页面', newValue) - parentTime.value = newValue.time - parentIndex.value = newValue.parentIndex - parentTaskId.value = newValue.parentTaskId - imageurl.value = newValue.url - handleSelect(newValue.url) - useParams.AIvideoImage = '' - } -}) onMounted(() => { if (props.generate) { generate() @@ -231,7 +157,7 @@ onMounted(() => { transition: all 0.3s ease; } -.scroll-to-bottom-btn { +.sender-top { width: 100%; display: flex; justify-content: flex-end; @@ -239,24 +165,48 @@ onMounted(() => { transition: all 0.3s ease; z-index: 101; margin-bottom: 10px; + position: relative; .scroll-to-bottom-text { - padding: 8px; + padding: 10px; width: auto; height: auto; border-radius: 10px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - background-color: rgba(0, 15, 51, 0.836); - color: #ffffff; + // box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + background-color: #F8F9FA; + color: #666; font-size: 12px; + display: flex; + align-items: center; + justify-content: center; + gap: 5px; &:active { transform: scale(0.95); } &:hover { - background-color: rgb(0, 15, 51); + background-color: #F0F1F2; + } + } + + // 上传图片 + .upload-img-container{ + position: absolute; + bottom: 0; + left: 0; + width: 80%; + display: flex; + justify-content: start; + align-items: center; + z-index: 1; + gap: 16px; + + .reference-diagram { + display: flex; + align-items: center; } } } + .generate{ height: 100%; display: flex; diff --git a/src/components/dialogBox/model/index.vue b/src/components/dialogBox/model/index.vue index f4de1a9..a34ef50 100644 --- a/src/components/dialogBox/model/index.vue +++ b/src/components/dialogBox/model/index.vue @@ -54,27 +54,34 @@ const props = defineProps({ modelValue: { type: String, default: 'flux' + }, + typeValue: { + type: String, + default: 'text' } }) -const emit = defineEmits(['update:modelValue']) +const emit = defineEmits(['update:modelValue', 'update:typeValue']) const model = computed({ get: () => props.modelValue, - set: (value) => emit('update:modelValue', value) + set: (value) => { + emit('update:modelValue', value) + const newType = getModelType(value) + emit('update:typeValue', newType) + } }) const generateModels = [ { value: 'flux', label: 'flux' }, - { value: 'Z-image', label: 'Z-image' }, + { value: 'zImage', label: 'Z-image' }, { value: 'jimeng', label: 'jimeng' }, - { value: 'Qwen-image', label: 'Qwen-image' } + { value: 'QwenImage', label: 'QwenImage' } ] const editModels = [ - { value: 'Banana-Pro', label: 'Banana-Pro' }, + { value: 'BananaPro', label: 'Banana-Pro' }, { value: 'Qwen-image', label: 'Qwen-image' }, - { value: 'Banana', label: 'Banana' }, { value: 'Kontext', label: 'Kontext' }, { value: 'Jimeng_4.0', label: 'Jimeng.4.0' } ] @@ -87,6 +94,19 @@ const selectModel = (value) => { model.value = value } +const getModelType = (value) => { + if (generateModels.find(m => m.value === value)) { + return 'text' + } + if (editModels.find(m => m.value === value)) { + return 'image' + } + if (visionModels.find(m => m.value === value)) { + return 'vision' + } + return 'text' +} + const getModelLabel = (value) => { const allModels = [...generateModels, ...editModels, ...visionModels] const model = allModels.find(item => item.value === value) diff --git a/src/config/runninghub/index.js b/src/config/runninghub/index.js index 70793b8..e6fe68b 100644 --- a/src/config/runninghub/index.js +++ b/src/config/runninghub/index.js @@ -1,8 +1,42 @@ -export function Playload(data, type) { - data = getWidthHeight(data) - console.log('宽与高', data) - if(data.modelName === 'flux'){ - return flux(data) +export async function Playload(data,type) { + // data = getWidthHeight(data) + try { + const response = await fetch(`https://resources.xueai.art/AIGC/static/public/Platform/Painting/workflows/${type}/${data.modelName}.json`) + const json = await response.json() + + const nodeInfoList = [] + + if (Array.isArray(data.imgs)) { + for (const key of data.imgs) { + if (json.nodeInfoList[key.name]) { + console.log(key) + json.nodeInfoList[key.name].fieldValue = key.url + nodeInfoList.push(json.nodeInfoList[key.name]) + } + } + json.nodeInfoList[index].fieldValue = data.imgs.length() - 1 + } + + if (Array.isArray(data.params)) { + for (const key of data.params) { + if (json.nodeInfoList[key.name]) { + console.log(key) + json.nodeInfoList[key.name].fieldValue = key.data + nodeInfoList.push(json.nodeInfoList[key.name]) + } + } + } + + if (Array.isArray(json.must)) { + nodeInfoList.push(...json.must) + } + return { + workflowId: json.workflowId, + nodeInfoList + } + } catch (error) { + console.error('获取 JSON 文件失败:', error) + throw error } } @@ -32,46 +66,21 @@ function getWidthHeight(data) { return data } -function LTX2(data) { - - const nodeInfoList = [ - { nodeId: '9', fieldName: 'text', fieldValue: data.prompt }, - { nodeId: '40', fieldName: 'text', fieldValue: data.aspect_ratio }, - { nodeId: '39', fieldName: 'text', fieldValue: data.resolution }, - { nodeId: '29', fieldName: 'index', fieldValue: '' } - ] - switch (index){ - case 1: - nodeInfoList[3].fieldValue = '1' - break - case 2: - nodeInfoList[3].fieldValue = '2' - break - case 3: - nodeInfoList[3].fieldValue = '3' - break - case 4: - nodeInfoList[3].fieldValue = '4' - break - } - if (data.image1) nodeInfoList.push({ nodeId: '2', fieldName: 'image', fieldValue: data.image1 }) - if (data.image2) nodeInfoList.push({ nodeId: '3', fieldName: 'image', fieldValue: data.image2 }) - if (data.image3) nodeInfoList.push({ nodeId: '4', fieldName: 'image', fieldValue: data.image3 }) - if (data.image4) nodeInfoList.push({ nodeId: '13', fieldName: 'image', fieldValue: data.image4 }) - if (data.image5) nodeInfoList.push({ nodeId: '14', fieldName: 'image', fieldValue: data.image5 }) - - return { - workflowId: '2031032712240304130', - nodeInfoList - } -} - function flux(data) { const nodeInfoList = [ { nodeId: '23', fieldName: 'text', fieldValue: data.prompt }, { nodeId: '129', fieldName: 'aspect_ratio', fieldValue: data.aspect_ratio }, - { nodeId: '101', fieldName: 'control_after_generate', fieldValue: 'randomize' } + { nodeId: '101', fieldName: 'control_after_generate', fieldValue: 'randomize' }, + { nodeId: '15', fieldName: 'index', fieldValue: 0 }, + + {nodeId: '2', fieldName: 'vae_name', fieldValue: 'ae.sft'}, + {nodeId: '5', fieldName: 'sampler_name', fieldValue: 'euler'}, + {nodeId: '50', fieldName: 'value', fieldValue: 1}, + {nodeId: '102', fieldName: 'value', fieldValue: 20}, + {nodeId: '103', fieldName: 'value', fieldValue: 1}, + {nodeId: '104', fieldName: 'value', fieldValue: 1}, + {nodeId: '105', fieldName: 'value', fieldValue: 0.8} ] return { diff --git a/src/stores/display.js b/src/stores/display.js index 7d1be53..57e12b1 100644 --- a/src/stores/display.js +++ b/src/stores/display.js @@ -2,6 +2,7 @@ const DisplayStoreSetup = () => { const Sender_variant = ref('updown') const scrollerRef = ref(null) const tempList = ref([]) + const isSubGerenate = ref(false) const addGeneratingItem = (item) => { const newItem = { @@ -63,6 +64,7 @@ const DisplayStoreSetup = () => { Sender_variant, scrollerRef, tempList, + isSubGerenate, addGeneratingItem, updateItemToSuccess, initHistoryList, diff --git a/src/utils/createTask.js b/src/utils/createTask.js index c463bd2..df24ce2 100644 --- a/src/utils/createTask.js +++ b/src/utils/createTask.js @@ -3,7 +3,7 @@ import outPlatform from '@/config/index' // 处理音频生成任务的数据并返回 export async function createTask(data, type, taskId, token) { console.log(data, type) - const payload = await outPlatform[data.platform].Playload(data) + const payload = await outPlatform[data.platform].Playload(data, type) return { AIGC: data.AIGC, diff --git a/src/utils/websocket.js b/src/utils/websocket.js index 948c6d2..4d2a564 100644 --- a/src/utils/websocket.js +++ b/src/utils/websocket.js @@ -55,11 +55,13 @@ export async function generate(type, data) { const token = getToken() const taskId = crypto.randomUUID() let currentTaskId = null + + useDisplay.isSubGerenate = true const result = await createTask(data, type, taskId, token) console.log(result) // const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjY0NDEwODAyMjk1OTgzNzIzMCwicm5TdHIiOiJiWkVwS2JLWFJyZmRIaFFHWXZKTkdzOGdGM0JSRmxQOCJ9.5eQ2GtVdrDntQDe2tnF8vl_DhTfd2uW-KNqzvl1imc0' - const wsURL = `${import.meta.env.VITE_API_WORKFLOW_WS}/?token=${token}` + const wsURL = `${import.meta.env.VITE_API_WORKFLOW_WS}/?token=Bearer ${token}` const socket = new WebSocket(wsURL) console.log('WebSocket连接已建立') @@ -94,7 +96,9 @@ export async function generate(type, data) { name: data.prompt || '生成中...', type: type }) - + setTimeout(() => { + useDisplay.scrollToBottom() + }, 100) return } message.value = event.data @@ -121,7 +125,7 @@ export async function generate(type, data) { // 处理链接关闭 socket.onclose = async (event) => { console.log('WebSocket已关闭:', event) - + useDisplay.isSubGerenate = false // 清理心跳定时器 if (heartbeatInterval) { clearInterval(heartbeatInterval) diff --git a/src/views/home/display/components/set.vue b/src/views/home/display/components/set.vue index 08ec2b8..70964c0 100644 --- a/src/views/home/display/components/set.vue +++ b/src/views/home/display/components/set.vue @@ -365,9 +365,11 @@ const addCollection = (url) => { align-items: center; justify-content: center; gap: 5px; - padding: 5px 10px; - border-radius: 5px; + width: 120px; + height: 36px; + border-radius: 10px; cursor: pointer; + background-color: #F8F9FA; } .bottom-btn:hover{ background-color: #e4e7ed; diff --git a/src/views/home/display/index.vue b/src/views/home/display/index.vue deleted file mode 100644 index 7611d82..0000000 --- a/src/views/home/display/index.vue +++ /dev/null @@ -1,389 +0,0 @@ - - - - -