Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front
This commit is contained in:
@@ -79,17 +79,17 @@ export const getTaskidResult = (data:taskIdList) => {
|
|||||||
* 图片转真是风格
|
* 图片转真是风格
|
||||||
* @param data 图片转真是风格的参数
|
* @param data 图片转真是风格的参数
|
||||||
* @param data.sketchId sketch id
|
* @param data.sketchId sketch id
|
||||||
|
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
|
||||||
* @param data.mode 选择的模型
|
* @param data.mode 选择的模型
|
||||||
* @param data.size 生成图片的大小
|
* @param data.size 生成图片的大小
|
||||||
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
|
|
||||||
* @param data.userPrompt 生成图片的提示词
|
* @param data.userPrompt 生成图片的提示词
|
||||||
* @returns 图片转真是风格
|
* @returns 图片转真是风格
|
||||||
*/
|
*/
|
||||||
export interface toRealStyleData {
|
export interface toRealStyleData {
|
||||||
sketchId?: string
|
sketchId?: string
|
||||||
|
imageUrl?: string
|
||||||
mode?: string
|
mode?: string
|
||||||
size?: string
|
size?: string
|
||||||
imageUrl?: string
|
|
||||||
userPrompt?: string
|
userPrompt?: string
|
||||||
}
|
}
|
||||||
export const toRealStyleApi = (data:toRealStyleData) => {
|
export const toRealStyleApi = (data:toRealStyleData) => {
|
||||||
@@ -98,11 +98,66 @@ export const toRealStyleApi = (data:toRealStyleData) => {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data:{
|
data:{
|
||||||
sketchId: data.sketchId,
|
sketchId: data.sketchId,
|
||||||
|
imageUrl: data.imageUrl,
|
||||||
mode: data.mode,
|
mode: data.mode,
|
||||||
size: data.size,
|
size: data.size,
|
||||||
imageUrl: data.imageUrl,
|
|
||||||
userPrompt: data.userPrompt
|
userPrompt: data.userPrompt
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 线稿图上色
|
||||||
|
* @param data 线稿图上色的参数
|
||||||
|
* @param data.sketchId sketch id
|
||||||
|
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
|
||||||
|
* @param data.variantCount 生成图片的数量
|
||||||
|
* @param data.colors 生成上色的图片颜色列表
|
||||||
|
* @returns 线稿图上色
|
||||||
|
*/
|
||||||
|
export interface toColorPaletteData {
|
||||||
|
sketchId?: string
|
||||||
|
imageUrl?: string
|
||||||
|
variantCount?: string
|
||||||
|
colors?: Array<string>
|
||||||
|
}
|
||||||
|
export const toColorPaletteApi = (data:toColorPaletteData) => {
|
||||||
|
return request({
|
||||||
|
url: `/api/image/color-palette`,
|
||||||
|
method: 'post',
|
||||||
|
data:{
|
||||||
|
sketchId: data.sketchId,
|
||||||
|
imageUrl: data.imageUrl,
|
||||||
|
variantCount: data.variantCount,
|
||||||
|
colors: data.colors
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景构图
|
||||||
|
* @param data 场景构图的参数
|
||||||
|
* @param data.sketchId sketch id
|
||||||
|
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
|
||||||
|
* @param data.styles 生成上色的图片颜色列表
|
||||||
|
* @param data.userPrompt 生成上色的图片颜色列表
|
||||||
|
* @returns 场景构图
|
||||||
|
*/
|
||||||
|
export interface toSceneCompositionData {
|
||||||
|
sketchId?: string
|
||||||
|
imageUrl?: string
|
||||||
|
userPrompt?: string
|
||||||
|
styles?: Array<string>
|
||||||
|
}
|
||||||
|
export const toSceneCompositionApi = (data:toSceneCompositionData) => {
|
||||||
|
return request({
|
||||||
|
url: `/api/image/scene-composition`,
|
||||||
|
method: 'post',
|
||||||
|
data:{
|
||||||
|
sketchId: data.sketchId,
|
||||||
|
imageUrl: data.imageUrl,
|
||||||
|
userPrompt: data.userPrompt,
|
||||||
|
styles: data.styles
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
src/assets/images/generateLoading.png
Normal file
BIN
src/assets/images/generateLoading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -51,7 +51,8 @@
|
|||||||
const activeObject = ref(null)
|
const activeObject = ref(null)
|
||||||
const updateActiveObject = () => {
|
const updateActiveObject = () => {
|
||||||
const obj = layers.value.find((v: any) => v.info.id === activeID.value)
|
const obj = layers.value.find((v: any) => v.info.id === activeID.value)
|
||||||
activeObject.value = obj?.toJSON('info') || null
|
// activeObject.value = obj?.toJSON('info') || null
|
||||||
|
activeObject.value = obj
|
||||||
}
|
}
|
||||||
watch(layers, () => updateActiveObject())
|
watch(layers, () => updateActiveObject())
|
||||||
watch(activeID, () => updateActiveObject())
|
watch(activeID, () => updateActiveObject())
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layer-item" @click="onClickLayer">
|
<div class="layer-item" @click="onClickLayer">
|
||||||
<div class="drag"><svg-icon name="dc-drag" size="18" /></div>
|
<div class="drag"><svg-icon name="dc-drag" size="18" /></div>
|
||||||
<div class="thumb"></div>
|
<div class="thumb">
|
||||||
|
<img v-if="layer.thumbnail" :src="layer.thumbnail" />
|
||||||
|
</div>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<div @dblclick="onClickEditName" v-if="!editName" :title="layer.info.name">
|
<div @dblclick="onClickEditName" v-if="!editName" :title="layer.info.name">
|
||||||
{{ layer.info.name || '未命名图层' }}
|
{{ layer.info.name || '未命名图层' }}
|
||||||
@@ -50,14 +52,14 @@
|
|||||||
editName.value = false
|
editName.value = false
|
||||||
const name = props.layer.info.name
|
const name = props.layer.info.name
|
||||||
if (name !== value) {
|
if (name !== value) {
|
||||||
layerManager.setLayerNameByID(props.layer.info.id, value)
|
layerManager.setLayerNameById(props.layer.info.id, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const onClickShowHide = () => {
|
const onClickShowHide = () => {
|
||||||
layerManager.setLayerVisibleByID(props.layer.info.id, !props.layer.visible)
|
layerManager.setLayerVisibleById(props.layer.info.id, !props.layer.visible)
|
||||||
}
|
}
|
||||||
const onClickDelete = () => {
|
const onClickDelete = () => {
|
||||||
layerManager.deleteLayerByID(props.layer.info.id)
|
layerManager.deleteLayerById(props.layer.info.id)
|
||||||
}
|
}
|
||||||
const onClickLayer = () => {
|
const onClickLayer = () => {
|
||||||
layerManager.setActiveID(props.layer.info.id)
|
layerManager.setActiveID(props.layer.info.id)
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { fabric } from 'fabric-with-all'
|
||||||
|
/** 智能框选工具管理器 */
|
||||||
|
export class AISelectboxToolManager {
|
||||||
|
// 管理器
|
||||||
|
canvasManager: any
|
||||||
|
stateManager: any
|
||||||
|
layerManager: any
|
||||||
|
|
||||||
|
isDragging: boolean = false
|
||||||
|
startX: number = 0
|
||||||
|
startY: number = 0
|
||||||
|
demoObject: any
|
||||||
|
constructor(options) {
|
||||||
|
this.canvasManager = options.canvasManager
|
||||||
|
this.stateManager = options.stateManager
|
||||||
|
this.layerManager = options.layerManager
|
||||||
|
}
|
||||||
|
mouseDownEvent(e) {
|
||||||
|
this.isDragging = true
|
||||||
|
this.startX = e.absolutePointer.x
|
||||||
|
this.startY = e.absolutePointer.y
|
||||||
|
const rect = new fabric.Rect({
|
||||||
|
left: this.startX,
|
||||||
|
top: this.startY,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
fill: 'transparent',
|
||||||
|
stroke: '#000',
|
||||||
|
strokeWidth: 1,
|
||||||
|
evented: false,
|
||||||
|
})
|
||||||
|
this.demoObject = rect
|
||||||
|
this.canvasManager.canvas.add(rect)
|
||||||
|
this.canvasManager.canvas.renderAll()
|
||||||
|
}
|
||||||
|
mouseMoveEvent(e) {
|
||||||
|
if (!this.isDragging) return;
|
||||||
|
var width = e.absolutePointer.x - this.startX
|
||||||
|
var height = e.absolutePointer.y - this.startY
|
||||||
|
var left = this.startX
|
||||||
|
var top = this.startY
|
||||||
|
if(width < 0) {
|
||||||
|
left += width
|
||||||
|
width = -width
|
||||||
|
}
|
||||||
|
if(height < 0) {
|
||||||
|
top += height
|
||||||
|
height = -height
|
||||||
|
}
|
||||||
|
this.demoObject.set({ width, height, left, top })
|
||||||
|
this.canvasManager.canvas.renderAll()
|
||||||
|
}
|
||||||
|
mouseUpEvent(e) {
|
||||||
|
if (!this.isDragging) return;
|
||||||
|
this.isDragging = false;
|
||||||
|
const object = this.demoObject.toJSON("evented")
|
||||||
|
if (object.width === 0) object.width = 100
|
||||||
|
if (object.height === 0) object.height = 100
|
||||||
|
// console.log(object)
|
||||||
|
this.canvasManager.canvas.remove(this.demoObject)
|
||||||
|
this.canvasManager.canvas.renderAll()
|
||||||
|
|
||||||
|
}
|
||||||
|
dispose() { }
|
||||||
|
}
|
||||||
@@ -6,6 +6,22 @@ import { detectDeviceType } from '../tools/index'
|
|||||||
import { CanvasEventManager } from "./events/CanvasEventManager";
|
import { CanvasEventManager } from "./events/CanvasEventManager";
|
||||||
import { OperationType } from '../tools/layerHelper'
|
import { OperationType } from '../tools/layerHelper'
|
||||||
|
|
||||||
|
// 自定义画布转对象属性
|
||||||
|
fabric.Object.prototype.customProperties = ["top", "left", "width", "height", "scaleX", "scaleY", "info", "thumbnail"];
|
||||||
|
fabric.Object.prototype.toObject_ = fabric.Object.prototype.toObject
|
||||||
|
fabric.Object.prototype.toObject = function () {
|
||||||
|
const args = [...arguments]
|
||||||
|
const arr = [...fabric.Object.prototype.customProperties]
|
||||||
|
args.forEach(v => {
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
arr.push(v)
|
||||||
|
} else if (Array.isArray(v)) {
|
||||||
|
arr.push(...v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return this.toObject_(arr)
|
||||||
|
}
|
||||||
|
|
||||||
interface CanvasInitOptions {
|
interface CanvasInitOptions {
|
||||||
canvasRef: any
|
canvasRef: any
|
||||||
canvasViewWidth?: number
|
canvasViewWidth?: number
|
||||||
@@ -43,8 +59,6 @@ export class CanvasManager {
|
|||||||
this.canvas = createCanvas(options.canvasRef.value, {
|
this.canvas = createCanvas(options.canvasRef.value, {
|
||||||
preserveObjectStacking: true,
|
preserveObjectStacking: true,
|
||||||
enableRetinaScaling: true,
|
enableRetinaScaling: true,
|
||||||
stopContextMenu: true,
|
|
||||||
fireRightClick: true,
|
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
})
|
})
|
||||||
this.setCanvasViewSize(options)
|
this.setCanvasViewSize(options)
|
||||||
@@ -118,7 +132,7 @@ export class CanvasManager {
|
|||||||
this.animationManager.setupInteractionAnimations();
|
this.animationManager.setupInteractionAnimations();
|
||||||
}
|
}
|
||||||
/** 设置激活对象 */
|
/** 设置激活对象 */
|
||||||
setActiveObjectByID(id: string) {
|
setActiveObjectById(id: string) {
|
||||||
const obj = this.getObjectById(id)
|
const obj = this.getObjectById(id)
|
||||||
console.log(obj)
|
console.log(obj)
|
||||||
if (obj) this.canvas.setActiveObject(obj)
|
if (obj) this.canvas.setActiveObject(obj)
|
||||||
@@ -184,8 +198,7 @@ export class CanvasManager {
|
|||||||
|
|
||||||
/** 导出画布为JSON */
|
/** 导出画布为JSON */
|
||||||
getCanvasJSON() {
|
getCanvasJSON() {
|
||||||
const keys = ["top", "left", "width", "height", "scaleX", "scaleY", "info",]
|
const json = this.canvas.toJSON()
|
||||||
const json = this.canvas.toJSON(keys)
|
|
||||||
return JSON.stringify(json)
|
return JSON.stringify(json)
|
||||||
}
|
}
|
||||||
/** 加载画布JSON */
|
/** 加载画布JSON */
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { fabric } from 'fabric-with-all'
|
import { fabric } from 'fabric-with-all'
|
||||||
import { createId } from '../../tools/tools'
|
import { createId } from '../../tools/tools'
|
||||||
import { exportObjectsToImage } from '../tools/exportMethod'
|
import { exportObjectsToImage, exportObjectToThumbnail } from '../tools/exportMethod'
|
||||||
import { OperationType } from '../tools/layerHelper'
|
import { OperationType } from '../tools/layerHelper'
|
||||||
|
|
||||||
export class LayerManager {
|
export class LayerManager {
|
||||||
@@ -18,22 +18,22 @@ export class LayerManager {
|
|||||||
setActiveID(id: string, isActive = true) {
|
setActiveID(id: string, isActive = true) {
|
||||||
this.activeID.value = id
|
this.activeID.value = id
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
this.canvasManager.setActiveObjectByID(id)
|
this.canvasManager.setActiveObjectById(id)
|
||||||
this.stateManager.toolManager.setTool(OperationType.SELECT)
|
this.stateManager.toolManager.setTool(OperationType.SELECT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getLayerByID(id) {
|
getLayerById(id) {
|
||||||
return this.layers.value.find((item: any) => item.info.id === id)
|
return this.layers.value.find((item: any) => item.info.id === id)
|
||||||
}
|
}
|
||||||
setLayerNameByID(id, name: string) {
|
setLayerNameById(id, name: string) {
|
||||||
const layer = this.getLayerByID(id)
|
const layer = this.getLayerById(id)
|
||||||
if (layer) {
|
if (layer) {
|
||||||
layer.info.name = name
|
layer.info.name = name
|
||||||
this.canvasManager.renderAll()
|
this.canvasManager.renderAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setLayerVisibleByID(id, visible: boolean) {
|
setLayerVisibleById(id, visible: boolean) {
|
||||||
const layer = this.getLayerByID(id)
|
const layer = this.getLayerById(id)
|
||||||
if (layer) {
|
if (layer) {
|
||||||
layer.set({
|
layer.set({
|
||||||
visible: visible
|
visible: visible
|
||||||
@@ -41,7 +41,7 @@ export class LayerManager {
|
|||||||
this.canvasManager.renderAll()
|
this.canvasManager.renderAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteLayerByID(id, isActive = true) {
|
deleteLayerById(id, isActive = true) {
|
||||||
this.canvasManager.deleteObjectById(id)
|
this.canvasManager.deleteObjectById(id)
|
||||||
if (id === this.activeID.value && isActive) {
|
if (id === this.activeID.value && isActive) {
|
||||||
this.setActiveID(this.layers.value[0]?.info?.id || "")
|
this.setActiveID(this.layers.value[0]?.info?.id || "")
|
||||||
@@ -54,18 +54,10 @@ export class LayerManager {
|
|||||||
}
|
}
|
||||||
// 更新图层列表
|
// 更新图层列表
|
||||||
updateLayers() {
|
updateLayers() {
|
||||||
this.layers.value = this.canvasManager.getObjects().filter((v: any) => !!v?.info?.id).reverse()
|
this.layers.value = this.canvasManager.getObjects()
|
||||||
window["layers"] = this.layers
|
.filter((v: any) => !!v?.info?.id)
|
||||||
}
|
.reverse()
|
||||||
// 更新图层参数
|
.map(v => v.toObject())
|
||||||
updateLayerParams(layer, keys = []) {
|
|
||||||
this.layers.value.forEach((item: any) => {
|
|
||||||
if (item.info.id === layer.info.id) {
|
|
||||||
keys.forEach((key: string) => {
|
|
||||||
item.set(key, layer[key])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 设置图层位置-不设置默认居中 */
|
/** 设置图层位置-不设置默认居中 */
|
||||||
@@ -202,7 +194,7 @@ export class LayerManager {
|
|||||||
})
|
})
|
||||||
// console.log(mergedImage)
|
// console.log(mergedImage)
|
||||||
const index = this.canvasManager.getObjects().indexOf(targetLayer);
|
const index = this.canvasManager.getObjects().indexOf(targetLayer);
|
||||||
this.deleteLayerByID(targetLayer.info.id, false)
|
this.deleteLayerById(targetLayer.info.id, false)
|
||||||
this.setActiveID(mergedImage.info.id, false)
|
this.setActiveID(mergedImage.info.id, false)
|
||||||
this.canvasManager.add(mergedImage, false);
|
this.canvasManager.add(mergedImage, false);
|
||||||
this.canvasManager.canvas.moveTo(mergedImage, index);
|
this.canvasManager.canvas.moveTo(mergedImage, index);
|
||||||
@@ -219,5 +211,19 @@ export class LayerManager {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/** 更新图层缩略图 */
|
||||||
|
async updateLayerThumbnailsById(id: string) {
|
||||||
|
const object = this.canvasManager.getObjectById(id);
|
||||||
|
if (!object) return;
|
||||||
|
const url = await exportObjectToThumbnail(object);
|
||||||
|
object.set({
|
||||||
|
thumbnail: url
|
||||||
|
})
|
||||||
|
// object.thumbnail = url
|
||||||
|
this.updateLayers()
|
||||||
|
// this.canvasManager.renderAll()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
dispose() { }
|
dispose() { }
|
||||||
}
|
}
|
||||||
@@ -33,28 +33,27 @@ export class RectToolManager {
|
|||||||
}
|
}
|
||||||
mouseMoveEvent(e) {
|
mouseMoveEvent(e) {
|
||||||
if (!this.isDragging) return;
|
if (!this.isDragging) return;
|
||||||
const width = e.absolutePointer.x - this.startX
|
var width = e.absolutePointer.x - this.startX
|
||||||
const height = e.absolutePointer.y - this.startY
|
var height = e.absolutePointer.y - this.startY
|
||||||
this.demoObject.set({ width, height })
|
var left = this.startX
|
||||||
|
var top = this.startY
|
||||||
|
if (width < 0) {
|
||||||
|
left += width
|
||||||
|
width = -width
|
||||||
|
}
|
||||||
|
if (height < 0) {
|
||||||
|
top += height
|
||||||
|
height = -height
|
||||||
|
}
|
||||||
|
this.demoObject.set({ width, height, left, top })
|
||||||
this.canvasManager.canvas.renderAll()
|
this.canvasManager.canvas.renderAll()
|
||||||
}
|
}
|
||||||
mouseUpEvent(e) {
|
mouseUpEvent(e) {
|
||||||
if (!this.isDragging) return;
|
if (!this.isDragging) return;
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
var width = e.absolutePointer.x - this.startX
|
|
||||||
var height = e.absolutePointer.y - this.startY
|
|
||||||
if(width === 0) width = 50
|
|
||||||
if(height === 0) height = 50
|
|
||||||
this.demoObject.set({ width, height })
|
|
||||||
const object = this.demoObject.toJSON("evented")
|
const object = this.demoObject.toJSON("evented")
|
||||||
if(object.width < 0) {
|
if (object.width === 0) object.width = 100
|
||||||
object.left += object.width
|
if (object.height === 0) object.height = 100
|
||||||
object.width = -object.width
|
|
||||||
}
|
|
||||||
if(object.height < 0) {
|
|
||||||
object.top += object.height
|
|
||||||
object.height = -object.height
|
|
||||||
}
|
|
||||||
this.layerManager.createRectLayer(object, true)
|
this.layerManager.createRectLayer(object, true)
|
||||||
this.canvasManager.canvas.remove(this.demoObject)
|
this.canvasManager.canvas.remove(this.demoObject)
|
||||||
this.canvasManager.canvas.renderAll()
|
this.canvasManager.canvas.renderAll()
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { isBoolean } from "lodash-es";
|
import { isBoolean } from "lodash-es";
|
||||||
import { OperationType, OperationTypes } from "../../tools/layerHelper";
|
import { OperationType, OperationTypes } from "../../tools/layerHelper";
|
||||||
import { RectToolManager } from "../RectToolManager"
|
import { RectToolManager } from "../RectToolManager"
|
||||||
|
import { AISelectboxToolManager } from "../AISelectboxToolManager"
|
||||||
|
|
||||||
|
|
||||||
export class CanvasEventManager {
|
export class CanvasEventManager {
|
||||||
constructor(canvas, options = {}) {
|
constructor(canvas, options = {}) {
|
||||||
@@ -31,6 +33,7 @@ export class CanvasEventManager {
|
|||||||
layerManager: this.layerManager,
|
layerManager: this.layerManager,
|
||||||
}
|
}
|
||||||
this.rectToolManager = new RectToolManager(managers)
|
this.rectToolManager = new RectToolManager(managers)
|
||||||
|
this.aiSelectboxToolManager = new AISelectboxToolManager(managers)
|
||||||
|
|
||||||
// 初始化所有事件
|
// 初始化所有事件
|
||||||
this.initEvents();
|
this.initEvents();
|
||||||
@@ -205,6 +208,7 @@ export class CanvasEventManager {
|
|||||||
// 橡皮擦模式
|
// 橡皮擦模式
|
||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
|
this.aiSelectboxToolManager.mouseDownEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (currentTool === OperationType.RECTANGLE) {
|
||||||
// 矩形模式
|
// 矩形模式
|
||||||
this.rectToolManager.mouseDownEvent(opt);
|
this.rectToolManager.mouseDownEvent(opt);
|
||||||
@@ -232,6 +236,7 @@ export class CanvasEventManager {
|
|||||||
// 橡皮擦模式
|
// 橡皮擦模式
|
||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
|
this.aiSelectboxToolManager.mouseMoveEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (currentTool === OperationType.RECTANGLE) {
|
||||||
// 矩形模式
|
// 矩形模式
|
||||||
this.rectToolManager.mouseMoveEvent(opt);
|
this.rectToolManager.mouseMoveEvent(opt);
|
||||||
@@ -315,6 +320,7 @@ export class CanvasEventManager {
|
|||||||
// 橡皮擦模式
|
// 橡皮擦模式
|
||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
|
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (currentTool === OperationType.RECTANGLE) {
|
||||||
// 矩形模式
|
// 矩形模式
|
||||||
this.rectToolManager.mouseDownEvent(opt);
|
this.rectToolManager.mouseDownEvent(opt);
|
||||||
@@ -379,6 +385,7 @@ export class CanvasEventManager {
|
|||||||
// 橡皮擦模式
|
// 橡皮擦模式
|
||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
|
this.aiSelectboxToolManager.mouseMoveEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (currentTool === OperationType.RECTANGLE) {
|
||||||
// 矩形模式
|
// 矩形模式
|
||||||
this.rectToolManager.mouseMoveEvent(opt);
|
this.rectToolManager.mouseMoveEvent(opt);
|
||||||
@@ -488,6 +495,7 @@ export class CanvasEventManager {
|
|||||||
// 橡皮擦模式
|
// 橡皮擦模式
|
||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
|
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (currentTool === OperationType.RECTANGLE) {
|
||||||
// 矩形模式
|
// 矩形模式
|
||||||
this.rectToolManager.mouseUpEvent(opt);
|
this.rectToolManager.mouseUpEvent(opt);
|
||||||
@@ -659,6 +667,7 @@ export class CanvasEventManager {
|
|||||||
// 橡皮擦模式
|
// 橡皮擦模式
|
||||||
} else if (currentTool === OperationType.SELECTBOX) {
|
} else if (currentTool === OperationType.SELECTBOX) {
|
||||||
// 选择框模式
|
// 选择框模式
|
||||||
|
this.aiSelectboxToolManager.mouseUpEvent(opt);
|
||||||
} else if (currentTool === OperationType.RECTANGLE) {
|
} else if (currentTool === OperationType.RECTANGLE) {
|
||||||
// 矩形模式
|
// 矩形模式
|
||||||
this.rectToolManager.mouseUpEvent(opt);
|
this.rectToolManager.mouseUpEvent(opt);
|
||||||
@@ -722,6 +731,7 @@ export class CanvasEventManager {
|
|||||||
this.canvas.on("object:modified", (e) => {
|
this.canvas.on("object:modified", (e) => {
|
||||||
updateLayers(e);
|
updateLayers(e);
|
||||||
this.stateManager.recordState();
|
this.stateManager.recordState();
|
||||||
|
this.layerManager.updateLayerThumbnailsById(e.target.info.id);
|
||||||
});
|
});
|
||||||
this.canvas.on("object:removed", (e) => {
|
this.canvas.on("object:removed", (e) => {
|
||||||
updateLayers(e);
|
updateLayers(e);
|
||||||
@@ -1065,6 +1075,7 @@ export class CanvasEventManager {
|
|||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.rectToolManager?.dispose()
|
this.rectToolManager?.dispose()
|
||||||
|
this.aiSelectboxToolManager?.dispose()
|
||||||
// 移除所有事件监听
|
// 移除所有事件监听
|
||||||
this.canvas.off();
|
this.canvas.off();
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ export const createCanvas = (elementId, options = {}) => {
|
|||||||
// skipOffscreen: true, // 跳过离屏渲染
|
// skipOffscreen: true, // 跳过离屏渲染
|
||||||
imageSmoothingEnabled: true, // 启用图像平滑 - 抗锯齿
|
imageSmoothingEnabled: true, // 启用图像平滑 - 抗锯齿
|
||||||
imageSmoothingQuality: "high", // 设置高质量图像平滑
|
imageSmoothingQuality: "high", // 设置高质量图像平滑
|
||||||
|
fireMiddleClick: true,// 启用中键点击事件
|
||||||
|
fireRightClick: true,// 启用右键点击事件
|
||||||
|
stopContextMenu: true,// 阻止浏览器默认的右键菜单弹出,避免干扰
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { fabric } from 'fabric-with-all'
|
||||||
import { createStaticCanvas } from './canvasFactory'
|
import { createStaticCanvas } from './canvasFactory'
|
||||||
import { getObjectsBoundingBox, cloneObjects } from './canvasMethod'
|
import { getObjectsBoundingBox, cloneObjects } from './canvasMethod'
|
||||||
/** 导出画布为图片 */
|
/** 导出画布为图片 */
|
||||||
@@ -40,3 +41,32 @@ export async function exportObjectsToImage(objects = [], isDetails = false) {
|
|||||||
...boundingBox,
|
...boundingBox,
|
||||||
} : dataURL
|
} : dataURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 导出指定对象为缩略图 */
|
||||||
|
export async function exportObjectToThumbnail(object, width = 100, height = 100) {
|
||||||
|
const url = await exportObjectsToImage([object]);
|
||||||
|
const staticCanvas = createStaticCanvas(document.createElement('canvas'))
|
||||||
|
staticCanvas.setWidth(width)
|
||||||
|
staticCanvas.setHeight(height)
|
||||||
|
const image = await new Promise((resolve, reject) => {
|
||||||
|
fabric.Image.fromURL(url, (img) => {
|
||||||
|
const scale = Math.min(width / img.width, height / img.height)
|
||||||
|
const left = (width - img.width * scale) / 2
|
||||||
|
const top = (height - img.height * scale) / 2
|
||||||
|
img.set({
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
scaleX: scale,
|
||||||
|
scaleY: scale,
|
||||||
|
})
|
||||||
|
resolve(img)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
staticCanvas.add(image)
|
||||||
|
// 导出图片
|
||||||
|
const dataURL = staticCanvas.toDataURL({
|
||||||
|
type: 'image/png',
|
||||||
|
quality: 1,
|
||||||
|
})
|
||||||
|
return dataURL
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
import { Handle, Position } from '@vue-flow/core'
|
import { Handle, Position } from '@vue-flow/core'
|
||||||
import { NODE_TYPE } from '../tools/index.d'
|
import { NODE_TYPE } from '../tools/index.d'
|
||||||
import { NODE_DATATYPE, NODE_DATATIER } from '../tools/index.d'
|
import { NODE_DATATYPE, NODE_DATATIER } from '../tools/index.d'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, inject } from 'vue'
|
||||||
const handles = ref({
|
const handles = ref({
|
||||||
[NODE_TYPE.INPUT]: [{ id: 'Right', type: 'source', position: Position.Right }],
|
[NODE_TYPE.INPUT]: [{ id: 'Right', type: 'source', position: Position.Right }],
|
||||||
[NODE_TYPE.SECONDARY]: [
|
[NODE_TYPE.SECONDARY]: [
|
||||||
@@ -49,14 +49,19 @@
|
|||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const stateManager = inject('stateManager') as any
|
||||||
const nodes = computed(() => props.stateManager.nodes.value)
|
const nodes = computed(() => props.stateManager.nodes.value)
|
||||||
const isSubord = computed(() => nodes.value.some((v) => v.data.superiorID === props.node.id))
|
const isSubord = computed(() => nodes.value.some((v) => v.data.superiorID === props.node.id))
|
||||||
const tier = computed(() => Number(props.node?.data?.tier || 0))
|
const tier = computed(() => Number(props.node?.data?.tier || 0))
|
||||||
|
const isReturned = computed(() => {
|
||||||
|
return props.node.data.type == 'result-image' && props.node.data.data.imageProcessTasks[0].status == 'RETURNED'
|
||||||
|
})
|
||||||
const isAdd = computed(
|
const isAdd = computed(
|
||||||
() =>
|
() =>
|
||||||
!isSubord.value &&
|
!isSubord.value &&
|
||||||
NODE_DATATYPE.RESULT_IMAGE === props.node.data.type &&
|
NODE_DATATYPE.RESULT_IMAGE === props.node.data.type &&
|
||||||
!(tier.value === NODE_DATATIER.TO_3VIEW)
|
!(tier.value === NODE_DATATIER.TO_3VIEW) &&
|
||||||
|
isReturned.value
|
||||||
)
|
)
|
||||||
const onAdd = () => {
|
const onAdd = () => {
|
||||||
const tier_ = tier.value + 1
|
const tier_ = tier.value + 1
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
const data = reactive({
|
const data = reactive({
|
||||||
colors: []
|
colors: []
|
||||||
})
|
})
|
||||||
|
const emit = defineEmits(['update-data'])
|
||||||
const maxColor = 5
|
const maxColor = 5
|
||||||
const delColor = (i: number) => {
|
const delColor = (i: number) => {
|
||||||
data.colors.splice(i, 1)
|
data.colors.splice(i, 1)
|
||||||
@@ -41,7 +42,17 @@
|
|||||||
data.colors.push(target.value)
|
data.colors.push(target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ data })
|
const getApiData = ()=>{
|
||||||
|
return {
|
||||||
|
variantCount: '2',
|
||||||
|
colors: data.colors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ data, getApiData })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
import To3View from './to-3view.vue'
|
import To3View from './to-3view.vue'
|
||||||
import To3DModel from './to-3d-model.vue'
|
import To3DModel from './to-3d-model.vue'
|
||||||
|
|
||||||
import { toRealStyleApi } from '@/api/flow-canvas'
|
import { toRealStyleApi, toColorPaletteApi, toSceneCompositionApi } from '@/api/flow-canvas'
|
||||||
|
|
||||||
// import ToVideo from './to-video.vue'
|
// import ToVideo from './to-video.vue'
|
||||||
// import AddPrint from './add-print.vue'
|
// import AddPrint from './add-print.vue'
|
||||||
@@ -67,13 +67,15 @@
|
|||||||
tier: NODE_DATATIER.SCENE_COMPOSITION,
|
tier: NODE_DATATIER.SCENE_COMPOSITION,
|
||||||
type: NODE_DATATYPE.SCENE_COMPOSITION,
|
type: NODE_DATATYPE.SCENE_COMPOSITION,
|
||||||
title: 'Scene Composition',
|
title: 'Scene Composition',
|
||||||
component: SceneComposition
|
component: SceneComposition,
|
||||||
|
api: toSceneCompositionApi
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: NODE_DATATIER.COLOR_PALETTE,
|
tier: NODE_DATATIER.COLOR_PALETTE,
|
||||||
type: NODE_DATATYPE.COLOR_PALETTE,
|
type: NODE_DATATYPE.COLOR_PALETTE,
|
||||||
title: 'Color Palette',
|
title: 'Color Palette',
|
||||||
component: ColorPalette
|
component: ColorPalette,
|
||||||
|
api: toColorPaletteApi
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: NODE_DATATIER.TO_3D_MODEL,
|
tier: NODE_DATATIER.TO_3D_MODEL,
|
||||||
@@ -119,32 +121,36 @@
|
|||||||
const componentRef = ref(null)
|
const componentRef = ref(null)
|
||||||
|
|
||||||
const onGenerateClick = async () => {
|
const onGenerateClick = async () => {
|
||||||
const data = componentRef.value.data
|
const data = componentRef.value?.getApiData?.() || {}
|
||||||
const subordNode = stateManager.getSubordNodeByID(attrs.node.id)
|
const subordNode = stateManager.getSubordNodeById(attrs.node.id)
|
||||||
emit('update-data', data)
|
emit('update-data', componentRef.value?.data)
|
||||||
console.log(attrs.node,data)
|
|
||||||
if(!attrs.node?.data?.originalImage)console.log('originalImage 找不到原始图片')
|
if(!attrs.node?.data?.originalImage)console.log('originalImage 找不到原始图片')
|
||||||
|
|
||||||
const apiData = {
|
const apiData = {
|
||||||
sketchId: props.sketchId,
|
sketchId: props.sketchId,
|
||||||
mode: data.mode,
|
|
||||||
size: data.pixelRatio,
|
|
||||||
imageUrl: attrs.node?.data?.originalImage,
|
imageUrl: attrs.node?.data?.originalImage,
|
||||||
userPrompt: data.prompt,
|
...data,
|
||||||
}
|
}
|
||||||
const taskList = await currentComponent.value.api(apiData).then((rv)=>{
|
const taskList = await currentComponent.value.api(apiData).then((rv)=>{
|
||||||
return rv
|
return rv
|
||||||
}) || []
|
}) || []
|
||||||
|
// const taskList = [{taskId:'123'}]
|
||||||
|
console.log(taskList)
|
||||||
if (!subordNode) {
|
if (!subordNode) {
|
||||||
nodeManager.createResultNode({
|
taskList.forEach((item,index) => {
|
||||||
data: {
|
nodeManager.createResultNode({
|
||||||
superiorID: attrs.node.id,
|
positionY: index * (50 + 250),
|
||||||
tier: currentComponent.value.tier,
|
|
||||||
data: {
|
data: {
|
||||||
imageProcessTasks:taskList,
|
superiorID: attrs.node.id,
|
||||||
selectTaskId:taskList[0].taskId,
|
createIndexPosition: index,
|
||||||
|
tier: currentComponent.value.tier,
|
||||||
|
isActive: index == 0,
|
||||||
|
data: {
|
||||||
|
imageProcessTasks:[item],
|
||||||
|
selectTaskId:item.taskId,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
subordNode.data.data.url =
|
subordNode.data.data.url =
|
||||||
|
|||||||
@@ -56,7 +56,13 @@
|
|||||||
data.styles.push(value)
|
data.styles.push(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ data })
|
const getApiData = ()=>{
|
||||||
|
return {
|
||||||
|
styles: data.styles,
|
||||||
|
userPrompt: data.prompt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({ data, getApiData })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -54,10 +54,18 @@
|
|||||||
const data = reactive({
|
const data = reactive({
|
||||||
prompt: '',
|
prompt: '',
|
||||||
pixelRatio: '1:1',
|
pixelRatio: '1:1',
|
||||||
mode: 'Normal'
|
mode: 'Normal',
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({ data })
|
const getApiData = ()=>{
|
||||||
|
return {
|
||||||
|
mode: data.mode,
|
||||||
|
size: data.pixelRatio,
|
||||||
|
userPrompt: data.prompt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ data, getApiData })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
<div class="result-image"
|
<div class="result-image"
|
||||||
v-for="(item, i) in data.imageProcessTasks"
|
v-for="(item, i) in data.imageProcessTasks"
|
||||||
:key="item.taskId"
|
:key="item.taskId"
|
||||||
:class="{'active': item.taskId === data.selectTaskId}"
|
:class="{'active': config.isActive}"
|
||||||
@click="setSelectTaskId(item.taskId)"
|
@click="setSelectTaskId(item.taskId)"
|
||||||
>
|
>
|
||||||
<div class="header" v-show="showHeader && item.taskId === data.selectTaskId" @mousedown.stop>
|
<div class="header" v-if="item.status == 'RETURNED'" v-show="showHeader && item.taskId === data.selectTaskId" @mousedown.stop>
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<svg-icon name="chat-compose" size="20" size-unit="px" />
|
<svg-icon name="chat-compose" size="20" size-unit="px" />
|
||||||
</span>
|
</span>
|
||||||
@@ -24,10 +24,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
class="image"
|
class="image"
|
||||||
|
v-if="item.status == 'RETURNED'"
|
||||||
:src="item?.url"
|
:src="item?.url"
|
||||||
:style="{ transform: `scale(${item?.scale?.x || 1}, ${item?.scale?.y || 1})` }"
|
:style="{ transform: `scale(${item?.scale?.x || 1}, ${item?.scale?.y || 1})` }"
|
||||||
/>
|
/>
|
||||||
<div class="more" @click="clickimageProcessTaskItem(item.taskId)" @mousedown.stop>
|
<div class="status" v-else>
|
||||||
|
<div class="content">
|
||||||
|
<HighlightAdmin>
|
||||||
|
<img src="@/assets/images/generateLoading.png" alt="">
|
||||||
|
<div class="mask"></div>
|
||||||
|
</HighlightAdmin>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="more" v-if="item.status == 'RETURNED'" @click="clickimageProcessTaskItem(item.taskId)" @mousedown.stop>
|
||||||
<svg-icon name="more" size="24" size-unit="px" color="#C9C9C9" />
|
<svg-icon name="more" size="24" size-unit="px" color="#C9C9C9" />
|
||||||
</div>
|
</div>
|
||||||
<div class="menu" v-show="showMenu && item.taskId === clickTaskId" @mousedown.stop>
|
<div class="menu" v-show="showMenu && item.taskId === clickTaskId" @mousedown.stop>
|
||||||
@@ -50,9 +59,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import myEvent from '@/utils/myEvent'
|
import myEvent from '@/utils/myEvent'
|
||||||
import { downloadImage } from '../../../tools/tools'
|
import { downloadImage } from '../../../tools/tools'
|
||||||
import { reactive, ref, onBeforeUnmount, useAttrs, inject, watch, } from 'vue'
|
import { reactive, ref, onBeforeUnmount, useAttrs, inject, watch, computed, onMounted } from 'vue'
|
||||||
|
import HighlightAdmin from '@/components/highlightAdmin.vue'
|
||||||
const openImagePreview = inject('openImagePreview') as (url: string) => void
|
const openImagePreview = inject('openImagePreview') as (url: string) => void
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
node: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
config: {
|
config: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
@@ -73,6 +87,7 @@
|
|||||||
const showHeader = ref(!!attrs.node?.data?.isHeader)
|
const showHeader = ref(!!attrs.node?.data?.isHeader)
|
||||||
const showMenu = ref(false)
|
const showMenu = ref(false)
|
||||||
const clickTaskId = ref('')
|
const clickTaskId = ref('')
|
||||||
|
const generateManager = inject('generateManager') as any
|
||||||
const clickimageProcessTaskItem = (taskId: string) => {
|
const clickimageProcessTaskItem = (taskId: string) => {
|
||||||
if(clickTaskId.value == taskId){
|
if(clickTaskId.value == taskId){
|
||||||
showMenu.value = !showMenu.value
|
showMenu.value = !showMenu.value
|
||||||
@@ -81,17 +96,21 @@
|
|||||||
}
|
}
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
selectTaskId: props.data?.selectTaskId || '',
|
selectTaskId: props.data?.selectTaskId || '',
|
||||||
imageProcessTasks: props.data?.imageProcessTasks || [],
|
imageProcessTasks: props.data?.imageProcessTasks,
|
||||||
|
isActive: props.data?.isActive || false,
|
||||||
})
|
})
|
||||||
const setSelectTaskId = (taskId: string) => {
|
const setSelectTaskId = (taskId: string) => {
|
||||||
data.selectTaskId = taskId
|
data.selectTaskId = taskId
|
||||||
emit('update-data', data)
|
|
||||||
}
|
}
|
||||||
watch(
|
watch(
|
||||||
() => props.data.url,
|
() => props.data.status,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
data.url = newVal
|
let selectNode = data.imageProcessTasks.find((item) => item.taskId === data.selectTaskId)
|
||||||
}
|
if(selectNode.status !== 'RETURNED'){
|
||||||
|
generateManager.addTaskId(selectNode,props.node.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
const menus = ref([
|
const menus = ref([
|
||||||
{ label: 'Copy', tip: 'Ctrl+C', on: () => emit('copy-node', clickTaskId.value) },
|
{ label: 'Copy', tip: 'Ctrl+C', on: () => emit('copy-node', clickTaskId.value) },
|
||||||
@@ -103,10 +122,9 @@
|
|||||||
if(clickTaskId.value){
|
if(clickTaskId.value){
|
||||||
data.imageProcessTasks = data.imageProcessTasks.filter((item) => item.taskId !== clickTaskId.value)
|
data.imageProcessTasks = data.imageProcessTasks.filter((item) => item.taskId !== clickTaskId.value)
|
||||||
clickTaskId.value = ''
|
clickTaskId.value = ''
|
||||||
emit('update-data', data)
|
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
emit('delete-node')
|
emit('delete-node', props.node.id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disabled: !!props.config?.disableDelete
|
disabled: !!props.config?.disableDelete
|
||||||
@@ -137,7 +155,6 @@
|
|||||||
}
|
}
|
||||||
item.scale.x = -item.scale.x
|
item.scale.x = -item.scale.x
|
||||||
})
|
})
|
||||||
emit('update-data', data)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -150,7 +167,6 @@
|
|||||||
}
|
}
|
||||||
item.scale.y = -item.scale.y
|
item.scale.y = -item.scale.y
|
||||||
})
|
})
|
||||||
emit('update-data', data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@@ -176,6 +192,9 @@
|
|||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
document.removeEventListener('mousedown', hideMenu)
|
document.removeEventListener('mousedown', hideMenu)
|
||||||
})
|
})
|
||||||
|
onMounted(()=>{
|
||||||
|
emit('update-data', data)
|
||||||
|
})
|
||||||
defineExpose({ data })
|
defineExpose({ data })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -252,6 +271,32 @@
|
|||||||
> .image {
|
> .image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
> .status{
|
||||||
|
width: 100%;
|
||||||
|
height: 140px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
> .content{
|
||||||
|
width: 86px;
|
||||||
|
height: 60px;
|
||||||
|
position: relative;
|
||||||
|
.mask{
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
img{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@
|
|||||||
const onInput = () => {
|
const onInput = () => {
|
||||||
const text = inputRef.value.innerHTML
|
const text = inputRef.value.innerHTML
|
||||||
data.text = text
|
data.text = text
|
||||||
emit('update-data', data)
|
|
||||||
}
|
}
|
||||||
// watch(
|
// watch(
|
||||||
// () => props.active,
|
// () => props.active,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<VueFlow
|
<VueFlow
|
||||||
ref="vueFlow"
|
ref="vueFlow"
|
||||||
:nodes="nodes"
|
:nodes="nodes"
|
||||||
:edges="edges"
|
:edges="edges_"
|
||||||
:min-zoom="0.1"
|
:min-zoom="0.1"
|
||||||
:max-zoom="10"
|
:max-zoom="10"
|
||||||
:nodes-draggable="nodesDraggable"
|
:nodes-draggable="nodesDraggable"
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
@node-drag-stop="(e) => eventManager.handleNodeDragStop(e)"
|
@node-drag-stop="(e) => eventManager.handleNodeDragStop(e)"
|
||||||
@viewport-change="(e) => eventManager.handleViewportChange(e)"
|
@viewport-change="(e) => eventManager.handleViewportChange(e)"
|
||||||
@pane-click="(e) => eventManager.handleClick(e)"
|
@pane-click="(e) => eventManager.handleClick(e)"
|
||||||
|
@node-click="(e) => clickNode(e)"
|
||||||
:class="{ 'custom-cursor': !!stateManager.cursor.value }"
|
:class="{ 'custom-cursor': !!stateManager.cursor.value }"
|
||||||
:style="{ '--custom-cursor': stateManager.cursor.value }"
|
:style="{ '--custom-cursor': stateManager.cursor.value }"
|
||||||
>
|
>
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
import { computed, ref, markRaw, onMounted, nextTick, provide, onBeforeMount } from 'vue'
|
import { computed, ref, watch, onMounted, nextTick, provide, onBeforeMount } from 'vue'
|
||||||
import { useLayout } from '@/utils/treeDiagram'
|
import { useLayout } from '@/utils/treeDiagram'
|
||||||
import { NODE_TYPE, NODE_COMPONENT } from './tools/index.d'
|
import { NODE_TYPE, NODE_COMPONENT } from './tools/index.d'
|
||||||
// 组件
|
// 组件
|
||||||
@@ -91,6 +92,7 @@
|
|||||||
import { FlowManager } from './manager/FlowManager'
|
import { FlowManager } from './manager/FlowManager'
|
||||||
import { NodeManager } from './manager/NodeManager'
|
import { NodeManager } from './manager/NodeManager'
|
||||||
import { ToolManager, TOOLS } from './manager/ToolManager'
|
import { ToolManager, TOOLS } from './manager/ToolManager'
|
||||||
|
import { GenerateManager } from './manager/GenerateManager'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
config: {
|
config: {
|
||||||
@@ -126,8 +128,17 @@
|
|||||||
stateManager.setManager({ toolManager })
|
stateManager.setManager({ toolManager })
|
||||||
provide('toolManager', toolManager)
|
provide('toolManager', toolManager)
|
||||||
|
|
||||||
|
// 生成管理器
|
||||||
|
const generateManager = new GenerateManager({ stateManager })
|
||||||
|
stateManager.setManager({ generateManager })
|
||||||
|
provide('generateManager', generateManager)
|
||||||
|
|
||||||
const nodes = computed(() => stateManager.nodes_.value)
|
const nodes = computed(() => stateManager.nodes_.value)
|
||||||
const edges = computed(() => stateManager.edges.value)
|
const edges = computed(() => stateManager.edges.value)
|
||||||
|
const edges_ = computed(() => {
|
||||||
|
console.log(123)
|
||||||
|
return edges.value.filter((v) => v.visible)
|
||||||
|
})
|
||||||
const nodesDraggable = computed(() => stateManager.nodesDraggable.value)
|
const nodesDraggable = computed(() => stateManager.nodesDraggable.value)
|
||||||
const panOnDrag = computed(() => stateManager.panOnDrag.value)
|
const panOnDrag = computed(() => stateManager.panOnDrag.value)
|
||||||
|
|
||||||
@@ -153,6 +164,11 @@
|
|||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 点击节点 */
|
||||||
|
const clickNode = (event) => {
|
||||||
|
let node = event.node
|
||||||
|
stateManager.showNodeConnections(node.id)
|
||||||
|
}
|
||||||
/** 删除节点 */
|
/** 删除节点 */
|
||||||
const deleteNode = (id) => {
|
const deleteNode = (id) => {
|
||||||
nodeManager.deleteNode(id)
|
nodeManager.deleteNode(id)
|
||||||
@@ -233,11 +249,12 @@
|
|||||||
disableDelete: true,
|
disableDelete: true,
|
||||||
isHeader: false,
|
isHeader: false,
|
||||||
data: {
|
data: {
|
||||||
|
selectable: false,
|
||||||
imageProcessTasks:[
|
imageProcessTasks:[
|
||||||
{
|
{
|
||||||
id: props.config.imgId,
|
id: props.config.imgId,
|
||||||
url: props.config.url,
|
url: props.config.url,
|
||||||
state:'success',
|
status:'RETURNED',
|
||||||
taskId: timestamp + '',
|
taskId: timestamp + '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class FlowManager {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
getSubordNodeByID(id: string) {
|
getSubordNodeById(id: string) {
|
||||||
return this.vueFlow.value.getNodes?.find((v) => v.data.superiorID === id)
|
return this.vueFlow.value.getNodes?.find((v) => v.data.superiorID === id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import { getTaskidResult } from '@/api/flow-canvas'
|
import { getTaskidResult } from '@/api/flow-canvas'
|
||||||
interface NodeOptions {
|
import { ElMessage } from 'element-plus'
|
||||||
id?: string
|
|
||||||
position?: { x: number, y: number }
|
|
||||||
positionX?: number
|
|
||||||
positionY?: number
|
|
||||||
component?: any
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// interface NodeOptions {
|
||||||
|
// }
|
||||||
export class GenerateManager {
|
export class GenerateManager {
|
||||||
stateManager: any
|
stateManager: any
|
||||||
taskIds: string[] = []
|
taskIds: string[] = []
|
||||||
|
getTaskIdsImgTime: any
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.stateManager = options.stateManager;
|
this.stateManager = options.stateManager;
|
||||||
}
|
}
|
||||||
@@ -19,19 +16,63 @@ export class GenerateManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
/** 添加taskId */
|
/** 添加taskId */
|
||||||
addTaskId(TaskId: any) {
|
addTaskId(generateData: any,nodeId: string) {
|
||||||
this.stateManager.addTaskId(TaskId)
|
this.taskIds.push({
|
||||||
|
...generateData,
|
||||||
|
nodeId,
|
||||||
|
})
|
||||||
|
this.getTasksIdImg()
|
||||||
}
|
}
|
||||||
/** 添加taskId */
|
/** 添加taskId */
|
||||||
async getTasksIdImg(list) {
|
async getTasksIdImg() {
|
||||||
let taskIds = list.map((item)=>item.taskId)
|
clearInterval(this.getTaskIdsImgTime)
|
||||||
getTaskidResult({taskIds}).then((rv)=>{
|
this.getTaskIdsImgTime = setInterval(()=>{
|
||||||
console.log(rv)
|
let taskIds = this.taskIds.map((item)=>item.taskId)
|
||||||
})
|
getTaskidResult({taskIds}).then((rv:any)=>{
|
||||||
|
//找出成功和失败的任务
|
||||||
|
let returnedTasks = rv.filter((item)=>item.status == 'RETURNED' || item.status == 'FAILED')
|
||||||
|
if(returnedTasks.length == 0)return
|
||||||
|
//剔除调成功的
|
||||||
|
let taskIds_ = JSON.parse(JSON.stringify(this.taskIds))
|
||||||
|
this.taskIds = taskIds_.filter(itemA =>
|
||||||
|
!returnedTasks.some(itemB => itemB.taskId === itemA.taskId)
|
||||||
|
)
|
||||||
|
//把成功的提取出来并且更新到node中
|
||||||
|
const result = returnedTasks.filter(itemA =>
|
||||||
|
taskIds_.some(itemB => {
|
||||||
|
itemA.nodeId = itemB.nodeId
|
||||||
|
return itemB.taskId === itemA.taskId
|
||||||
|
})
|
||||||
|
)
|
||||||
|
result.forEach(item => {
|
||||||
|
this.stateManager.getNodeById(item.nodeId).data.data.imageProcessTasks.forEach((nodeDataItem:any)=>{
|
||||||
|
if(item.taskId == nodeDataItem.taskId){
|
||||||
|
if(item.status == 'FAILED'){
|
||||||
|
this.stateManager.deleteNode(item.nodeId)
|
||||||
|
ElMessage.error(item.errorMessage)
|
||||||
|
}else if(item.status == 'RETURNED'){
|
||||||
|
nodeDataItem.url = item.url
|
||||||
|
nodeDataItem.createTime = item.createTime
|
||||||
|
nodeDataItem.status = item.status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if(this.taskIds.length == 0)clearInterval(this.getTaskIdsImgTime)
|
||||||
|
}).catch(()=>{
|
||||||
|
clearInterval(this.getTaskIdsImgTime)
|
||||||
|
console.warn('获取taskId图片失败')
|
||||||
|
})
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
/** 更新taskId */
|
||||||
|
updateTaskId(taskId: string) {
|
||||||
|
this.stateManager.getNodeById()
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.taskIds = []
|
this.taskIds = []
|
||||||
|
clearInterval(this.getTaskIdsImgTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ interface NodeData {
|
|||||||
disableDelete?: boolean// 是否禁用删除
|
disableDelete?: boolean// 是否禁用删除
|
||||||
disableCopy?: boolean// 是否禁用复制
|
disableCopy?: boolean// 是否禁用复制
|
||||||
originalImage?: string// 要进行生成的图片
|
originalImage?: string// 要进行生成的图片
|
||||||
|
createIndexPosition?: number// 创建索引位置
|
||||||
|
isActive?: boolean// 是否激活
|
||||||
}
|
}
|
||||||
interface NodeOptions {
|
interface NodeOptions {
|
||||||
id?: string
|
id?: string
|
||||||
@@ -68,9 +70,11 @@ export class NodeManager {
|
|||||||
const options_ = {
|
const options_ = {
|
||||||
...(options ? options : {}),
|
...(options ? options : {}),
|
||||||
component: NODE_COMPONENT.RESULT_IMAGE,
|
component: NODE_COMPONENT.RESULT_IMAGE,
|
||||||
|
positionY: options?.positionY || 0,
|
||||||
data: {
|
data: {
|
||||||
tier: NODE_DATATIER.RESULT_IMAGE,
|
tier: NODE_DATATIER.RESULT_IMAGE,
|
||||||
type: NODE_DATATYPE.RESULT_IMAGE,
|
type: NODE_DATATYPE.RESULT_IMAGE,
|
||||||
|
createIndexPosition: options?.data?.createIndexPosition || 1,
|
||||||
isHeader: true,
|
isHeader: true,
|
||||||
...(options?.data || {}),
|
...(options?.data || {}),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { NODE_TYPE } from '../tools/index.d'
|
import { NODE_TYPE, NODE_DATATYPE } from '../tools/index.d'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
import i18n from '@/lang'
|
import i18n from '@/lang'
|
||||||
const t = i18n.global.t
|
const t = i18n.global.t
|
||||||
@@ -35,12 +35,14 @@ export class StateManager {
|
|||||||
flowManager: any
|
flowManager: any
|
||||||
nodeManager: any
|
nodeManager: any
|
||||||
toolManager: any
|
toolManager: any
|
||||||
|
generateManager: any
|
||||||
// 设置管理器
|
// 设置管理器
|
||||||
setManager(options) {
|
setManager(options) {
|
||||||
options.eventManager && (this.eventManager = options.eventManager)
|
options.eventManager && (this.eventManager = options.eventManager)
|
||||||
options.flowManager && (this.flowManager = options.flowManager)
|
options.flowManager && (this.flowManager = options.flowManager)
|
||||||
options.nodeManager && (this.nodeManager = options.nodeManager)
|
options.nodeManager && (this.nodeManager = options.nodeManager)
|
||||||
options.toolManager && (this.toolManager = options.toolManager)
|
options.toolManager && (this.toolManager = options.toolManager)
|
||||||
|
options.generateManager && (this.generateManager = options.generateManager)
|
||||||
}
|
}
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.vueFlow = options.vueFlow
|
this.vueFlow = options.vueFlow
|
||||||
@@ -87,12 +89,14 @@ export class StateManager {
|
|||||||
source: source,
|
source: source,
|
||||||
target: target,
|
target: target,
|
||||||
selectable: false,
|
selectable: false,
|
||||||
|
visible: (node.data.type !== NODE_DATATYPE.RESULT_IMAGE || node.data.isActive),
|
||||||
type: 'default'
|
type: 'default'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return arr
|
return arr
|
||||||
})
|
})
|
||||||
|
window.nodes = this.nodes
|
||||||
|
|
||||||
}
|
}
|
||||||
/** 设置激活节点 */
|
/** 设置激活节点 */
|
||||||
@@ -131,8 +135,8 @@ export class StateManager {
|
|||||||
/** 获取节点 */
|
/** 获取节点 */
|
||||||
getNodeById(id: string) { return this.nodes.value.find((node: NodesItem) => node.id === id) }
|
getNodeById(id: string) { return this.nodes.value.find((node: NodesItem) => node.id === id) }
|
||||||
/** 获取下级节点 */
|
/** 获取下级节点 */
|
||||||
getSubordNodeByID(id: string) { return this.nodes.value.find((node: NodesItem) => node.data.superiorID === id) }
|
getSubordNodeById(id: string) { return this.nodes.value.find((node: NodesItem) => node.data.superiorID === id) }
|
||||||
getLastNode() { return this.nodes.value[this.nodes.value.length - 1] }
|
getLastNode() { console.log(this.nodes.value); return this.nodes.value[this.nodes.value.length - 1] }
|
||||||
/** 设置工具 */
|
/** 设置工具 */
|
||||||
setTool(tool: string) { this.tool.value = tool }
|
setTool(tool: string) { this.tool.value = tool }
|
||||||
/** 设置光标 */
|
/** 设置光标 */
|
||||||
@@ -183,6 +187,20 @@ export class StateManager {
|
|||||||
this.nodes.value = JSON.parse(state.nodes)
|
this.nodes.value = JSON.parse(state.nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 显示指定子节点和父节点连接线,隐藏父节点和其他子节点链接线, */
|
||||||
|
showNodeConnections(id: string) {
|
||||||
|
const node = this.getNodeById(id)
|
||||||
|
if(node.data.component != NODE_DATATYPE.RESULT_IMAGE && node.data.superiorID) return
|
||||||
|
let edges_ = JSON.parse(JSON.stringify(this.edges.value))
|
||||||
|
this.nodes.value.forEach((nodeItem) => {
|
||||||
|
if(node.data.superiorID === nodeItem.data.superiorID && nodeItem.id == id) {
|
||||||
|
nodeItem.data.isActive = true
|
||||||
|
}else if(node.data.superiorID == nodeItem.data.superiorID){
|
||||||
|
nodeItem.data.isActive = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.historyList.value = []
|
this.historyList.value = []
|
||||||
this.historyIndex.value = 0
|
this.historyIndex.value = 0
|
||||||
|
|||||||
61
src/components/highlightAdmin.vue
Normal file
61
src/components/highlightAdmin.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div class="loading" ref="textBox">
|
||||||
|
<slot></slot>
|
||||||
|
<div class="loading-dot" ref="dotBox"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, nextTick, ref } from "vue";
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
|
||||||
|
const dotBox = ref(null)
|
||||||
|
let tl1 = null;
|
||||||
|
const setTl1 = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
let el = dotBox.value
|
||||||
|
let width = el.offsetWidth + el.parentElement.offsetWidth
|
||||||
|
let time = el.parentElement.offsetWidth / el.offsetWidth
|
||||||
|
tl1 = gsap.timeline();
|
||||||
|
tl1.to(el, time,
|
||||||
|
{
|
||||||
|
ease: "power1.in",
|
||||||
|
left: width * 1.5,
|
||||||
|
onComplete: () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
tl1.restart()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
tl1.progress(0);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTl1()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.loading {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.loading-dot {
|
||||||
|
height: 200%;
|
||||||
|
aspect-ratio: .2/1;
|
||||||
|
background: radial-gradient(ellipse 150% 150% at center, #ffffff, rgba(255, 255, 255, .2), transparent);
|
||||||
|
box-shadow: 0 0 20px rgba(255, 255, 255, 1);
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
transform: translate(-175%, -50%) rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user