@lxc2006/mindmap-vue
v0.2.1
Published
字符网格思维导图 Vue 3 组件 —— @lxc2006/mindmap-core 的官方封装(平移缩放 / 节点拖拽 / 折叠选中)
Maintainers
Readme
@lxc2006/mindmap-vue
字符网格风格的 Vue 3 思维导图组件。布局引擎是
@lxc2006/mindmap-core(org-mindmap 的 TypeScript 移植,
与原版渲染逐字符对拍),本包提供开箱即用的组件封装:画布平移缩放、
节点拖拽重组、折叠选中。
完整文档站(指南 / API 参考 / 交互演示)在本仓库 docs/:
npm run docs:dev。
设计理念 ─╮ ╭─ 布局选项 ── centered / top
核心算法 ─┼ « dom-mindmap » ┬───┤ ├─ compacted 空隙填充
前端组件 ─╯ ╰─ 交互 ─┬─ 点击选中 │ 软换行 maxWidth
╰─ 节点拖拽 ╰─ 子树着色安装
npm install @lxc2006/mindmap-vue要求 Vue ^3.5(peer dependency)。核心引擎与本包的 API 一并从本包导出,
无需单独安装 @lxc2006/mindmap-core(想直接用布局引擎时再装亦可)。
使用
组件(推荐)
<script setup lang="ts">
import { ref } from 'vue'
import { DomMindmap } from '@lxc2006/mindmap-vue'
// 必须引入样式
import '@lxc2006/mindmap-vue/style.css'
import type { MindmapNodeData } from '@lxc2006/mindmap-vue'
const tree = ref<MindmapNodeData>({
text: 'Root',
children: [
{ text: '左侧分支', side: 'left', children: [{ text: '子节点' }] },
{ text: '右侧分支' },
],
})
</script>
<template>
<!-- 容器需要有确定尺寸(flex 子项 / 显式高度),组件视口填满它 -->
<div style="height: 480px">
<DomMindmap
v-model:data="tree"
:options="{ layout: 'centered', compacted: true }"
@node-drop="(info) => console.log(info.moved, info.position, info.target)"
/>
</div>
</template>插件注册
import { mindmapPlugin } from '@lxc2006/mindmap-vue'
import '@lxc2006/mindmap-vue/style.css'
app.use(mindmapPlugin) // 全局注册 <DomMindmap>组合式函数
import { useMindmap } from '@lxc2006/mindmap-vue'
const { collapsed, selectedId, toggle, expandAll, collapseToDepth } = useMindmap(tree)大纲导入
import { parseOutline } from '@lxc2006/mindmap-vue'
const tree = parseOutline(`Root
left: 左分支
子节点
右分支`)Props
| Prop | 类型 | 默认 | 说明 |
| ------------ | ----------------------------- | -------- | ------------------------------------------------------------ |
| data | MindmapNodeData | 必填 | 树数据;配合 v-model:data 接收拖拽产生的新树 |
| options | Partial<MindmapOptions> | {} | 布局选项(见下) |
| collapsed | Iterable<string> | — | 初始折叠的节点 id;之后组件内部管理并外抛 update:collapsed |
| selectedId | string \| null | null | 选中节点 id(v-model:selectedId) |
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | 主题;auto 跟随系统(含实时切换) |
| pannable | boolean | true | 画布拖拽平移(鼠标 / 触屏) |
| zoomable | boolean | true | 画布缩放(滚轮 / 双指捏合) |
| draggable | boolean | true | 节点拖拽重组树 |
Events
| 事件 | 载荷 | 说明 |
| ------------------- | ----------------------------- | ------------------------------------------------------------- |
| node-click | NodeView | 单击节点(同时触发选中) |
| node-select | string \| null | 选中变化 |
| toggle | (NodeView, boolean) | 折叠 / 展开(双击或折叠按钮) |
| update:data | MindmapNodeData | 拖拽落点有效时的新树(v-model:data) |
| node-drag-start | NodeView | 节点拖拽激活(位移超过 4px) |
| node-drop | { moved, target, position } | 拖拽成功落点;position 为 'before' \| 'after' \| 'inside' |
| update:collapsed | string[] | 折叠集合变化 |
| update:selectedId | string \| null | 选中变化(v-model 回写) |
NodeView 含 id / row / col / lines / side / color / parentId / childIds /
source 等字段,其中 source 指向用户数据树中的原节点(定位/再编辑用)。
交互
| 操作 | 效果 |
| ----------------- | ----------------------------------------------- |
| 空白 / 连线处拖拽 | 平移画布(触屏单指同理) |
| 滚轮 / 双指 | 缩放画布 |
| 节点单击 | 选中 |
| 节点双击 | 折叠 / 展开子树(折叠后显示 ▸N 隐藏计数) |
| 节点拖拽 | 重组树(4px 位移阈值激活,与点击 / 双击不冲突) |
整棵画布文字不可选中(user-select: none)——双击折叠不会选中文字,
连线也不可选中。后续引入文本编辑节点时会在编辑框内单独放开。
拖拽落点分区
节点矩形按横向 30% / 40% / 30% 分区(left 侧镜像):
right 侧目标节点:
┌──────────┬────────────────┬──────────┐
│ before │ inside │ after │
│ 成为前兄弟 │ 成为子节点 │ 成为后兄弟 │
└──────────┴────────────────┴──────────┘
left 侧镜像(树心在右):before ↔ after 互换
拖到根节点上 = 成为根的直接子节点(保留原 side)
拖进自己子树 = 拒绝(环检测),不显示落点指示移动语义由纯函数 moveNode(tree, drag, target, position) 实现
(不可变更新、side 规范化、环检测),从本包导出,可在组件外复用。
组件方法(模板 ref)
const mindmap = ref<InstanceType<typeof DomMindmap> | null>(null)
// mindmap.value?.fitView() 适配视图(内容完整可见并居中)
// mindmap.value?.zoomIn() 放大 / zoomOut() 缩小 / zoomTo(1.5)
// mindmap.value?.pan(x, y) 平移(画布局部单位,屏幕位移 = scale × 该值)
// mindmap.value?.resetView() 复位
// mindmap.value?.exportText() 导出字符网格文本数据格式与布局选项
interface MindmapNodeData {
text: string // 逻辑文本(单行)
children?: MindmapNodeData[] // 子树
side?: 'left' | 'right' // 仅根的直接子级有效,缺省 right
id?: string // 稳定标识(折叠状态等),缺省按对象身份分配
}
interface MindmapOptions {
layout: 'centered' | 'top' // 父节点垂直定位,默认 centered
compacted: boolean // 子树上浮填空隙,默认 false
spacing: number // 碰撞边距(列),默认 1
maxWidth: number | null // 软换行目标宽度(显示列),null 不换行
wrapLeaves: boolean | number // 叶节点换行倍数,默认 3(false = 叶不换行)
paintDepth: number | null // 从该深度起子树着色,null 关闭
rootDelimiters: [string, string] // 根定界符,默认 ['«', '»']
}折叠持久化提示:未提供
id的节点按对象身份自动分配 id;宿主每次 重建数据对象(如 computed 里parseOutline(...))会使折叠状态重置。 需要稳定折叠请在数据里自带id。
CSS 变量
在组件根(viewport)上覆盖:--mm-font(等宽字体栈)、--mm-font-size、
--mm-line-height(行高倍数,决定行网格密度)、--mm-accent、
--mm-fg / --mm-bg。
<DomMindmap style="--mm-accent: #d02970; --mm-font-size: 16px" ... />内联 style 或任何能命中组件根元素的选择器均可;写在更外层祖先上的
--mm-accent 等会被组件根的默认声明遮蔽。
本仓库开发
vp install # 按 package.json 安装(core 为正式 semver 依赖)
vp dev # demo(本仓库即组件的 demo / 开发场)
vp test # 组件层单测(核心引擎的 57 个用例在 mindmap-core 仓库)
vp check # fmt + lint + 类型检查
npm run build # 库构建:dist/index.js(ESM) + index.cjs + mindmap-vue.css + d.ts联调本地核心(修改 ../mindmap-core 后想立即在 demo 生效):
npm install --no-save file:../mindmap-core # 临时替换为本地链接,不写回 package.json发布前记得恢复正式依赖(rm -rf node_modules/@lxc2006 && vp install),
并且不要把 file: 路径写进 package.json 的 dependencies 再 publish——
npm 会原样发布出去,宿主装不到(0.1.0 版本就吃过这个亏)。
发布(本机默认源是镜像,需先回到官方源):
unset NPM_CONFIG_REGISTRY
npm publish --access public@panzoom/panzoom已内联打包(其 npm 包 main 指向 UMD,Node 原生 ESM 无法加载;内联后宿主零配置且产物仍然只有 ~10KB)- 类型声明由
vue-tsc -p tsconfig.lib.json生成(vite-plugin-dts 与 typescript-native-bridge 不兼容)
