@lxc2006/mindmap-vue

字符网格思维导图 Vue 3 组件 —— @lxc2006/mindmap-core 的官方封装(平移缩放 / 节点拖拽 / 折叠选中)
782
Install
npm install @lxc2006/mindmap-vue
Latest Version:0.2.1
Vue:3
License:MIT
Last Updated:Oct 7, 2026
Links: npm
Maintainer: lxc2006

@lxc2006/mindmap-vue

npm version npm downloads License: MIT

字符网格风格的 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 不兼容)

Related Packages

vue3-mindmap

Mindmap component for Vue3

7,028 450
@billtaofbj/mindmap

Mind map editor component

3,274
@hellowuxin/mindmap

Mindmap Vue Component

3,128 462
proc-mindmap

Mindmap Vue Component

344 462
dragonfly-dag

基于Vue3的DAG流程图组件。

3,120 84

Version History

Version Released Vue License
0.2.1 ^3.5.0 MIT
0.2.0 ^3.5.0 MIT
0.1.2 ^3.5.0 MIT
0.1.1 ^3.5.0 MIT
0.1.0 ^3.5.0 MIT