This commit is contained in:
X1627315083@163.com
2026-03-11 16:57:27 +08:00
parent bb740dfd2e
commit ea3818fd2e
8 changed files with 257 additions and 82 deletions

View File

@@ -60,8 +60,12 @@
)
const onAdd = () => {
const tier_ = tier.value + 1
// 从data中获取originalImage
let nodeData = props.node?.data?.data.imageProcessTasks.filter((v) => v.taskId === props.node?.data?.data.selectTaskId)
const originalImage = nodeData[0]?.url
if(!originalImage)console.log('originalImage 找不到原始图片')
props.stateManager.nodeManager.createCardsSelect({
data: { tier: tier_, superiorID: props.node.id }
data: { tier: tier_, superiorID: props.node.id, originalImage }
})
}
const posCenter = computed(() => {

View File

@@ -60,7 +60,8 @@
data: {
tier: v.tier,
type: v.type,
superiorID
superiorID,
originalImage: props.node.data?.originalImage,
}
})
}

View File

@@ -36,6 +36,8 @@
import To3View from './to-3view.vue'
import To3DModel from './to-3d-model.vue'
import { toRealStyleApi } from '@/api/flow-canvas'
// import ToVideo from './to-video.vue'
// import AddPrint from './add-print.vue'
// import ToCAD from './to-cad.vue'
@@ -46,19 +48,20 @@
title: 'Advanced Tools',
component: CardsSelect,
hideFooter: true,
hideIcon: true
hideIcon: true,
},
{
tier: NODE_DATATIER.TO_REAL_STYLE,
type: NODE_DATATYPE.TO_REAL_STYLE,
title: 'To Real Style',
component: ToRealStyle
component: ToRealStyle,
api: toRealStyleApi
},
{
tier: NODE_DATATIER.SURFACE_EDIT,
type: NODE_DATATYPE.SURFACE_EDIT,
title: 'Surface Edit',
component: SurfaceEdit
component: SurfaceEdit,
},
{
tier: NODE_DATATIER.SCENE_COMPOSITION,
@@ -100,6 +103,10 @@
| 'to-3view',
default: 'to-real-style'
},
sketchId: {
type: String,
default: ''
},
data: {
type: Object,
default: () => ({})
@@ -110,17 +117,32 @@
return components.find((item) => item.type === props.type)
})
const componentRef = ref(null)
const onGenerateClick = () => {
const onGenerateClick = async () => {
const data = componentRef.value.data
const subordNode = stateManager.getSubordNodeByID(attrs.node.id)
emit('update-data', data)
console.log(attrs.node,data)
if(!attrs.node?.data?.originalImage)console.log('originalImage 找不到原始图片')
const apiData = {
sketchId: props.sketchId,
mode: data.mode,
size: data.pixelRatio,
imageUrl: attrs.node?.data?.originalImage,
userPrompt: data.prompt,
}
const taskList = await currentComponent.value.api(apiData).then((rv)=>{
return rv
}) || []
if (!subordNode) {
nodeManager.createResultNode({
data: {
superiorID: attrs.node.id,
tier: currentComponent.value.tier,
data: {
url: 'https://s3-alpha-sig.figma.com/img/ea2f/590e/9638f62a2fc91e31f33db0022db1642c?Expires=1773014400&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=M0B8oJJOk~dGG0aZAqOIocAp7T0LFdJ9FYmCrEZVTCRzYxM6SJRNtYMTX-rTO3Z~s14QINh~o-S41XiZnBv-0zcKjuWot~VVaNHfd0~1LesfNe2KwvCinT~72btFut1pheLnKE-wWCX5ewtonxU77bnw386YPMTqv7DBZzksf2udsJA7NmOYD6~TUG3Q2dWSt~zPH~lkaidscPqpCnCbqzljCEi4RiHY4U3A45l5XypcX2umqn1UaYUFCTqV9471J4qdB6Dg2pcKocdp-7-3s1De6Q~2SmBOrSgDQ~KEADCB2lhKfhxgWmy0lwMvhTd4l90ygVZDWZRABgjHNrGUvg__'
imageProcessTasks:taskList,
selectTaskId:taskList[0].taskId,
}
}
})

View File

@@ -1,49 +1,56 @@
<template>
<!-- 结果图片 -->
<div class="result-image">
<div class="header" v-show="showHeader" @mousedown.stop>
<span class="icon">
<svg-icon name="chat-compose" size="20" size-unit="px" />
</span>
<span class="icon" @click="onPreview">
<svg-icon name="expand-lg" size="20" size-unit="px" />
</span>
<span class="icon" @click="onDownload">
<svg-icon name="download" size="20" size-unit="px" />
</span>
<button class="edit" @click="onEdit">
<span class="icon"><svg-icon name="edit" size="13" /></span>
<span class="text">Edit</span>
</button>
</div>
<img
class="image"
:src="data.url"
:style="{ transform: `scale(${data?.scale?.x || 1}, ${data?.scale?.y || 1})` }"
/>
<div class="more" @click="showMenu = !showMenu" @mousedown.stop>
<svg-icon name="more" size="24" size-unit="px" color="#C9C9C9" />
</div>
<div class="menu" v-show="showMenu" @mousedown.stop>
<div
v-for="(v, i) in menus"
:key="i"
:class="[v.isDivide ? 'divide' : 'item', { disabled: v.disabled }]"
@click="onMenuItem(v)"
>
<template v-if="!v.isDivide">
<span class="label">{{ v.label }}</span>
<span class="tip">{{ v.tip }}</span>
</template>
<div class="result-image-container" :class="{'show-border': data.imageProcessTasks.length > 1}">
<div class="result-image"
v-for="(item, i) in data.imageProcessTasks"
:key="item.taskId"
:class="{'active': item.taskId === data.selectTaskId}"
@click="setSelectTaskId(item.taskId)"
>
<div class="header" v-show="showHeader && item.taskId === data.selectTaskId" @mousedown.stop>
<span class="icon">
<svg-icon name="chat-compose" size="20" size-unit="px" />
</span>
<span class="icon" @click="onPreview(item?.url)">
<svg-icon name="expand-lg" size="20" size-unit="px" />
</span>
<span class="icon" @click="onDownload(item?.url)">
<svg-icon name="download" size="20" size-unit="px" />
</span>
<button class="edit" @click="onEdit(item?.url)">
<span class="icon"><svg-icon name="edit" size="13" /></span>
<span class="text">Edit</span>
</button>
</div>
<img
class="image"
:src="item?.url"
:style="{ transform: `scale(${item?.scale?.x || 1}, ${item?.scale?.y || 1})` }"
/>
<div class="more" @click="clickimageProcessTaskItem(item.taskId)" @mousedown.stop>
<svg-icon name="more" size="24" size-unit="px" color="#C9C9C9" />
</div>
<div class="menu" v-show="showMenu && item.taskId === clickTaskId" @mousedown.stop>
<div
v-for="(v, i) in menus"
:key="i"
:class="[v.isDivide ? 'divide' : 'item', { disabled: v.disabled }]"
@click="onMenuItem(v)"
>
<template v-if="!v.isDivide">
<span class="label">{{ v.label }}</span>
<span class="tip">{{ v.tip }}</span>
</template>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import myEvent from '@/utils/myEvent'
import { downloadImage } from '../../../tools/tools'
import { reactive, ref, onBeforeUnmount, useAttrs, inject, watch } from 'vue'
import { reactive, ref, onBeforeUnmount, useAttrs, inject, watch, } from 'vue'
const openImagePreview = inject('openImagePreview') as (url: string) => void
const props = defineProps({
config: {
@@ -65,10 +72,21 @@
const attrs = useAttrs()
const showHeader = ref(!!attrs.node?.data?.isHeader)
const showMenu = ref(false)
const clickTaskId = ref('')
const clickimageProcessTaskItem = (taskId: string) => {
if(clickTaskId.value == taskId){
showMenu.value = !showMenu.value
}
clickTaskId.value = taskId
}
const data = reactive({
url: props.data?.url || '',
scale: props.data?.scale || { x: 1, y: 1 }
selectTaskId: props.data?.selectTaskId || '',
imageProcessTasks: props.data?.imageProcessTasks || [],
})
const setSelectTaskId = (taskId: string) => {
data.selectTaskId = taskId
emit('update-data', data)
}
watch(
() => props.data.url,
(newVal) => {
@@ -76,34 +94,49 @@
}
)
const menus = ref([
{ label: 'Copy', tip: 'Ctrl+C', on: () => emit('copy-node') },
{ label: 'Copy', tip: 'Ctrl+C', on: () => emit('copy-node', clickTaskId.value) },
{
label: 'Delete',
tip: 'Del',
on: () => emit('delete-node'),
on: () => {
if(data.imageProcessTasks.length > 1){
if(clickTaskId.value){
data.imageProcessTasks = data.imageProcessTasks.filter((item) => item.taskId !== clickTaskId.value)
clickTaskId.value = ''
emit('update-data', data)
}
}else{
emit('delete-node')
}
},
disabled: !!props.config?.disableDelete
},
{ isDivide: true },
{
label: 'Bring to font',
tip: '',
on: () => {
emit('bring-to-font')
}
},
{
label: 'Send to back',
tip: '',
on: () => {
emit('send-to-back')
}
},
{ isDivide: true },
// {
// label: 'Bring to font',
// tip: '',
// on: () => {
// emit('bring-to-font')
// }
// },
// {
// label: 'Send to back',
// tip: '',
// on: () => {
// emit('send-to-back')
// }
// },
// { isDivide: true },
{
label: 'Flip horizontal',
tip: '',
on: () => {
data.scale.x = -data.scale.x
data.imageProcessTasks.forEach((item) => {
if(!item.scale){
item.scale = { x: 1, y: 1 }
}
item.scale.x = -item.scale.x
})
emit('update-data', data)
}
},
@@ -111,16 +144,21 @@
label: 'Flip vertical',
tip: '',
on: () => {
data.scale.y = -data.scale.y
data.imageProcessTasks.forEach((item) => {
if(!item.scale){
item.scale = { x: 1, y: 1 }
}
item.scale.y = -item.scale.y
})
emit('update-data', data)
}
}
])
const onPreview = () => {
openImagePreview(data.url)
const onPreview = (url: string) => {
openImagePreview(url)
}
const onDownload = () => {
downloadImage(data.url, 'image.png')
const onDownload = (url: string) => {
downloadImage(url, 'image.png')
}
const onMenuItem = (v) => {
if (v.disabled) return
@@ -129,9 +167,10 @@
}
const hideMenu = () => {
showMenu.value = false
clickTaskId.value = ''
}
const onEdit = () => {
myEvent.emit('openDepthCanvas', { url: data.url })
const onEdit = (url: string) => {
myEvent.emit('openDepthCanvas', { url })
}
document.addEventListener('mousedown', hideMenu)
onBeforeUnmount(() => {
@@ -141,6 +180,16 @@
</script>
<style lang="less" scoped>
.result-image-container{
&.show-border{
border: 1px solid #d9d9d9;
border-radius: 9.4px;
padding: 16px;
display: grid;
grid-template-columns: repeat(2, 1fr); /* 一行3列 */
gap: 8px;
}
}
.result-image {
width: 244px;
border-radius: 16px;

View File

@@ -31,10 +31,11 @@
:active="stateManager.activeNodeID.value === node.id"
:node="node"
:config="node.data"
:sketchId="config.imgId"
:data="node.data.data"
v-bind="node.data"
@delete-node="deleteNode(node.id)"
@copy-node="copyNode(node.id)"
@copy-node="copyNode($event,node.id)"
@update-data="(v) => (node.data.data = v)"
@bring-to-font="bringToFont(node.id)"
@send-to-back="sendToBack(node.id)"
@@ -157,8 +158,8 @@
nodeManager.deleteNode(id)
}
/** 复制节点 */
const copyNode = (id) => {
nodeManager.copyNodeById(id)
const copyNode = (clickTaskId,id) => {
nodeManager.copyNodeById(clickTaskId,id)
}
/** 节点zIndex设置最大 */
const bringToFont = (id) => {
@@ -171,11 +172,11 @@
// 导出流程
const exportFlow = () => {
// flowManager.exportFlow()
const str = JSON.stringify(stateManager.nodes.value)
const json = JSON.parse(str)
putSketchFlowCanvas({
id: props.config.id || '==========',
id: props.config.imgId,
canvasData: str
}).then((res) => {
if (res) {
@@ -214,10 +215,9 @@
// window['nodes'] = nodes
let json = []
await new Promise((resolve) => {
getSketchFlowCanvas({ id: props.config.id || '==========' }).then((res) => {
getSketchFlowCanvas({ id: props.config.imgId }).then((res:any) => {
if (res) {
console.log(res)
json = res.data
json = JSON.parse(res)
}
resolve(true)
}).catch(() => {
@@ -227,12 +227,21 @@
if(json.length > 0){
importFlow(json)
}else{
const timestamp = Date.now()
nodeManager.createResultNode({
data: {
disableDelete: true,
isHeader: false,
data: {
url: props.config.url
imageProcessTasks:[
{
id: props.config.imgId,
url: props.config.url,
state:'success',
taskId: timestamp + '',
},
],
selectTaskId: timestamp + '',
}
}
})

View File

@@ -0,0 +1,33 @@
import { getTaskidResult } from '@/api/flow-canvas'
interface NodeOptions {
id?: string
position?: { x: number, y: number }
positionX?: number
positionY?: number
component?: any
}
export class GenerateManager {
stateManager: any
taskIds: string[] = []
constructor(options) {
this.stateManager = options.stateManager;
}
/** 删除taskId */
deleteTaskId(taskId: string) {
}
/** 添加taskId */
addTaskId(TaskId: any) {
this.stateManager.addTaskId(TaskId)
}
/** 添加taskId */
async getTasksIdImg(list) {
let taskIds = list.map((item)=>item.taskId)
getTaskidResult({taskIds}).then((rv)=>{
console.log(rv)
})
}
}

View File

@@ -9,6 +9,7 @@ interface NodeData {
superiorID?: string// 上级节点ID
disableDelete?: boolean// 是否禁用删除
disableCopy?: boolean// 是否禁用复制
originalImage?: string// 要进行生成的图片
}
interface NodeOptions {
id?: string
@@ -113,13 +114,15 @@ export class NodeManager {
return this.createNode(options_)
}
copyNodeById(id: string) {
copyNodeById(clickTaskId:string, id: string) {
const node = this.stateManager.getNodeById(id)
let copyNode = JSON.parse(JSON.stringify(node))
copyNode.data.data.imageProcessTasks = copyNode.data.data.imageProcessTasks.filter((item:any)=>item.taskId == clickTaskId)
const flowNode = this.stateManager.flowManager.getNodeById(id)
if (!node) return console.warn(`${id}找不到对应节点`)
if (node.data?.disableCopy) return console.warn(`${id}节点已禁用复制`)
const node_ = {
...JSON.parse(JSON.stringify(node)),
...copyNode,
id: createId(),
position: {
x: node.position.x,