Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front
This commit is contained in:
BIN
src/assets/images/three/sample.glb
Normal file
BIN
src/assets/images/three/sample.glb
Normal file
Binary file not shown.
@@ -106,6 +106,7 @@
|
|||||||
isReady.value = true // 准备就绪
|
isReady.value = true // 准备就绪
|
||||||
})
|
})
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
observer.value.disconnect()
|
||||||
canvasManager.dispose()
|
canvasManager.dispose()
|
||||||
stateManager.dispose()
|
stateManager.dispose()
|
||||||
layerManager.dispose()
|
layerManager.dispose()
|
||||||
|
|||||||
@@ -153,6 +153,9 @@ export class CanvasManager {
|
|||||||
getObjectById(id: string) {
|
getObjectById(id: string) {
|
||||||
return this.getObjects().find((item: any) => item?.info?.id === id)
|
return this.getObjects().find((item: any) => item?.info?.id === id)
|
||||||
}
|
}
|
||||||
|
getActiveObject() {
|
||||||
|
return this.getObjectById(this.layerManager.activeID.value)
|
||||||
|
}
|
||||||
renderAll() {
|
renderAll() {
|
||||||
this.canvas.renderAll()
|
this.canvas.renderAll()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ export class LayerManager {
|
|||||||
this.stateManager.toolManager.setTool(OperationType.SELECT)
|
this.stateManager.toolManager.setTool(OperationType.SELECT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
getActiveLayer() {
|
||||||
|
return this.getLayerById(this.activeID.value)
|
||||||
|
}
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="node" :class="{ center: posCenter, mask: mask }">
|
<div class="node-el" :class="{ center: posCenter, mask: mask }">
|
||||||
<Handle
|
<Handle
|
||||||
v-for="handle in handles[type] || []"
|
v-for="handle in handles[type] || []"
|
||||||
:key="handle.id"
|
:key="handle.id"
|
||||||
@@ -19,8 +19,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Handle, Position } from '@vue-flow/core'
|
import { Handle, Position } from '@vue-flow/core'
|
||||||
import { NODE_TYPE } from '../tools/index.d'
|
import { NODE_DATATYPE, NODE_DATATIER, NODE_TYPE } from '../tools/index.d'
|
||||||
import { NODE_DATATYPE, NODE_DATATIER } from '../tools/index.d'
|
|
||||||
import { computed, ref, inject } 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 }],
|
||||||
@@ -54,7 +53,10 @@
|
|||||||
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(() => {
|
const isReturned = computed(() => {
|
||||||
return props.node.data.type == 'result-image' && props.node.data.data.imageProcessTasks[0].status == 'RETURNED'
|
return (
|
||||||
|
props.node.data.type == NODE_DATATYPE.RESULT_IMAGE &&
|
||||||
|
props.node.data.data.imageProcessTasks[0].status == 'RETURNED'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
const isAdd = computed(
|
const isAdd = computed(
|
||||||
() =>
|
() =>
|
||||||
@@ -66,9 +68,11 @@
|
|||||||
const onAdd = () => {
|
const onAdd = () => {
|
||||||
const tier_ = tier.value + 1
|
const tier_ = tier.value + 1
|
||||||
// 从data中获取originalImage
|
// 从data中获取originalImage
|
||||||
let nodeData = props.node?.data?.data.imageProcessTasks.filter((v) => v.taskId === props.node?.data?.data.selectTaskId)
|
let nodeData = props.node?.data?.data.imageProcessTasks.filter(
|
||||||
|
(v) => v.taskId === props.node?.data?.data.selectTaskId
|
||||||
|
)
|
||||||
const originalImage = nodeData[0]?.url
|
const originalImage = nodeData[0]?.url
|
||||||
if(!originalImage)console.log('originalImage 找不到原始图片')
|
if (!originalImage) console.log('originalImage 找不到原始图片')
|
||||||
props.stateManager.nodeManager.createCardsSelect({
|
props.stateManager.nodeManager.createCardsSelect({
|
||||||
data: { tier: tier_, superiorID: props.node.id, originalImage }
|
data: { tier: tier_, superiorID: props.node.id, originalImage }
|
||||||
})
|
})
|
||||||
@@ -80,7 +84,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.node {
|
.node-el {
|
||||||
position: relative;
|
position: relative;
|
||||||
--top: 50px;
|
--top: 50px;
|
||||||
&.mask *,
|
&.mask *,
|
||||||
@@ -4,7 +4,8 @@ import { useI18n } from 'vue-i18n'
|
|||||||
|
|
||||||
import gsap from 'gsap';
|
import gsap from 'gsap';
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { initThree,clearModel,addModel } from './threeTool'
|
import { initThree,clearModel,addModel,getModelInfo,calculateCameraPosition } from './threeTool'
|
||||||
|
import threeGlb from '@/assets/images/three/sample.glb'
|
||||||
|
|
||||||
//const props = defineProps({
|
//const props = defineProps({
|
||||||
//})
|
//})
|
||||||
@@ -28,27 +29,56 @@ const load = ref({
|
|||||||
progress:0
|
progress:0
|
||||||
})
|
})
|
||||||
// const textureLoader = ref(new THREE.TextureLoader())//材质
|
// const textureLoader = ref(new THREE.TextureLoader())//材质
|
||||||
//初始化
|
const init = () => {
|
||||||
const init = ()=>{
|
//初始化threejs
|
||||||
//初始化threejs
|
if (scene.value) return
|
||||||
if(scene.value)return
|
const initResult = initThree(threeDom.value)
|
||||||
const initResult = initThree(threeDom.value)
|
scene.value = initResult.scene
|
||||||
scene.value = initResult.scene
|
group.value = initResult.group
|
||||||
group.value = initResult.group
|
camera.value = initResult.camera
|
||||||
camera.value = initResult.camera
|
renderer.value = initResult.renderer
|
||||||
renderer.value = initResult.renderer
|
ambient.value = initResult.ambient
|
||||||
ambient.value = initResult.ambient
|
controls.value = initResult.controls
|
||||||
controls.value = initResult.controls
|
pointLight.value = initResult.pointLight
|
||||||
pointLight.value = initResult.pointLight
|
|
||||||
|
|
||||||
threeDom.value.ondblclick = (event:any)=>{
|
threeDom.value.ondblclick = (event:any)=>{
|
||||||
let intersects = openModel(event);
|
let intersects = openModel(event);
|
||||||
if(!intersects || intersects.length<=0) return
|
if(!intersects || intersects.length<=0) return
|
||||||
const bbox = new THREE.Box3().setFromObject(intersects[0].object);
|
|
||||||
const size = new THREE.Vector3();
|
const clickedObject = intersects[0].object;
|
||||||
let target2 = bbox.getCenter(size);//获取选中包围起来后的中心坐标
|
const modelInfo = getModelInfo(clickedObject);
|
||||||
animateCamera(camera.value.position,intersects[0].point,controls.value.target,target2)
|
const { size } = modelInfo;
|
||||||
}
|
const maxSize = Math.max(size.x, size.y, size.z);
|
||||||
|
let distanceFactor = 1.2;
|
||||||
|
let heightFactor = 0.3;
|
||||||
|
let angle = 0;
|
||||||
|
if (size.y > size.x * 2) {
|
||||||
|
// 高瘦物体,拉远一点,稍微抬高视角
|
||||||
|
distanceFactor = 1.5;
|
||||||
|
heightFactor = 0.4;
|
||||||
|
angle = Math.PI / 6; // 30度
|
||||||
|
} else if (size.x > size.y * 2) {
|
||||||
|
// 扁平物体,降低视角
|
||||||
|
heightFactor = 0.2;
|
||||||
|
angle = Math.PI / 8; // 22.5度
|
||||||
|
}
|
||||||
|
const { position: cameraPos, target } = calculateCameraPosition(
|
||||||
|
modelInfo,
|
||||||
|
camera.value,
|
||||||
|
{
|
||||||
|
distanceFactor,
|
||||||
|
heightFactor,
|
||||||
|
angle
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// 执行相机动画
|
||||||
|
animateCamera(
|
||||||
|
camera.value.position,
|
||||||
|
cameraPos,
|
||||||
|
controls.value.target,
|
||||||
|
target
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let openModel = (event:any)=>{
|
let openModel = (event:any)=>{
|
||||||
@@ -61,39 +91,43 @@ let openModel = (event:any)=>{
|
|||||||
return intersects
|
return intersects
|
||||||
}
|
}
|
||||||
let isTweening = false;
|
let isTweening = false;
|
||||||
function animateCamera(current1:any, target1:any, current2:any, target2:any){
|
|
||||||
if (isTweening) return
|
function animateCamera(
|
||||||
isTweening = true
|
startCameraPos: THREE.Vector3,
|
||||||
let options = {
|
endCameraPos: THREE.Vector3,
|
||||||
x1: current1.x, // 相机当前位置x
|
startTarget: THREE.Vector3,
|
||||||
y1: current1.y, // 相机当前位置y
|
endTarget: THREE.Vector3
|
||||||
z1: current1.z, // 相机当前位置z
|
) {
|
||||||
x2: current2.x, // 控制当前的中心点x
|
if (isTweening) return;
|
||||||
y2: current2.y, // 控制当前的中心点y
|
isTweening = true;
|
||||||
// z2: current2.z // 控制当前的中心点z
|
|
||||||
}
|
let options = {
|
||||||
gsap.to(options,{
|
cx: startCameraPos.x,
|
||||||
x1: 0, // 新的相机位置x
|
cy: startCameraPos.y,
|
||||||
y1: target2.y, // 新的相机位置y
|
cz: startCameraPos.z,
|
||||||
z1: 1000, // 新的相机位置z
|
tx: startTarget.x,
|
||||||
x2: 0, // 新的控制中心点位置x
|
ty: startTarget.y,
|
||||||
y2: target2.y, // 新的控制中心点位置x
|
tz: startTarget.z
|
||||||
duration:1,
|
};
|
||||||
ease:'linear',
|
|
||||||
onUpdate:()=>{
|
gsap.to(options, {
|
||||||
camera.value.position.x = options.x1;
|
cx: endCameraPos.x,
|
||||||
camera.value.position.y = options.y1;
|
cy: endCameraPos.y,
|
||||||
camera.value.position.z = options.z1;
|
cz: endCameraPos.z,
|
||||||
controls.value.target.x = options.x2;
|
tx: endTarget.x,
|
||||||
controls.value.target.y = options.y2;
|
ty: endTarget.y,
|
||||||
// controls.value.target.z = object.z2;
|
tz: endTarget.z,
|
||||||
controls.value.update();
|
duration: 1,
|
||||||
},
|
ease: 'power2.inOut', // 使用更自然的缓动
|
||||||
onComplete:()=>{
|
onUpdate: () => {
|
||||||
isTweening = false
|
camera.value.position.set(options.cx, options.cy, options.cz);
|
||||||
}
|
controls.value.target.set(options.tx, options.ty, options.tz);
|
||||||
// z2: target2.z // 新的控制中心点位置x
|
controls.value.update();
|
||||||
})
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
isTweening = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const setModel = async (url:any)=>{
|
const setModel = async (url:any)=>{
|
||||||
clearModel(group,scene)
|
clearModel(group,scene)
|
||||||
@@ -118,7 +152,7 @@ const open = async ()=>{
|
|||||||
// composer.render();
|
// composer.render();
|
||||||
};
|
};
|
||||||
animate();
|
animate();
|
||||||
await setModel("https://www.minio-api.aida.com.hk/aida-threed/female/glb/1%E7%9F%AD%E8%A2%96T%E6%81%A4.glb?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260310%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260310T032933Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=184ec7f9ff3076dde5aca66e2d2e27f8d180add698a5b1040fe903a55cb2f85e")
|
await setModel(threeGlb)
|
||||||
load.value.state = false
|
load.value.state = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,21 +57,25 @@ export const initThree = (threeDom)=>{
|
|||||||
DirectionalLight 平行光,比如太阳光
|
DirectionalLight 平行光,比如太阳光
|
||||||
SpotLight 聚光源
|
SpotLight 聚光源
|
||||||
*/
|
*/
|
||||||
const pointLight = new THREE.DirectionalLight(0xffffff,.5);
|
//设置环境光全亮
|
||||||
pointLight.intensity = 1.2
|
const pointLight = new THREE.AmbientLight(0xffffff,.2);
|
||||||
pointLight.castShadow = true//开启阴影
|
scene.add(pointLight);
|
||||||
pointLight.shadow.mapSize = new THREE.Vector2(width, height)
|
// const pointLight = new THREE.AmbientLight(0xffffff,1.0);
|
||||||
scene.add(pointLight); //点光源添加到场景中
|
// pointLight.intensity = 1.2//光源强度
|
||||||
|
// pointLight.castShadow = true//开启阴影
|
||||||
|
// pointLight.shadow.mapSize = new THREE.Vector2(width, height)
|
||||||
|
// scene.add(pointLight); //点光源添加到场景中
|
||||||
// pointLight.position.set(400, 200, 300); //点光源位置
|
// pointLight.position.set(400, 200, 300); //点光源位置
|
||||||
pointLight.position.y = 400;
|
// pointLight.position.y = 100;
|
||||||
pointLight.position.z = 200;
|
// pointLight.position.z = 50;
|
||||||
pointLight.position.x = 200;
|
// pointLight.position.x = 100;
|
||||||
let floorGeometry = new THREE.PlaneGeometry(5000, 3000)//地板大小
|
|
||||||
let floorMaterial = new THREE.MeshPhongMaterial({ color: "#7e7ab0", })
|
// let floorGeometry = new THREE.PlaneGeometry(5000, 3000)//地板大小
|
||||||
let floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
|
// let floorMaterial = new THREE.MeshPhongMaterial({ color: "#7e7ab0", })
|
||||||
floorMesh.rotation.x = -0.5 * Math.PI;
|
// let floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
|
||||||
floorMesh.receiveShadow = true;
|
// floorMesh.rotation.x = -0.5 * Math.PI;
|
||||||
floorMesh.position.y = -0.001;
|
// floorMesh.receiveShadow = true;
|
||||||
|
// floorMesh.position.y = -0.001;
|
||||||
// scene.add(floorMesh);
|
// scene.add(floorMesh);
|
||||||
const textureLoader = new THREE.TextureLoader();
|
const textureLoader = new THREE.TextureLoader();
|
||||||
// const texture = textureLoader.load('/3dModel/sketch-thick.jpg');
|
// const texture = textureLoader.load('/3dModel/sketch-thick.jpg');
|
||||||
@@ -85,41 +89,144 @@ export const clearModel = (group,scene)=>{
|
|||||||
scene.value.remove(oldGroup);
|
scene.value.remove(oldGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算模型包围盒
|
||||||
|
export const getModelInfo = (model: THREE.Object3D) => {
|
||||||
|
const box = new THREE.Box3().setFromObject(model);
|
||||||
|
const center = box.getCenter(new THREE.Vector3());
|
||||||
|
const size = box.getSize(new THREE.Vector3());
|
||||||
|
const maxSize = Math.max(size.x, size.y, size.z);
|
||||||
|
return {
|
||||||
|
box,
|
||||||
|
center,
|
||||||
|
size,
|
||||||
|
maxSize
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// 根据模型信息计算相机位置
|
||||||
|
export const calculateCameraPosition = (
|
||||||
|
modelInfo: ReturnType<typeof getModelInfo>,
|
||||||
|
camera: THREE.PerspectiveCamera,
|
||||||
|
options?: {
|
||||||
|
distanceFactor?: number; // 距离系数,默认1.5
|
||||||
|
heightFactor?: number; // 高度偏移系数,默认0.3
|
||||||
|
angle?: number; // 观察角度,默认正前方
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const { center, size, maxSize } = modelInfo;
|
||||||
|
const fov = camera.fov * (Math.PI / 180);
|
||||||
|
|
||||||
|
const distanceFactor = options?.distanceFactor ?? 1.5;
|
||||||
|
const heightFactor = options?.heightFactor ?? 0.3;
|
||||||
|
|
||||||
|
// 计算合适的相机距离
|
||||||
|
const distance = (maxSize / 2) / Math.tan(fov / 2) * distanceFactor;
|
||||||
|
|
||||||
|
// 根据角度计算相机位置
|
||||||
|
const angle = options?.angle ?? 0; // 0表示正前方
|
||||||
|
|
||||||
|
return {
|
||||||
|
position: new THREE.Vector3(
|
||||||
|
center.x + distance * Math.sin(angle),
|
||||||
|
center.y + size.y * heightFactor,
|
||||||
|
center.z + distance * Math.cos(angle)
|
||||||
|
),
|
||||||
|
target: center.clone(),
|
||||||
|
distance,
|
||||||
|
center,
|
||||||
|
size
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// 根据模型信息计算光源位置
|
||||||
|
export const calculateLightPosition = (
|
||||||
|
modelInfo: ReturnType<typeof getModelInfo>,
|
||||||
|
options?: {
|
||||||
|
xFactor?: number; // X轴偏移系数,默认0.5
|
||||||
|
yFactor?: number; // Y轴偏移系数,默认0.8
|
||||||
|
zFactor?: number; // Z轴偏移系数,默认0.5
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const { center, size } = modelInfo;
|
||||||
|
|
||||||
|
const xFactor = options?.xFactor ?? 0.5;
|
||||||
|
const yFactor = options?.yFactor ?? 0.8;
|
||||||
|
const zFactor = options?.zFactor ?? 0.5;
|
||||||
|
|
||||||
|
return new THREE.Vector3(
|
||||||
|
center.x + size.x * xFactor,
|
||||||
|
center.y + size.y * yFactor,
|
||||||
|
center.z + size.z * zFactor
|
||||||
|
);
|
||||||
|
};
|
||||||
export const addModel = async (
|
export const addModel = async (
|
||||||
url:any,
|
url: any,
|
||||||
controls:OrbitControls,
|
controls: OrbitControls,
|
||||||
camera:THREE.PerspectiveCamera,
|
camera: THREE.PerspectiveCamera,
|
||||||
pointLight:THREE.DirectionalLight,
|
pointLight: THREE.DirectionalLight,
|
||||||
group:THREE.Group,
|
group: THREE.Group,
|
||||||
load:any)=>{
|
load: any
|
||||||
await new Promise((resolve, reject) => {
|
) => {
|
||||||
var fbxLoader = new GLTFLoader();
|
await new Promise((resolve, reject) => {
|
||||||
let drac = new DRACOLoader()
|
var fbxLoader = new GLTFLoader();
|
||||||
drac.setDecoderPath('/draco/')
|
let drac = new DRACOLoader()
|
||||||
fbxLoader.setDRACOLoader(drac)
|
drac.setDecoderPath('/draco/')
|
||||||
// fbxLoader.load('/3dModel/222/1111.glb',
|
fbxLoader.setDRACOLoader(drac)
|
||||||
fbxLoader.load(url,
|
|
||||||
|
|
||||||
(obj:any) => {
|
fbxLoader.load(url,
|
||||||
let scene = obj.scene;
|
(obj: any) => {
|
||||||
var box = new THREE.Box3().setFromObject(scene);
|
let scene = obj.scene;
|
||||||
var center = box.getCenter(new THREE.Vector3());
|
scene.traverse((child: any) => {
|
||||||
controls.value.target.copy(center);
|
if (child.isMesh) {
|
||||||
// controls.autoRotate = true
|
// 如果是基础材质,转换为标准材质
|
||||||
camera.value.position.y = center.y;
|
if (child.material instanceof THREE.MeshBasicMaterial) {
|
||||||
camera.value.position.z = 1000;
|
const oldMat = child.material;
|
||||||
pointLight.value.position.y = 250;
|
child.material = new THREE.MeshStandardMaterial({
|
||||||
pointLight.value.position.z = 1250;
|
map: oldMat.map,
|
||||||
group.value.add(scene);
|
color: oldMat.color,
|
||||||
resolve('')
|
roughness: 0.4,
|
||||||
},(xhr:any) => { // 加载进度回调
|
metalness: 0
|
||||||
const percent = xhr.total == 0?100:(xhr.loaded / xhr.total * 100).toFixed(2);
|
});
|
||||||
load.value.progress = percent
|
}
|
||||||
// updateProgressBar(Number(percent));
|
// 如果是标准材质,调整粗糙度
|
||||||
},(error:any) => { // 加载失败回调
|
else if (child.material instanceof THREE.MeshStandardMaterial) {
|
||||||
console.error('模型加载失败:', error);
|
child.material.roughness = 0.4;
|
||||||
reject('')
|
child.material.metalness = 0;
|
||||||
})
|
}
|
||||||
})
|
}
|
||||||
|
});
|
||||||
|
// 获取模型信息
|
||||||
|
const modelInfo = getModelInfo(scene);
|
||||||
|
const { position: cameraPos, target } = calculateCameraPosition(
|
||||||
|
modelInfo,
|
||||||
|
camera.value,
|
||||||
|
{
|
||||||
|
distanceFactor: 1.5,
|
||||||
|
heightFactor: 0.3,
|
||||||
|
angle: 0 // 正前方
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// 设置相机位置
|
||||||
|
camera.value.position.copy(cameraPos);
|
||||||
|
// 设置控制器目标点
|
||||||
|
controls.value.target.copy(target);
|
||||||
|
// 计算并设置光源位置
|
||||||
|
const lightPos = calculateLightPosition(modelInfo);
|
||||||
|
pointLight.value.position.copy(lightPos);
|
||||||
|
// 将模型添加到场景
|
||||||
|
group.value.add(scene);
|
||||||
|
// 可选:将模型信息存储在模型上,方便后续使用
|
||||||
|
(scene as any).userData.modelInfo = modelInfo;
|
||||||
|
|
||||||
|
resolve('')
|
||||||
|
},
|
||||||
|
(xhr: any) => { // 加载进度回调
|
||||||
|
const percent = xhr.total == 0 ? 100 : (xhr.loaded / xhr.total * 100).toFixed(2);
|
||||||
|
load.value.progress = percent
|
||||||
|
console.log('模型加载进度:', percent);
|
||||||
|
},
|
||||||
|
(error: any) => { // 加载失败回调
|
||||||
|
console.error('模型加载失败:', error);
|
||||||
|
reject('')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
:style="{ '--custom-cursor': stateManager.cursor.value }"
|
:style="{ '--custom-cursor': stateManager.cursor.value }"
|
||||||
>
|
>
|
||||||
<template v-for="v in nodeTypes" :key="v" #[`node-${v}`]="node">
|
<template v-for="v in nodeTypes" :key="v" #[`node-${v}`]="node">
|
||||||
<node
|
<node-el
|
||||||
:type="v"
|
:type="v"
|
||||||
:stateManager="stateManager"
|
:stateManager="stateManager"
|
||||||
:node="node"
|
:node="node"
|
||||||
@@ -36,12 +36,12 @@
|
|||||||
:data="node.data.data"
|
:data="node.data.data"
|
||||||
v-bind="node.data"
|
v-bind="node.data"
|
||||||
@delete-node="deleteNode(node.id)"
|
@delete-node="deleteNode(node.id)"
|
||||||
@copy-node="copyNode($event,node.id)"
|
@copy-node="copyNode($event, node.id)"
|
||||||
@update-data="(v) => (node.data.data = v)"
|
@update-data="(v) => (node.data.data = v)"
|
||||||
@bring-to-font="bringToFont(node.id)"
|
@bring-to-font="bringToFont(node.id)"
|
||||||
@send-to-back="sendToBack(node.id)"
|
@send-to-back="sendToBack(node.id)"
|
||||||
/>
|
/>
|
||||||
</node>
|
</node-el>
|
||||||
</template>
|
</template>
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
@home="() => fitView({ maxZoom: 1 })"
|
@home="() => fitView({ maxZoom: 1 })"
|
||||||
/>
|
/>
|
||||||
<image-preview ref="imagePreviewRef" />
|
<image-preview ref="imagePreviewRef" />
|
||||||
<baseModal ref="threeModelRef" >
|
<baseModal ref="threeModelRef">
|
||||||
<threeModel />
|
<threeModel />
|
||||||
</baseModal>
|
</baseModal>
|
||||||
</template>
|
</template>
|
||||||
@@ -73,13 +73,11 @@
|
|||||||
// 工具
|
// 工具
|
||||||
import threeModel from './components/tools/threeModel/index.vue'
|
import threeModel from './components/tools/threeModel/index.vue'
|
||||||
// 节点
|
// 节点
|
||||||
import node from './components/node.vue'
|
import nodeEl from './components/node-el.vue'
|
||||||
import resultImage from './components/nodes/result-image.vue'
|
import resultImage from './components/nodes/result-image.vue'
|
||||||
import card from './components/nodes/cards/index.vue'
|
import card from './components/nodes/cards/index.vue'
|
||||||
import text from './components/nodes/text.vue'
|
import text from './components/nodes/text.vue'
|
||||||
|
|
||||||
// 接口
|
|
||||||
import { getSketchFlowCanvas, putSketchFlowCanvas } from '@/api/flow-canvas'
|
|
||||||
const components = {
|
const components = {
|
||||||
[NODE_COMPONENT.RESULT_IMAGE]: resultImage,
|
[NODE_COMPONENT.RESULT_IMAGE]: resultImage,
|
||||||
[NODE_COMPONENT.CARD]: card,
|
[NODE_COMPONENT.CARD]: card,
|
||||||
@@ -100,6 +98,7 @@
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const emit = defineEmits(['exportFlow'])
|
||||||
|
|
||||||
const vueFlow = ref<any>()
|
const vueFlow = ref<any>()
|
||||||
const nodeTypes = ref([NODE_TYPE.INPUT, NODE_TYPE.SECONDARY, NODE_TYPE.OUTPUT, NODE_TYPE.ALONE])
|
const nodeTypes = ref([NODE_TYPE.INPUT, NODE_TYPE.SECONDARY, NODE_TYPE.OUTPUT, NODE_TYPE.ALONE])
|
||||||
@@ -136,8 +135,7 @@
|
|||||||
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(() => {
|
const edges_ = computed(() => {
|
||||||
console.log(123)
|
return edges.value.filter((v) => v?.visible)
|
||||||
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)
|
||||||
@@ -146,6 +144,7 @@
|
|||||||
const { layout } = useLayout()
|
const { layout } = useLayout()
|
||||||
const index = ref(0)
|
const index = ref(0)
|
||||||
async function layoutGraph(direction) {
|
async function layoutGraph(direction) {
|
||||||
|
if (props.config.json > 0) return
|
||||||
if (index.value > 0) return
|
if (index.value > 0) return
|
||||||
index.value++
|
index.value++
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -174,8 +173,8 @@
|
|||||||
nodeManager.deleteNode(id)
|
nodeManager.deleteNode(id)
|
||||||
}
|
}
|
||||||
/** 复制节点 */
|
/** 复制节点 */
|
||||||
const copyNode = (clickTaskId,id) => {
|
const copyNode = (clickTaskId, id) => {
|
||||||
nodeManager.copyNodeById(clickTaskId,id)
|
nodeManager.copyNodeById(clickTaskId, id)
|
||||||
}
|
}
|
||||||
/** 节点zIndex设置最大 */
|
/** 节点zIndex设置最大 */
|
||||||
const bringToFont = (id) => {
|
const bringToFont = (id) => {
|
||||||
@@ -190,15 +189,7 @@
|
|||||||
// flowManager.exportFlow()
|
// flowManager.exportFlow()
|
||||||
|
|
||||||
const str = JSON.stringify(stateManager.nodes.value)
|
const str = JSON.stringify(stateManager.nodes.value)
|
||||||
const json = JSON.parse(str)
|
emit('exportFlow', str)
|
||||||
putSketchFlowCanvas({
|
|
||||||
id: props.config.imgId,
|
|
||||||
canvasData: str
|
|
||||||
}).then((res) => {
|
|
||||||
if (res) {
|
|
||||||
console.log(res)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// localStorage.setItem('flow_json', str)
|
// localStorage.setItem('flow_json', str)
|
||||||
}
|
}
|
||||||
// 导入流程
|
// 导入流程
|
||||||
@@ -229,20 +220,10 @@
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// window['vueFlow'] = vueFlow
|
// window['vueFlow'] = vueFlow
|
||||||
// window['nodes'] = nodes
|
// window['nodes'] = nodes
|
||||||
let json = []
|
|
||||||
await new Promise((resolve) => {
|
if (props.config.json.length > 0) {
|
||||||
getSketchFlowCanvas({ id: props.config.imgId }).then((res:any) => {
|
importFlow(props.config.json)
|
||||||
if (res) {
|
} else {
|
||||||
json = JSON.parse(res)
|
|
||||||
}
|
|
||||||
resolve(true)
|
|
||||||
}).catch(() => {
|
|
||||||
resolve(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if(json.length > 0){
|
|
||||||
importFlow(json)
|
|
||||||
}else{
|
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
nodeManager.createResultNode({
|
nodeManager.createResultNode({
|
||||||
data: {
|
data: {
|
||||||
@@ -250,20 +231,19 @@
|
|||||||
isHeader: false,
|
isHeader: false,
|
||||||
data: {
|
data: {
|
||||||
selectable: false,
|
selectable: false,
|
||||||
imageProcessTasks:[
|
imageProcessTasks: [
|
||||||
{
|
{
|
||||||
id: props.config.imgId,
|
id: props.config.imgId,
|
||||||
url: props.config.url,
|
url: props.config.url,
|
||||||
status:'RETURNED',
|
status: 'RETURNED',
|
||||||
taskId: timestamp + '',
|
taskId: timestamp + ''
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
selectTaskId: timestamp + '',
|
selectTaskId: timestamp + ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
stateManager.dispose()
|
stateManager.dispose()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<fullscreen-dialog v-model="dialogVisible" hide-destroy>
|
<fullscreen-dialog v-model="dialogVisible" hide-destroy>
|
||||||
<flow-canvas :config="config" />
|
<flow-canvas :config="config" @exportFlow="exportFlow" />
|
||||||
</fullscreen-dialog>
|
</fullscreen-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -8,18 +8,46 @@
|
|||||||
import FullscreenDialog from '../components/fullscreen-dialog.vue'
|
import FullscreenDialog from '../components/fullscreen-dialog.vue'
|
||||||
import flowCanvas from './flow-canvas.vue'
|
import flowCanvas from './flow-canvas.vue'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { getSketchFlowCanvas, putSketchFlowCanvas } from '@/api/flow-canvas'
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const config = ref({})
|
const config = ref({}) as any
|
||||||
const open = (options) => {
|
const open = async (options) => {
|
||||||
dialogVisible.value = true
|
let json = []
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
getSketchFlowCanvas({ id: options.imgId }).then((res:any) => {
|
||||||
|
if (res) {
|
||||||
|
json = JSON.parse(res)
|
||||||
|
}
|
||||||
|
resolve(true)
|
||||||
|
}).catch(() => {
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
config.value = options || {}
|
config.value = options || {}
|
||||||
|
config.value.json = json
|
||||||
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
const exportFlow = async (str) => {
|
||||||
|
if(!config.value.imgId)return
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
putSketchFlowCanvas({
|
||||||
|
id: config.value.imgId,
|
||||||
|
canvasData: str }).then(() => {
|
||||||
|
resolve(true)
|
||||||
|
}).catch(() => {
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
}
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
open,
|
open,
|
||||||
close
|
close,
|
||||||
|
exportFlow
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ export class StateManager {
|
|||||||
return arr
|
return arr
|
||||||
})
|
})
|
||||||
window.nodes = this.nodes
|
window.nodes = this.nodes
|
||||||
|
window.aaa = this
|
||||||
|
|
||||||
}
|
}
|
||||||
/** 设置激活节点 */
|
/** 设置激活节点 */
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
inject: 'body-last' // 注入位置优化
|
inject: 'body-last' // 注入位置优化
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
assetsInclude: ['**/*.glb'],
|
||||||
define: {
|
define: {
|
||||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user