支持组件缩放

This commit is contained in:
2025-05-27 18:22:32 +08:00
parent a465450bf1
commit 45565de5ef
3 changed files with 72 additions and 33 deletions

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue'; import {ref, watch} from 'vue';
import { Handle, useVueFlow } from '@vue-flow/core'; import {Handle, useVueFlow} from '@vue-flow/core';
import { NodeResizer } from '@vue-flow/node-resizer'; import {NodeResizer} from '@vue-flow/node-resizer';
import '@vue-flow/node-resizer/dist/style.css'; import '@vue-flow/node-resizer/dist/style.css';
const props = defineProps({ const props = defineProps({
@ -17,18 +17,19 @@ const nodeHeight = ref(120);
watch(() => props.data, (newData) => { watch(() => props.data, (newData) => {
if (newData && newData.width) nodeWidth.value = newData.width; if (newData && newData.width) nodeWidth.value = newData.width;
if (newData && newData.height) nodeHeight.value = newData.height; if (newData && newData.height) nodeHeight.value = newData.height;
}, { immediate: true }); }, {immediate: true});
</script> </script>
<template> <template>
<div class="image-node" :style="{ width: `${nodeWidth}px`, height: `${nodeHeight}px` }"> <NodeResizer v-if="selected" :min-width="60" :min-height="60" :max-width="400" :max-height="400"/>
<NodeResizer v-if="selected" :min-width="60" :min-height="60" :max-width="400" :max-height="400" /> <div class="image-node">
<Handle type="target" position="left" :id="`${id}-target`" /> <Handle type="target" position="left" :id="`${id}-target`"/>
<div class="image-content"> <div class="image-content">
<img v-if="props.data && props.data.url" :src="props.data.url" alt="图片节点" style="width:100%;height:100%;object-fit:contain;" /> <img v-if="props.data && props.data.url" :src="props.data.url" alt="图片节点"
style="width:100%;height:100%;object-fit:contain;"/>
<div v-else class="image-placeholder">未上传图片</div> <div v-else class="image-placeholder">未上传图片</div>
</div> </div>
<Handle type="source" position="right" :id="`${id}-source`" /> <Handle type="source" position="right" :id="`${id}-source`"/>
</div> </div>
</template> </template>
<style scoped> <style scoped>
@ -41,14 +42,22 @@ watch(() => props.data, (newData) => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
position: relative;
width: 100%;
height: 100%;
min-width: 180px;
min-height: 180px;
} }
.image-content { .image-content {
position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.image-placeholder { .image-placeholder {
color: #bbb; color: #bbb;
font-size: 14px; font-size: 14px;

View File

@ -205,15 +205,14 @@ defineExpose({
</script> </script>
<template> <template>
<div class="property-node" :class="[currentProperty.priority ? `priority-${currentProperty.priority}` : '']" :style="{ width: `${nodeWidth}px`, height: `${nodeHeight}px` }"> <NodeResizer
<NodeResizer
v-if="selected" v-if="selected"
:min-width="150" :min-width="150"
:min-height="150" :min-height="150"
:max-width="300" :max-width="300"
:max-height="300" :max-height="300"
/> />
<div class="property-node" :class="[currentProperty.priority ? `priority-${currentProperty.priority}` : '']" >
<!-- 输入连接点 --> <!-- 输入连接点 -->
<Handle type="target" position="left" :id="`${id}-target`" /> <Handle type="target" position="left" :id="`${id}-target`" />
@ -252,12 +251,28 @@ defineExpose({
</template> </template>
<style scoped> <style scoped>
.property-node {
position: relative;
width: 100%;
height: 100%;
min-width: 180px;
min-height: 180px;
}
.node-content { .node-content {
position: relative;
background-color: white; background-color: white;
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
border-radius: 4px; border-radius: 4px;
padding: 0; padding: 0;
cursor: pointer; cursor: pointer;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 100%;
height: 100%;
min-width: 180px;
min-height: 180px;
} }
:deep(.vue-flow__node-resizer) { :deep(.vue-flow__node-resizer) {

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'; import {ref, onMounted, onUnmounted, watch} from 'vue';
import { Handle, Position, useVueFlow } from '@vue-flow/core'; import {Handle, Position, useVueFlow} from '@vue-flow/core';
import { NodeResizer } from '@vue-flow/node-resizer' import {NodeResizer} from '@vue-flow/node-resizer'
import '@vue-flow/node-resizer/dist/style.css'; import '@vue-flow/node-resizer/dist/style.css';
const props = defineProps({ const props = defineProps({
@ -11,10 +11,10 @@ const props = defineProps({
}); });
// 获取Vue Flow的实例和节点更新方法 // 获取Vue Flow的实例和节点更新方法
const { findNode, updateNode } = useVueFlow(); const {findNode, updateNode} = useVueFlow();
// 御魂信息保存在节点数据中 // 御魂信息保存在节点数据中
const currentYuhun = ref({ name: '未选择御魂', avatar: '', type: '' }); const currentYuhun = ref({name: '未选择御魂', avatar: '', type: ''});
// 节点尺寸 // 节点尺寸
const nodeWidth = ref(180); const nodeWidth = ref(180);
@ -25,7 +25,7 @@ watch(() => props.data, (newData) => {
if (newData && newData.yuhun) { if (newData && newData.yuhun) {
currentYuhun.value = newData.yuhun; currentYuhun.value = newData.yuhun;
} }
}, { immediate: true }); }, {immediate: true});
// 更新御魂信息的方法将由App.vue调用 // 更新御魂信息的方法将由App.vue调用
const updateNodeYuhun = (yuhun) => { const updateNodeYuhun = (yuhun) => {
@ -34,14 +34,14 @@ const updateNodeYuhun = (yuhun) => {
// 备用方案:通过全局事件总线监听更新 // 备用方案:通过全局事件总线监听更新
const handleYuhunUpdate = (event) => { const handleYuhunUpdate = (event) => {
const { nodeId, yuhun } = event.detail; const {nodeId, yuhun} = event.detail;
if (nodeId === props.id) { if (nodeId === props.id) {
updateNodeYuhun(yuhun); updateNodeYuhun(yuhun);
} }
}; };
// 处理调整大小 // 处理调整大小
const handleResize = (event, { width, height }) => { const handleResize = (event, {width, height}) => {
// 更新本地状态 // 更新本地状态
nodeWidth.value = width; nodeWidth.value = width;
nodeHeight.value = height; nodeHeight.value = height;
@ -84,17 +84,16 @@ defineExpose({
</script> </script>
<template> <template>
<div class="yuhun-node" :style="{ width: `${nodeWidth}px`, height: `${nodeHeight}px` }"> <NodeResizer
<NodeResizer
v-if="selected" v-if="selected"
:min-width="150" :min-width="150"
:min-height="150" :min-height="150"
:max-width="300" :max-width="300"
:max-height="300" :max-height="300"
/> />
<div class="yuhun-node" >
<!-- 输入连接点 --> <!-- 输入连接点 -->
<Handle type="target" position="left" :id="`${id}-target`" /> <Handle type="target" position="left" :id="`${id}-target`"/>
<div class="node-content"> <div class="node-content">
<div class="node-header"> <div class="node-header">
@ -103,7 +102,7 @@ defineExpose({
<div class="node-body"> <div class="node-body">
<div v-if="currentYuhun.avatar" class="yuhun-avatar"> <div v-if="currentYuhun.avatar" class="yuhun-avatar">
<img :src="currentYuhun.avatar" alt="御魂图片" /> <img :src="currentYuhun.avatar" alt="御魂图片"/>
</div> </div>
<div v-else class="yuhun-placeholder"> <div v-else class="yuhun-placeholder">
点击选择御魂 点击选择御魂
@ -114,17 +113,33 @@ defineExpose({
</div> </div>
<!-- 输出连接点 --> <!-- 输出连接点 -->
<Handle type="source" position="right" :id="`${id}-source`" /> <Handle type="source" position="right" :id="`${id}-source`"/>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.yuhun-node {
position: relative;
width: 100%;
height: 100%;
min-width: 180px;
min-height: 180px;
}
.node-content { .node-content {
position: relative;
background-color: white; background-color: white;
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
border-radius: 4px; border-radius: 4px;
padding: 0; padding: 0;
cursor: pointer; cursor: pointer;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 100%;
height: 100%;
min-width: 180px;
min-height: 180px;
} }
:deep(.vue-flow__node-resizer) { :deep(.vue-flow__node-resizer) {