mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2026-03-05 15:05:27 +00:00
feat: 简化 TextNode 实现,完全依赖 LogicFlow 原生文本节点能力
- 移除自定义文本编辑逻辑,交由 LogicFlow 和 TextNodeModel 处理 - TextNode.vue 简化为空容器,LogicFlow 自动渲染文本内容 - 保留 TextNodeModel.ts 用于配置文本样式和编辑行为 - 采用模型-视图分离架构,符合 LogicFlow 设计模式
This commit is contained in:
@@ -45,9 +45,9 @@ const componentGroups = [
|
|||||||
id: 'text',
|
id: 'text',
|
||||||
name: '文字编辑框',
|
name: '文字编辑框',
|
||||||
type: 'textNode',
|
type: 'textNode',
|
||||||
description: '可编辑富文本的节点',
|
description: '可编辑文本的节点',
|
||||||
data: {
|
data: {
|
||||||
html: '<div>双击右侧可编辑文字</div>',
|
text: '双击编辑文字',
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 120
|
height: 120
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ import YuhunSelectNode from './nodes/yys/YuhunSelectNode.vue';
|
|||||||
import PropertySelectNode from './nodes/yys/PropertySelectNode.vue';
|
import PropertySelectNode from './nodes/yys/PropertySelectNode.vue';
|
||||||
import ImageNode from './nodes/common/ImageNode.vue';
|
import ImageNode from './nodes/common/ImageNode.vue';
|
||||||
import AssetSelectorNode from './nodes/common/AssetSelectorNode.vue';
|
import AssetSelectorNode from './nodes/common/AssetSelectorNode.vue';
|
||||||
// import TextNode from './nodes/common/TextNode.vue';
|
import TextNode from './nodes/common/TextNode.vue';
|
||||||
|
import TextNodeModel from './nodes/common/TextNodeModel';
|
||||||
import PropertyPanel from './PropertyPanel.vue';
|
import PropertyPanel from './PropertyPanel.vue';
|
||||||
import { useGlobalMessage } from '@/ts/useGlobalMessage';
|
import { useGlobalMessage } from '@/ts/useGlobalMessage';
|
||||||
import { setLogicFlowInstance, destroyLogicFlowInstance } from '@/ts/useLogicFlow';
|
import { setLogicFlowInstance, destroyLogicFlowInstance } from '@/ts/useLogicFlow';
|
||||||
@@ -312,7 +313,7 @@ function bringForward(nodeId?: string) {
|
|||||||
if (!targetId) return;
|
if (!targetId) return;
|
||||||
|
|
||||||
const currentNode = lfInstance.getNodeModelById(targetId);
|
const currentNode = lfInstance.getNodeModelById(targetId);
|
||||||
if (!currentNode) return;
|
if (!currentNode) return;t
|
||||||
|
|
||||||
const currentZIndex = currentNode.zIndex;
|
const currentZIndex = currentNode.zIndex;
|
||||||
currentNode.setZIndex(currentZIndex + 1);
|
currentNode.setZIndex(currentZIndex + 1);
|
||||||
@@ -662,7 +663,7 @@ function registerNodes(lfInstance: LogicFlow) {
|
|||||||
|
|
||||||
register({ type: 'imageNode', component: ImageNode }, lfInstance);
|
register({ type: 'imageNode', component: ImageNode }, lfInstance);
|
||||||
register({ type: 'assetSelector', component: AssetSelectorNode }, lfInstance);
|
register({ type: 'assetSelector', component: AssetSelectorNode }, lfInstance);
|
||||||
// register({ type: 'textNode', component: TextNode }, lfInstance);
|
register({ type: 'textNode', component: TextNode, model: TextNodeModel }, lfInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化 LogicFlow
|
// 初始化 LogicFlow
|
||||||
@@ -677,6 +678,22 @@ onMounted(() => {
|
|||||||
keyboard: {
|
keyboard: {
|
||||||
enabled: true
|
enabled: true
|
||||||
},
|
},
|
||||||
|
style: {
|
||||||
|
text: {
|
||||||
|
color: '#333333',
|
||||||
|
fontSize: 14,
|
||||||
|
background: {
|
||||||
|
fill: '#ffffff',
|
||||||
|
stroke: '#dcdfe6',
|
||||||
|
strokeWidth: 1,
|
||||||
|
radius: 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeText: {
|
||||||
|
color: '#333333',
|
||||||
|
fontSize: 14
|
||||||
|
}
|
||||||
|
},
|
||||||
plugins: [Menu, Label, Snapshot, SelectionSelect, MiniMap, Control],
|
plugins: [Menu, Label, Snapshot, SelectionSelect, MiniMap, Control],
|
||||||
pluginsOptions: {
|
pluginsOptions: {
|
||||||
label: {
|
label: {
|
||||||
|
|||||||
@@ -1,51 +1,30 @@
|
|||||||
<!--<script setup lang="ts">-->
|
<script setup lang="ts">
|
||||||
<!--import { ref, watch } from 'vue';-->
|
// LogicFlow 会自动处理文本节点的渲染和编辑
|
||||||
<!--import { Handle, useVueFlow } from '@vue-flow/core';-->
|
</script>
|
||||||
<!--import { NodeResizer } from '@vue-flow/node-resizer';-->
|
|
||||||
<!--import '@vue-flow/node-resizer/dist/style.css';-->
|
|
||||||
|
|
||||||
<!--const props = defineProps({-->
|
<template>
|
||||||
<!-- data: Object,-->
|
<div class="text-content">
|
||||||
<!-- id: String,-->
|
<!-- LogicFlow 会自动渲染 text 内容 -->
|
||||||
<!-- selected: Boolean-->
|
</div>
|
||||||
<!--});-->
|
</template>
|
||||||
|
|
||||||
<!--const nodeWidth = ref(200);-->
|
<style scoped>
|
||||||
<!--const nodeHeight = ref(120);-->
|
.text-content {
|
||||||
<!--const html = ref('');-->
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
outline: none;
|
||||||
|
word-break: break-word;
|
||||||
|
padding: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
<!--watch(() => props.data, (newData) => {-->
|
.text-content:focus {
|
||||||
<!-- if (newData && newData.html !== undefined) html.value = newData.html;-->
|
background: rgba(64, 158, 255, 0.1);
|
||||||
<!-- if (newData && newData.width) nodeWidth.value = newData.width;-->
|
border-radius: 2px;
|
||||||
<!-- if (newData && newData.height) nodeHeight.value = newData.height;-->
|
}
|
||||||
<!--}, { immediate: true });-->
|
</style>
|
||||||
<!--</script>-->
|
|
||||||
<!--<template>-->
|
|
||||||
<!-- <div class="text-node" :style="{ width: `${nodeWidth}px`, height: `${nodeHeight}px` }">-->
|
|
||||||
<!-- <NodeResizer v-if="selected" :min-width="80" :min-height="40" :max-width="400" :max-height="400" />-->
|
|
||||||
<!-- <Handle type="target" position="left" :id="`${id}-target`" />-->
|
|
||||||
<!-- <div class="text-content" v-html="html"></div>-->
|
|
||||||
<!-- <Handle type="source" position="right" :id="`${id}-source`" />-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!--</template>-->
|
|
||||||
<!--<style scoped>-->
|
|
||||||
<!--.text-node {-->
|
|
||||||
<!-- background: #fff;-->
|
|
||||||
<!-- border: 1px solid #dcdfe6;-->
|
|
||||||
<!-- border-radius: 4px;-->
|
|
||||||
<!-- display: flex;-->
|
|
||||||
<!-- flex-direction: column;-->
|
|
||||||
<!-- align-items: center;-->
|
|
||||||
<!-- justify-content: center;-->
|
|
||||||
<!-- overflow: hidden;-->
|
|
||||||
<!--}-->
|
|
||||||
<!--.text-content {-->
|
|
||||||
<!-- width: 100%;-->
|
|
||||||
<!-- height: 100%;-->
|
|
||||||
<!-- padding: 8px;-->
|
|
||||||
<!-- font-size: 15px;-->
|
|
||||||
<!-- color: #333;-->
|
|
||||||
<!-- word-break: break-all;-->
|
|
||||||
<!-- overflow: auto;-->
|
|
||||||
<!--}-->
|
|
||||||
<!--</style>-->
|
|
||||||
|
|||||||
37
src/components/flow/nodes/common/TextNodeModel.ts
Normal file
37
src/components/flow/nodes/common/TextNodeModel.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { HtmlNodeModel } from '@logicflow/core';
|
||||||
|
|
||||||
|
class TextNodeModel extends HtmlNodeModel {
|
||||||
|
initNodeData(data: any) {
|
||||||
|
super.initNodeData(data);
|
||||||
|
// 启用文本编辑功能,支持双击编辑
|
||||||
|
this.text.editable = true;
|
||||||
|
this.text.draggable = false;
|
||||||
|
|
||||||
|
// 如果有 text 属性,设置为文本内容
|
||||||
|
if (data.properties?.text) {
|
||||||
|
this.text.value = data.properties.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setAttributes() {
|
||||||
|
// 设置默认尺寸
|
||||||
|
this.width = 200;
|
||||||
|
this.height = 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义文本样式
|
||||||
|
getTextStyle() {
|
||||||
|
const style = super.getTextStyle();
|
||||||
|
style.fontSize = 14;
|
||||||
|
style.color = '#333';
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当文本被编辑后,同步到 properties
|
||||||
|
updateText(value: string) {
|
||||||
|
super.updateText(value);
|
||||||
|
this.setProperty('text', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TextNodeModel;
|
||||||
@@ -8,7 +8,15 @@ const props = defineProps<{
|
|||||||
<div class="property-section">
|
<div class="property-section">
|
||||||
<div class="section-header">文本节点</div>
|
<div class="section-header">文本节点</div>
|
||||||
<div class="property-item">
|
<div class="property-item">
|
||||||
<div class="property-value">文本编辑器待实现,当前节点内容:{{ props.node?.properties?.text?.content || '未设置' }}</div>
|
<label class="property-label">内容</label>
|
||||||
|
<div class="property-value">
|
||||||
|
{{ props.node?.properties?.text || '未设置' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="property-item">
|
||||||
|
<div class="property-note">
|
||||||
|
💡 提示:双击画布中的节点即可编辑文字
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user