mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2026-03-05 06:55:26 +00:00
docs: update progress for minimap control and toolbar toggles
This commit is contained in:
@@ -10,10 +10,10 @@
|
|||||||
- DnD 接入:由组件库触发拖拽放置
|
- DnD 接入:由组件库触发拖拽放置
|
||||||
- 右键菜单:节点置顶/置底与删除、边删除、画布添加节点(基于 LogicFlow Menu + `setElementZIndex`)
|
- 右键菜单:节点置顶/置底与删除、边删除、画布添加节点(基于 LogicFlow Menu + `setElementZIndex`)
|
||||||
- 多选/框选、对齐线、吸附网格;左/右/上/下/水平/垂直居中与横/纵等距分布(SelectionSelect + snapline + 自定义对齐分布指令)
|
- 多选/框选、对齐线、吸附网格;左/右/上/下/水平/垂直居中与横/纵等距分布(SelectionSelect + snapline + 自定义对齐分布指令)
|
||||||
|
- 扩展与控制:接入 MiniMap + Control 插件;吸附/对齐线/框选开关共享到 Toolbar 与 FlowEditor;新增清空画布入口
|
||||||
- 未完成:
|
- 未完成:
|
||||||
- 右键菜单层级命令:已接通置顶/置底,单步前移/后移(`bringForward`/`sendBackward`)未实现
|
- 右键菜单层级命令:已接通置顶/置底,单步前移/后移(`bringForward`/`sendBackward`)未实现
|
||||||
- 撤销重做、组合/锁定/隐藏、快捷键(Del/Ctrl+C/V、方向键微移、Ctrl+Z/Y)
|
- 撤销重做、组合/锁定/隐藏、快捷键(Del/Ctrl+C/V、方向键微移、Ctrl+Z/Y)
|
||||||
- MiniMap/控制条/Snapshot 等扩展能力
|
|
||||||
|
|
||||||
## 2. 左侧组件库(Palette) — 完成度:60%
|
## 2. 左侧组件库(Palette) — 完成度:60%
|
||||||
- 已完成:
|
- 已完成:
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
4) 多选/对齐/吸附:框选、对齐线、吸附网格;左/右/上/下/水平/垂直居中与横/纵等距分布(FlowEditor/extension)。已完成
|
4) 多选/对齐/吸附:框选、对齐线、吸附网格;左/右/上/下/水平/垂直居中与横/纵等距分布(FlowEditor/extension)。已完成
|
||||||
5) 快捷键与微调:Del 删除、方向键微移、Ctrl+C/V 复制粘贴、Ctrl+G/U 组/解组(简单组:父 meta id + 同步移动)、锁定/隐藏(`properties.locked`/`visible`)。
|
5) 快捷键与微调:Del 删除、方向键微移、Ctrl+C/V 复制粘贴、Ctrl+G/U 组/解组(简单组:父 meta id + 同步移动)、锁定/隐藏(`properties.locked`/`visible`)。
|
||||||
6) 样式模型补齐:统一 `properties.style` 字段并在 PropertyPanel 全量编辑(填充/描边/圆角/阴影/透明度/文字对齐/行高/字重)。【已完成】
|
6) 样式模型补齐:统一 `properties.style` 字段并在 PropertyPanel 全量编辑(填充/描边/圆角/阴影/透明度/文字对齐/行高/字重)。【已完成】
|
||||||
7) 扩展与控制:接入 MiniMap/Control/Snapshot;Toolbar 增加吸附/对齐开关与清空画布。
|
7) 扩展与控制:接入 MiniMap/Control/Snapshot;Toolbar 增加吸附/对齐开关与清空画布。【已完成:MiniMap + Control 接入;Toolbar/FlowEditor 共享开关 + 清空画布;Snapshot 已有】
|
||||||
8) 矢量节点 MVP:`vectorNode`(SVG path/rect/ellipse/polygon),属性面板支持 path/stroke/fill/strokeWidth;新增 SVG 导入弹窗。
|
8) 矢量节点 MVP:`vectorNode`(SVG path/rect/ellipse/polygon),属性面板支持 path/stroke/fill/strokeWidth;新增 SVG 导入弹窗。
|
||||||
9) 资源与导出增强:图片资源选择/上传弹窗(base64 或预留 `assetId`),导出 SVG/PDF(PDF 可延后)。
|
9) 资源与导出增强:图片资源选择/上传弹窗(base64 或预留 `assetId`),导出 SVG/PDF(PDF 可延后)。
|
||||||
10) 历史与撤销重做:抽象 Action + HistoryService,记录增删改/移动/层级;Ctrl+Z/Y。
|
10) 历史与撤销重做:抽象 Action + HistoryService,记录增删改/移动/层级;Ctrl+Z/Y。
|
||||||
|
|||||||
114
docs/2design/StyleAndAppearance.md
Normal file
114
docs/2design/StyleAndAppearance.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# 样式与节点结构说明(v1)
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
- 目标:统一节点的样式字段与渲染消费路径,避免各节点分散管理填充/描边/阴影/文字等样式。
|
||||||
|
- 数据载体:沿用 LogicFlow 的 `GraphData`,仅约定 `node.properties` 内的业务字段与样式字段。
|
||||||
|
|
||||||
|
## 样式模型:`properties.style`
|
||||||
|
位于每个节点 `properties.style`,是尺寸与外观的单一事实来源;渲染时会同步 `style.width/height` 到节点的 `width/height`。
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface NodeStyle {
|
||||||
|
// 尺寸与变换
|
||||||
|
width: number; // px,必填
|
||||||
|
height: number; // px,必填
|
||||||
|
rotate?: number; // deg,逆时针,围绕节点中心
|
||||||
|
|
||||||
|
// 形状
|
||||||
|
fill?: string; // 背景填充色
|
||||||
|
stroke?: string; // 描边色
|
||||||
|
strokeWidth?: number; // ≥0,px
|
||||||
|
radius?: number | [number, number, number, number]; // 圆角(rect 生效)
|
||||||
|
opacity?: number; // 0..1
|
||||||
|
|
||||||
|
// 阴影
|
||||||
|
shadow?: { color?: string; blur?: number; offsetX?: number; offsetY?: number };
|
||||||
|
|
||||||
|
// 文本样式(text 节点或带文本的节点)
|
||||||
|
textStyle?: {
|
||||||
|
color?: string;
|
||||||
|
fontFamily?: string;
|
||||||
|
fontSize?: number; // px
|
||||||
|
fontWeight?: number | string;
|
||||||
|
lineHeight?: number; // 推荐 1..3
|
||||||
|
align?: 'left' | 'center' | 'right';
|
||||||
|
verticalAlign?: 'top' | 'middle' | 'bottom';
|
||||||
|
letterSpacing?: number; // px
|
||||||
|
padding?: [number, number, number, number];
|
||||||
|
background?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
默认值参考 `src/ts/schema.ts:DefaultNodeStyle`:
|
||||||
|
- `width=180, height=120, rotate=0`
|
||||||
|
- `fill='#ffffff', stroke='#dcdfe6', strokeWidth=1, radius=4, opacity=1`
|
||||||
|
- `shadow={ color:'rgba(0,0,0,0.1)', blur:4, offsetX:0, offsetY:2 }`
|
||||||
|
- `textStyle={ color:'#303133', fontFamily:'system-ui', fontSize:14, fontWeight:400, lineHeight:1.4, align:'left', verticalAlign:'top', letterSpacing:0, padding:[8,8,8,8] }`
|
||||||
|
|
||||||
|
## 归一化与工具(`src/ts/nodeStyle.ts`)
|
||||||
|
- `normalizeNodeStyle(style, sizeFallback)`:补全缺省,数值钳制(opacity ∈[0,1]),将散落的 width/height 收敛到 style。
|
||||||
|
- `normalizePropertiesWithStyle(props)`:返回带规范化 style 的 props,并镜像 `width/height`。
|
||||||
|
- `toContainerStyle` / `toTextStyle`:将 `NodeStyle` 转为 Vue 行内样式(背景、描边、圆角、阴影、透明度 / 文字)。
|
||||||
|
- `styleEquals`:归一化后比较,避免重复 setProperties。
|
||||||
|
|
||||||
|
## 节点元信息:`properties.meta`
|
||||||
|
```ts
|
||||||
|
interface NodeMeta {
|
||||||
|
z?: number; // 层级
|
||||||
|
locked?: boolean; // 锁定
|
||||||
|
visible?: boolean; // 可见
|
||||||
|
groupId?: string; // 分组
|
||||||
|
name?: string;
|
||||||
|
createdAt?: number;
|
||||||
|
updatedAt?: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
FlowEditor 在渲染/normalize 时会补齐 `meta.visible=true`、`meta.locked=false` 并应用到节点模型(可见性、可拖拽等)。
|
||||||
|
|
||||||
|
## 监听与渲染路径
|
||||||
|
1) **归一化入口**:`FlowEditor.normalizeNodeModel` 在节点创建/属性变更/渲染后执行:
|
||||||
|
- 归一化 `properties.style` 和 `meta`,必要时回写 `lf.setProperties`。
|
||||||
|
- 同步 `style.width/height` 到节点模型,保证渲染尺寸一致。
|
||||||
|
2) **事件监听**:节点组件通过 composable 监听 `NODE_PROPERTIES_CHANGE`,实时更新样式与自定义数据。
|
||||||
|
|
||||||
|
## 复用方案:`useNodeAppearance`(`src/ts/useNodeAppearance.ts`)
|
||||||
|
作用:在节点组件中统一获取样式和属性变更。
|
||||||
|
```ts
|
||||||
|
const { containerStyle, textStyle } = useNodeAppearance({
|
||||||
|
onPropsChange(props, node) {
|
||||||
|
// 可在这里同步业务字段,例如 image url、shikigami 数据等
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
内部行为:
|
||||||
|
- 注入 `getNode`/`getGraph`,挂载时读取当前节点 props。
|
||||||
|
- 监听 `NODE_PROPERTIES_CHANGE`,调用 `normalizeNodeStyle`,输出 `containerStyle`/`textStyle`。
|
||||||
|
- 自动解绑监听,减少重复代码。
|
||||||
|
|
||||||
|
已接入的节点组件:
|
||||||
|
- 图片节点 `src/components/flow/nodes/common/ImageNode.vue`
|
||||||
|
- 式神节点 `.../yys/ShikigamiSelectNode.vue`
|
||||||
|
- 御魂节点 `.../yys/YuhunSelectNode.vue`
|
||||||
|
- 属性节点 `.../yys/PropertySelectNode.vue`
|
||||||
|
|
||||||
|
## 编辑入口:PropertyPanel
|
||||||
|
- 样式面板 `src/components/flow/panels/StylePanel.vue` 写入 `properties.style`:
|
||||||
|
- 填充色、描边色/线宽、圆角、阴影(色/模糊/偏移)、透明度
|
||||||
|
- 文本对齐、行高、字重
|
||||||
|
- 图片面板 `ImagePanel.vue` 写入宽高/fit/url,并保持与 `style.width/height` 同步。
|
||||||
|
- 其他面板(式神/御魂/属性)仅写业务字段,样式统一由 `useNodeAppearance` 消费。
|
||||||
|
|
||||||
|
## 持久化与 schema
|
||||||
|
- 顶层导出结构:`RootDocument`(见 `docs/2design/DataModel.md`),包含 `schemaVersion`。
|
||||||
|
- 保存/导入路径:`useFilesStore` 写出/读入 `schemaVersion`;缺省数据走 `migrateToV1` 将散落的宽高/可见/锁定补齐到 `properties.style/meta`。
|
||||||
|
|
||||||
|
## 校验建议
|
||||||
|
- width/height > 0;strokeWidth ≥ 0;radius ≥ 0 或四元组合法;opacity ∈ [0,1]。
|
||||||
|
- 文本:fontSize > 0,lineHeight 合理(1~3),padding 四元组 ≥ 0。
|
||||||
|
- 圆角仅对 rect 类节点生效;其他 kind 忽略 radius。
|
||||||
|
|
||||||
|
## 后续扩展建议
|
||||||
|
- 文本节点接入富文本:在 `textStyle` 扩展行高倍数、字间距、背景/描边。
|
||||||
|
- 叠加高级效果:blendMode/filter;保持向后兼容,新增字段时通过 `schemaVersion` 控制迁移。
|
||||||
|
- 矢量节点:在 `vector` 节点中消费同一套 `style`,并在编辑面板复用 `StylePanel`。
|
||||||
11
src/App.vue
11
src/App.vue
@@ -21,6 +21,13 @@ const toolbarHeight = 48; // 工具栏的高度
|
|||||||
const windowHeight = ref(window.innerHeight);
|
const windowHeight = ref(window.innerHeight);
|
||||||
const contentHeight = computed(() => `${windowHeight.value - toolbarHeight}px`);
|
const contentHeight = computed(() => `${windowHeight.value - toolbarHeight}px`);
|
||||||
|
|
||||||
|
const normalizeGraphData = (data: any) => {
|
||||||
|
if (data && Array.isArray((data as any).nodes) && Array.isArray((data as any).edges)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return { nodes: [], edges: [] };
|
||||||
|
};
|
||||||
|
|
||||||
const handleTabsEdit = (
|
const handleTabsEdit = (
|
||||||
targetName: string | undefined,
|
targetName: string | undefined,
|
||||||
action: 'remove' | 'add'
|
action: 'remove' | 'add'
|
||||||
@@ -56,7 +63,7 @@ watch(
|
|||||||
|
|
||||||
if (logicFlowInstance && currentTab?.graphRawData) {
|
if (logicFlowInstance && currentTab?.graphRawData) {
|
||||||
try {
|
try {
|
||||||
logicFlowInstance.render(currentTab.graphRawData);
|
logicFlowInstance.render(normalizeGraphData(currentTab.graphRawData));
|
||||||
logicFlowInstance.zoom(
|
logicFlowInstance.zoom(
|
||||||
currentTab.transform?.SCALE_X ?? 1,
|
currentTab.transform?.SCALE_X ?? 1,
|
||||||
[currentTab.transform?.TRANSLATE_X ?? 0, currentTab.transform?.TRANSLATE_Y ?? 0]
|
[currentTab.transform?.TRANSLATE_X ?? 0, currentTab.transform?.TRANSLATE_Y ?? 0]
|
||||||
@@ -79,7 +86,7 @@ watch(
|
|||||||
|
|
||||||
if (logicFlowInstance && currentTab?.graphRawData) {
|
if (logicFlowInstance && currentTab?.graphRawData) {
|
||||||
try {
|
try {
|
||||||
logicFlowInstance.render(currentTab.graphRawData);
|
logicFlowInstance.render(normalizeGraphData(currentTab.graphRawData));
|
||||||
logicFlowInstance.zoom(
|
logicFlowInstance.zoom(
|
||||||
currentTab.transform?.SCALE_X ?? 1,
|
currentTab.transform?.SCALE_X ?? 1,
|
||||||
[currentTab.transform?.TRANSLATE_X ?? 0, currentTab.transform?.TRANSLATE_Y ?? 0]
|
[currentTab.transform?.TRANSLATE_X ?? 0, currentTab.transform?.TRANSLATE_Y ?? 0]
|
||||||
|
|||||||
@@ -9,6 +9,30 @@
|
|||||||
<el-button type="info" @click="showUpdateLog">{{ t('updateLog') }}</el-button>
|
<el-button type="info" @click="showUpdateLog">{{ t('updateLog') }}</el-button>
|
||||||
<el-button type="warning" @click="showFeedbackForm">{{ t('feedback') }}</el-button>
|
<el-button type="warning" @click="showFeedbackForm">{{ t('feedback') }}</el-button>
|
||||||
<el-button type="danger" @click="handleResetWorkspace">重置工作区</el-button>
|
<el-button type="danger" @click="handleResetWorkspace">重置工作区</el-button>
|
||||||
|
<el-button type="warning" plain @click="handleClearCanvas">清空画布</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-controls">
|
||||||
|
<el-switch
|
||||||
|
v-model="selectionEnabled"
|
||||||
|
size="small"
|
||||||
|
inline-prompt
|
||||||
|
active-text="框选开"
|
||||||
|
inactive-text="框选关"
|
||||||
|
/>
|
||||||
|
<el-switch
|
||||||
|
v-model="snapGridEnabled"
|
||||||
|
size="small"
|
||||||
|
inline-prompt
|
||||||
|
active-text="吸附开"
|
||||||
|
inactive-text="吸附关"
|
||||||
|
/>
|
||||||
|
<el-switch
|
||||||
|
v-model="snaplineEnabled"
|
||||||
|
size="small"
|
||||||
|
inline-prompt
|
||||||
|
active-text="对齐线开"
|
||||||
|
inactive-text="对齐线关"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 更新日志对话框 -->
|
<!-- 更新日志对话框 -->
|
||||||
@@ -84,9 +108,11 @@ import { useFilesStore } from "@/ts/useStore";
|
|||||||
import { ElMessageBox } from "element-plus";
|
import { ElMessageBox } from "element-plus";
|
||||||
import { useGlobalMessage } from "@/ts/useGlobalMessage";
|
import { useGlobalMessage } from "@/ts/useGlobalMessage";
|
||||||
import { getLogicFlowInstance } from "@/ts/useLogicFlow";
|
import { getLogicFlowInstance } from "@/ts/useLogicFlow";
|
||||||
|
import { useCanvasSettings } from '@/ts/useCanvasSettings';
|
||||||
|
|
||||||
const filesStore = useFilesStore();
|
const filesStore = useFilesStore();
|
||||||
const { showMessage } = useGlobalMessage();
|
const { showMessage } = useGlobalMessage();
|
||||||
|
const { selectionEnabled, snapGridEnabled, snaplineEnabled } = useCanvasSettings();
|
||||||
|
|
||||||
// 获取当前的 i18n 实例
|
// 获取当前的 i18n 实例
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
@@ -228,6 +254,39 @@ const handleResetWorkspace = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClearCanvas = () => {
|
||||||
|
ElMessageBox.confirm('仅清空当前画布,不影响其他文件,确定继续?', '提示', {
|
||||||
|
confirmButtonText: '清空',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
}).then(() => {
|
||||||
|
const lfInstance = getLogicFlowInstance();
|
||||||
|
const activeId = filesStore.activeFileId;
|
||||||
|
const activeFile = filesStore.getTab(activeId);
|
||||||
|
|
||||||
|
if (lfInstance) {
|
||||||
|
lfInstance.clearData();
|
||||||
|
lfInstance.render({ nodes: [], edges: [] });
|
||||||
|
lfInstance.zoom(1, [0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeFile) {
|
||||||
|
activeFile.graphRawData = { nodes: [], edges: [] };
|
||||||
|
activeFile.transform = {
|
||||||
|
SCALE_X: 1,
|
||||||
|
SCALE_Y: 1,
|
||||||
|
TRANSLATE_X: 0,
|
||||||
|
TRANSLATE_Y: 0
|
||||||
|
};
|
||||||
|
filesStore.updateTab(activeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessage('success', '当前画布已清空');
|
||||||
|
}).catch(() => {
|
||||||
|
// 用户取消
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const watermark = reactive({
|
const watermark = reactive({
|
||||||
text: localStorage.getItem('watermark.text') || '示例水印',
|
text: localStorage.getItem('watermark.text') || '示例水印',
|
||||||
fontSize: Number(localStorage.getItem('watermark.fontSize')) || 30,
|
fontSize: Number(localStorage.getItem('watermark.fontSize')) || 30,
|
||||||
@@ -363,6 +422,13 @@ const handleClose = (done) => {
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="editor-layout" :style="{ height }">
|
<div class="editor-layout" :style="{ height }">
|
||||||
<!-- 中间流程图区域 -->
|
<!-- 中间流程图区域 -->
|
||||||
<div class="flow-container">
|
<div class="flow-container" :class="{ 'snapline-disabled': !snaplineEnabled }">
|
||||||
<div class="flow-controls">
|
<div class="flow-controls">
|
||||||
<div class="control-row toggles">
|
<div class="control-row toggles">
|
||||||
<label class="control-toggle">
|
<label class="control-toggle">
|
||||||
@@ -12,7 +12,10 @@
|
|||||||
<input type="checkbox" v-model="snapGridEnabled" />
|
<input type="checkbox" v-model="snapGridEnabled" />
|
||||||
<span>吸附网格</span>
|
<span>吸附网格</span>
|
||||||
</label>
|
</label>
|
||||||
<span class="control-hint">对齐线已开启</span>
|
<label class="control-toggle">
|
||||||
|
<input type="checkbox" v-model="snaplineEnabled" />
|
||||||
|
<span>对齐线</span>
|
||||||
|
</label>
|
||||||
<span class="control-hint">已选 {{ selectedCount }}</span>
|
<span class="control-hint">已选 {{ selectedCount }}</span>
|
||||||
<button class="control-button" type="button" @click="showAllNodes">显示全部</button>
|
<button class="control-button" type="button" @click="showAllNodes">显示全部</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,7 +62,7 @@ import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
|
|||||||
import LogicFlow, { EventType } from '@logicflow/core';
|
import LogicFlow, { EventType } from '@logicflow/core';
|
||||||
import type { Position, NodeData, EdgeData, BaseNodeModel, GraphModel, GraphData } from '@logicflow/core';
|
import type { Position, NodeData, EdgeData, BaseNodeModel, GraphModel, GraphData } from '@logicflow/core';
|
||||||
import '@logicflow/core/lib/style/index.css';
|
import '@logicflow/core/lib/style/index.css';
|
||||||
import { Menu, Label, Snapshot, SelectionSelect } from '@logicflow/extension';
|
import { Menu, Label, Snapshot, SelectionSelect, MiniMap, Control } from '@logicflow/extension';
|
||||||
import '@logicflow/extension/lib/style/index.css';
|
import '@logicflow/extension/lib/style/index.css';
|
||||||
import '@logicflow/core/es/index.css';
|
import '@logicflow/core/es/index.css';
|
||||||
import '@logicflow/extension/es/index.css';
|
import '@logicflow/extension/es/index.css';
|
||||||
@@ -75,6 +78,7 @@ 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';
|
||||||
import { normalizePropertiesWithStyle, normalizeNodeStyle, styleEquals } from '@/ts/nodeStyle';
|
import { normalizePropertiesWithStyle, normalizeNodeStyle, styleEquals } from '@/ts/nodeStyle';
|
||||||
|
import { useCanvasSettings } from '@/ts/useCanvasSettings';
|
||||||
|
|
||||||
type AlignType = 'left' | 'right' | 'top' | 'bottom' | 'hcenter' | 'vcenter';
|
type AlignType = 'left' | 'right' | 'top' | 'bottom' | 'hcenter' | 'vcenter';
|
||||||
type DistributeType = 'horizontal' | 'vertical';
|
type DistributeType = 'horizontal' | 'vertical';
|
||||||
@@ -90,8 +94,7 @@ const props = defineProps<{
|
|||||||
const containerRef = ref<HTMLElement | null>(null);
|
const containerRef = ref<HTMLElement | null>(null);
|
||||||
const lf = ref<LogicFlow | null>(null);
|
const lf = ref<LogicFlow | null>(null);
|
||||||
const selectedCount = ref(0);
|
const selectedCount = ref(0);
|
||||||
const selectionEnabled = ref(true);
|
const { selectionEnabled, snapGridEnabled, snaplineEnabled } = useCanvasSettings();
|
||||||
const snapGridEnabled = ref(true);
|
|
||||||
const alignmentButtons: { key: AlignType; label: string }[] = [
|
const alignmentButtons: { key: AlignType; label: string }[] = [
|
||||||
{ key: 'left', label: '左对齐' },
|
{ key: 'left', label: '左对齐' },
|
||||||
{ key: 'right', label: '右对齐' },
|
{ key: 'right', label: '右对齐' },
|
||||||
@@ -578,11 +581,11 @@ onMounted(() => {
|
|||||||
allowResize: true,
|
allowResize: true,
|
||||||
allowRotate: true,
|
allowRotate: true,
|
||||||
overlapMode: -1,
|
overlapMode: -1,
|
||||||
snapline: true,
|
snapline: snaplineEnabled.value,
|
||||||
keyboard: {
|
keyboard: {
|
||||||
enabled: true
|
enabled: true
|
||||||
},
|
},
|
||||||
plugins: [Menu, Label, Snapshot, SelectionSelect],
|
plugins: [Menu, Label, Snapshot, SelectionSelect, MiniMap, Control],
|
||||||
pluginsOptions: {
|
pluginsOptions: {
|
||||||
label: {
|
label: {
|
||||||
isMultiple: true,
|
isMultiple: true,
|
||||||
@@ -591,6 +594,14 @@ onMounted(() => {
|
|||||||
// textOverflowMode -> 'ellipsis' | 'wrap' | 'clip' | 'nowrap' | 'default'
|
// textOverflowMode -> 'ellipsis' | 'wrap' | 'clip' | 'nowrap' | 'default'
|
||||||
textOverflowMode: 'wrap',
|
textOverflowMode: 'wrap',
|
||||||
},
|
},
|
||||||
|
miniMap: {
|
||||||
|
isShowHeader: false,
|
||||||
|
isShowCloseIcon: true,
|
||||||
|
width: 200,
|
||||||
|
height: 140,
|
||||||
|
rightPosition: 16,
|
||||||
|
bottomPosition: 16
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -665,8 +676,10 @@ onMounted(() => {
|
|||||||
registerNodes(lfInstance);
|
registerNodes(lfInstance);
|
||||||
setLogicFlowInstance(lfInstance);
|
setLogicFlowInstance(lfInstance);
|
||||||
lfInstance.render({
|
lfInstance.render({
|
||||||
// 渲染的数据
|
nodes: [],
|
||||||
|
edges: []
|
||||||
});
|
});
|
||||||
|
lfInstance.extension.miniMap.show();
|
||||||
normalizeAllNodes();
|
normalizeAllNodes();
|
||||||
lfInstance.updateEditConfig({
|
lfInstance.updateEditConfig({
|
||||||
multipleSelectKey: 'shift',
|
multipleSelectKey: 'shift',
|
||||||
@@ -723,6 +736,14 @@ watch(snapGridEnabled, (enabled) => {
|
|||||||
applySnapGrid(enabled);
|
applySnapGrid(enabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(snaplineEnabled, (enabled) => {
|
||||||
|
const lfInstance = lf.value as any;
|
||||||
|
if (!lfInstance) return;
|
||||||
|
if (!enabled) {
|
||||||
|
lfInstance.snaplineModel?.clearSnapline?.();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 销毁 LogicFlow
|
// 销毁 LogicFlow
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
lf.value?.destroy();
|
lf.value?.destroy();
|
||||||
@@ -830,4 +851,8 @@ onBeforeUnmount(() => {
|
|||||||
background-color: #f5f7fa;
|
background-color: #f5f7fa;
|
||||||
color: #409eff;
|
color: #409eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.snapline-disabled .lf-snapline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
14
src/ts/useCanvasSettings.ts
Normal file
14
src/ts/useCanvasSettings.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
// Shared canvas control state between FlowEditor and Toolbar.
|
||||||
|
const selectionEnabled = ref(true);
|
||||||
|
const snapGridEnabled = ref(true);
|
||||||
|
const snaplineEnabled = ref(true);
|
||||||
|
|
||||||
|
export function useCanvasSettings() {
|
||||||
|
return {
|
||||||
|
selectionEnabled,
|
||||||
|
snapGridEnabled,
|
||||||
|
snaplineEnabled
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user