npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

irr-gh

v1.2.0

Published

灌溉系统概化图组件

Readme

irr-gh - 灌溉系统概化图组件

基于 Vue 3 + AntV G6 v5 的灌溉系统概化图可视化组件库。

目录


安装

npm install irr-gh
# 或
pnpm add irr-gh
# 或
bun add irr-gh

快速开始

组件导入方式

<script setup lang="ts">
import { ref } from 'vue'
import { IrrigationGraph } from 'irr-gh'
import 'irr-gh/style.css'
import type { IrrigationData } from 'irr-gh'

const graphRef = ref()

// 示例数据
const data: IrrigationData = {
  canals: [
    { id: 'main', name: '主干渠', type: 'canal', level: 1 },
    { id: 'branch-1', name: '支渠1', type: 'canal', parentId: 'main', bankSide: 'left' }
  ],
  points: [
    { id: 'sluice-1', name: '闸门1', type: 'sluice', canalId: 'main', chainage: 0 },
    { id: 'sluice-2', name: '闸门2', type: 'sluice', canalId: 'main', chainage: 100 }
  ]
}

function handleReady(graph: unknown) {
  console.log('图渲染完成', graph)
}
</script>

<template>
  <IrrigationGraph
    ref="graphRef"
    :data="data"
    :enable-minimap="true"
    :fit-view="true"
    :show-toolbar="true"
    @ready="handleReady"
  />
</template>

全局注册方式

您也可以在 main.js/main.ts 中全局注册组件:

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import IrrGh from 'irr-gh'
import 'irr-gh/style.css'

const app = createApp(App)

// 全局注册组件
app.use(IrrGh)

app.mount('#app')

之后就可以在任何组件中直接使用 <IrrigationGraph> 组件,无需单独导入:

<script setup lang="ts">
import { ref } from 'vue'
import type { IrrigationData } from 'irr-gh'

const graphRef = ref()

// 示例数据
const data: IrrigationData = {
  canals: [
    { id: 'main', name: '主干渠', type: 'canal', level: 1 },
    { id: 'branch-1', name: '支渠1', type: 'canal', parentId: 'main', bankSide: 'left' }
  ],
  points: [
    { id: 'sluice-1', name: '闸门1', type: 'sluice', canalId: 'main', chainage: 0 },
    { id: 'sluice-2', name: '闸门2', type: 'sluice', canalId: 'main', chainage: 100 }
  ]
}

function handleReady(graph: unknown) {
  console.log('图渲染完成', graph)
}
</script>

<template>
  <IrrigationGraph
    ref="graphRef"
    :data="data"
    :enable-minimap="true"
    :fit-view="true"
    :show-toolbar="true"
    @ready="handleReady"
  />
</template>

布局系统

布局方向

组件支持两种布局方向:

| 布局类型 | 值 | 说明 | |---------|------|------| | 横向布局 | 'horizontal' | 主干渠从左到右水平排列,支渠向上/下垂直分叉 | | 纵向布局 | 'vertical' | 主干渠从上到下垂直排列,支渠向左/右水平分叉 |

布局规则

1. 主干渠方向决定布局

  • 横向布局:主干渠从左到右水平绘制
  • 纵向布局:主干渠从上到下垂直绘制

2. 连接与分叉原则

  • 上下游延续 (bankSide: 'center'):子渠道继续沿父级渠道方向在同一轴线上延续连接。
  • 支渠分叉 (bankSide: 'left' | 'right'):支渠与父级渠道垂直相交。

横向布局时:

  • center → 继续水平延续
  • left (左岸) → 向上垂直分叉
  • right (右岸) → 向下垂直分叉

纵向布局时:

  • center → 继续垂直延续
  • left (左岸) → 向左水平分叉
  • right (右岸) → 向右水平分叉

3. 主干渠居中

主干渠自动居中到画布中心位置。

布局间距配置

interface LayoutSpacing {
  /** 主渠方向节点间距 */
  main: number        // 默认: 150
  /** 支渠方向节点间距 */
  branch: number      // 默认: 120
  /** 分支偏移距离 */
  branchOffset: number // 默认: 80
}

组件配置

Props 属性

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | config | IrrigationGraphConfig | - | (推荐)结构化配置对象,收敛全部能力;与扁平 props 共存时 config 覆盖 | | width | number | - | 画布宽度(不设置则自适应容器) | | height | number | - | 画布高度(不设置则自适应容器) | | data | IrrigationData | - | 业务数据(原始格式) | | graphData | IrrigationGraphData | - | G6图数据(已转换格式) | | layout | LayoutConfig | - | 布局配置 | | theme | ThemeConfig | - | 主题配置 | | tooltip | TooltipConfig | - | Tooltip配置 | | dataPanel | DataPanelConfig | - | 常显数据面板配置 | | enableDrag | boolean | true | 是否启用拖拽 | | enableZoom | boolean | true | 是否启用缩放 | | enableBrush | boolean | false | 是否启用框选 | | enableMinimap | boolean | false | 是否启用小地图 | | fitView | boolean | true | 是否适应画布 | | fitViewPadding | number \| number[] | - | 适应画布的内边距 | | readonly | boolean | false | 是否只读模式 | | showToolbar | boolean | false | 是否显示内置工具栏 | | enabledToolbarActions | ToolbarActions | 全部启用 | 启用的工具栏功能 | | evolution | EvolutionConfig | - | 演进功能配置 | | evolutionData | EvolutionData | - | 演进数据 |

推荐:扁平 props 保留用于向后兼容,新用法请直接使用 config 对象, 把散落的配置按域收敛(size / data / layout / theme / interaction / toolbar / tooltip / dataPanel / evolution)。

结构化 config(推荐写法)

<script setup>
import { ref } from 'vue'

const config = ref({
  data: { business: myBusiData },          // 业务格式;或 { graph: myGraphData } 传 G6 格式
  theme: { backgroundColor: '#fff' },
  interaction: {                           // 交互相关开关
    enableDrag: true,
    enableZoom: true,
    enableBrush: false,
    enableMinimap: false,
    fitView: true,
    readonly: false,
  },
  toolbar: {
    showToolbar: true,
    actions: {
      zoomIn: true,
      zoomOut: true,
      fitView: true,
      export: true,
      config: true,
      align: true,   // 对齐 / 分布(含均匀分布),默认 true
    },
  },
  dataPanel: { enabled: false },
  evolution: {
    config: { enabled: false, interval: 2000, loop: true, autoPlay: false },
    data: undefined,
  },
})
</script>

<template>
  <IrrigationGraph :config="config" @ready="onReady" />
</template>

IrrigationGraphConfig 顶层分类:

| 配置路径 | 说明 | 对应旧扁平 props | |---|---|---| | size.width / size.height | 画布尺寸 | width / height | | data.business / data.graph | 图数据(二选一,graph 优先) | data / graphData | | layout | 布局配置 | layout | | theme | 主题配置 | theme | | tooltip | Tooltip 配置 | tooltip | | dataPanel | 常显数据面板 | dataPanel | | interaction.enableDrag · enableZoom · enableBrush · enableMinimap · fixElementSize · fitView · autoResize · fitViewPadding · readonly | 交互开关 | 同名扁平 props | | toolbar.showToolbar · toolbar.actions | 工具栏显隐与动作 | showToolbar / enabledToolbarActions | | evolution.config / evolution.data | 演进配置与数据 | evolution / evolutionData |

ToolbarActions 配置

interface ToolbarActions {
  zoomIn?: boolean   // 放大按钮
  zoomOut?: boolean  // 缩小按钮
  fitView?: boolean  // 适应视图按钮
  save?: boolean     // 保存按钮
  export?: boolean   // 导出按钮
  print?: boolean    // 打印按钮
  layout?: boolean   // 布局切换按钮
  config?: boolean   // 配置面板按钮
  align?: boolean    // 对齐/分布功能(含各种对齐与均匀分布),默认 true
  getData?: ((data: IrrigationGraphData) => void) | boolean  // 获取数据回调
}

DataPanelConfig 配置

常显数据面板配置,仅在点位节点上显示,当启用时会自动禁用 Tooltip。

interface DataPanelConfig {
  enabled?: boolean              // 是否启用,默认 false
  formatter?: (data: IrrigationNodeData) => string  // 自定义内容格式化(返回 HTML 字符串)
  offsetX?: number               // 水平偏移,默认 10
  offsetY?: number               // 垂直偏移,默认 -10
}

使用示例:

<script setup>
import { ref } from 'vue'

const dataPanelConfig = ref({
  enabled: true,
  formatter: (nodeData) => {
    const label = nodeData.data?.label || nodeData.id
    const kind = nodeData.businessData?.kind || '未知类型'
    
    return `
      <div style="font-weight: bold; color: #1890ff;">${label}</div>
      <div style="color: #666; font-size: 11px;">类型: ${kind}</div>
      <div style="color: #999; font-size: 10px;">状态: 正常</div>
    `
  },
  offsetX: 0,
  offsetY: -15
})
</script>

<template>
  <IrrigationGraph :data="data" :data-panel="dataPanelConfig" />
</template>

Events 事件

| 事件名 | 参数 | 说明 | |--------|------|------| | ready | (graph: Graph) | 图渲染完成 | | itemClick | ({ type: string, item: unknown }) | 节点/边点击 | | itemDblClick | ({ type: string, item: unknown }) | 节点/边双击 | | itemContextMenu | ({ type: string, item: unknown, event: MouseEvent }) | 节点/边右键菜单 | | selectionChange | ({ nodes: string[], edges: string[] }) | 选中状态变化 | | canvasClick | (event: MouseEvent) | 画布点击 | | evolutionChange | (params: EvolutionEventParams) | 演进状态/时间点变化 | | dataChange | (data: IrrigationGraphData) | 数据变化 |

Slots 插槽

| 插槽名 | 参数 | 说明 | |--------|------|------| | toolbar | { zoomIn, zoomOut, fitView } | 自定义工具栏 | | loading | - | 自定义加载状态 | | default | - | 默认插槽,用于额外内容 |


API 方法

通过 ref 获取组件实例调用方法:

<script setup>
const graphRef = ref()

// 调用方法
graphRef.value?.zoomIn()
</script>

<template>
  <IrrigationGraph ref="graphRef" />
</template>

数据操作

| 方法 | 签名 | 说明 | |------|------|------| | render | (data: IrrigationGraphData) => Promise<void> | 渲染G6格式数据 | | renderFromData | (data: IrrigationData, direction?: LayoutDirection) => Promise<void> | 从业务数据渲染 | | updateData | (data: Partial<IrrigationGraphData>) => Promise<void> | 更新数据 | | addNode | (node: IrrigationNodeData) => Promise<void> | 添加节点 | | addEdge | (edge: IrrigationEdgeData) => Promise<void> | 添加边 | | removeNode | (nodeId: string) => Promise<void> | 删除节点 | | removeEdge | (edgeId: string) => Promise<void> | 删除边 | | updateNode | (nodeId: string, data: Partial<IrrigationNodeData>) => Promise<void> | 更新节点 | | updateEdge | (edgeId: string, data: Partial<IrrigationEdgeData>) => Promise<void> | 更新边 | | setEvolutionData | (data: EvolutionData) => void | 设置/更新演进数据 | | getEvolutionManager | () => EvolutionManager | 获取演进管理器实例 | | getData | () => IrrigationGraphData | 获取当前图数据 |

视图控制

| 方法 | 签名 | 说明 | |------|------|------| | zoomIn | () => void | 放大视图 | | zoomOut | () => void | 缩小视图 | | zoomTo | (ratio: number) => void | 缩放到指定比例 | | fitView | () => void | 适应视图 | | focusNode | (nodeId: string) => void | 聚焦到指定节点 |

布局操作

| 方法 | 签名 | 说明 | |------|------|------| | changeLayout | (type: 'horizontal' \| 'vertical') => Promise<void> | 切换布局方向 | | changeLayoutDirection | (data: IrrigationData, direction: LayoutDirection) => Promise<void> | 使用新布局方向重新渲染 | | setLayout | (layout: LayoutConfig) => Promise<void> | 设置G6布局配置 |

导出与保存

| 方法 | 签名 | 说明 | |------|------|------| | save | () => string | 保存为JSON字符串 | | load | (json: string) => Promise<void> | 从JSON加载 | | toImage | (config?: ExportConfig) => Promise<string> | 导出为图片(Base64) | | downloadImage | (config?: ExportConfig) => Promise<void> | 下载图片 | | print | () => void | 打印 |

ExportConfig 配置

interface ExportConfig {
  fileName?: string      // 文件名,默认: 'irrigation-graph'
  format?: 'png' | 'jpg' | 'svg' | 'pdf'  // 导出格式
  backgroundColor?: string  // 背景色
  scale?: number         // 缩放比例
}

事件监听

| 方法 | 签名 | 说明 | |------|------|------| | on | (type: InteractionEventType, handler: Function) => void | 监听事件 | | off | (type: InteractionEventType, handler?: Function) => void | 移除事件监听 |

可监听的事件类型

type InteractionEventType =
  | 'node:click' | 'node:dblclick' | 'node:contextmenu'
  | 'node:mouseenter' | 'node:mouseleave'
  | 'node:dragstart' | 'node:drag' | 'node:dragend'
  | 'edge:click' | 'edge:dblclick' | 'edge:contextmenu'
  | 'edge:mouseenter' | 'edge:mouseleave'
  | 'canvas:click' | 'canvas:dragstart' | 'canvas:drag' | 'canvas:dragend'
  | 'combo:click' | 'combo:dblclick'

数据结构

自 1.0 起,业务侧 I/O 采用标准化灌溉契约(domain/irrigation/schema 的 IrrigationInput), 通过 normalize(脏值清洗)→ adapter(IrrigationInput → TopoData)接入通用拓扑内核。 旧的 connections / nodeConfig / edgeConfig / SluiceEntity / entityType 接口已废弃。

标准化灌溉输入 (IrrigationInput)

interface IrrigationInput {
  elements: IrrigationElement[]  // 全部实体/辅助点(点/线/面统一,几何由 kind 推导)
  buffers: IrrigationCombo[]     // 空间容器(调度单元缓冲面)
  groups: IrrigationGroup[]      // 高亮围框(纯展示)
}

统一实体 (IrrigationElement)

实体类型 kind(线 / 点 / 面)、几何形态 geometry、空间容器(buffers/groups) 是三个正交维度:geometry 由 kind 推导默认值,可覆盖(同一种实体在不同比例尺 下可以是点图标或面)。

// 事实实体类型(去重收口)
type EntityKind =
  | 'river' | 'canal' | 'ditch'          // 线:河道 / 渠道 / 沟道
  | 'sluice' | 'pumpStation' | 'weir'    // 点:闸门 / 泵站 / 堰坝
  | 'reservoir' | 'pond'                 // 面:水库 / 水池(蓄水)
  | 'catchment' | 'contourBand' | 'fieldUnit' | 'subsurfaceUnit' // 面:集水区/等高带/田块单元/地下单元

type GeoType = 'point' | 'line' | 'area'   // 几何形态
type AuxKind = 'start' | 'end' | 'fork' | 'afflux' | 'stream'  // 辅助拓扑节点

interface IrrigationElement {
  id: string
  name: string
  discriminator: 'entity' | 'aux'        // 事实实体 / 辅助拓扑节点
  kind: EntityKind | AuxKind             // 实体类型(收口枚举)
  geometry: GeoType                      // 点/线/面(默认由 kind 推导)
  onChannel?: { channelId: string; chainage?: number; bankSide?: BankSide } // 依附渠道的定位
  standalone?: boolean                   // 独立设施(水池/水库等不依附渠道)
  // 线实体(kind: river/canal/ditch)特有渠系字段
  base?: boolean
  parentId?: string
  forkChainage?: number
  startChainage?: number
  endChainage?: number
  level?: number
  targetId?: string
  targetChainage?: number
  targetBankSide?: 'left' | 'right' | 'center'
  targetFacilityId?: string
  facilityEnd?: 'start' | 'end'
  drawType?: 'virtual' | 'actual'
  ctrlTargetId?: string
  style?: Record<string, unknown>        // 样式(shape/img/label*,与 g6 对齐)
  [key: string]: unknown                 // 其余扩展
}

样式示例(用 style 替代旧 nodeConfig;shape 支持 rect/circle/image/diamond):

const pointWithImage: IrrigationElement = {
  id: 'slu-1', name: '进水闸1', discriminator: 'entity', kind: 'sluice', geometry: 'point',
  onChannel: { channelId: 'canal-A', chainage: 0 }, standalone: false,
  style: { shape: 'image', img: '/head.png', width: 48, height: 48 }
}

const pumpRect: IrrigationElement = {
  id: 'pump-1', name: '提水泵站1', discriminator: 'entity', kind: 'pumpStation', geometry: 'point',
  onChannel: { channelId: 'canal-B', chainage: 100 }, standalone: false,
  style: { shape: 'rect', width: 60, height: 40, fill: '#ffccc7', stroke: '#ff4d4f', radius: 8 }
}

规范化与适配(canonical 为源头,引擎直接消费)

// 1. 规范输入(canonical = IrrigationInput)直接交给组件 / hook / adapter:
//    IrrigationGraph 的 data / useIrrigationGraph 的 data / toTopoDataFromIrrigation
//    均可传 canonical IrrigationInput 或 legacy IrrigationData(自适应)。
// 2. 引擎(routine transformDataWithLayout)直接读取 canonical 字段
//    (kind / onChannel / standalone / geometry);legacy 由入口 normalizeIrrigation 归并。
const canon = normalizeIrrigation(rawIrrigationData)   // dirty → canonical
const topo = toTopoDataWithLayout(canon, "horizontal")  // canonical → TopoData
  • canalId === "undefined"(历史脏值)会被清洗为 standalone: true,依附渠道定位统一收进 onChannel;
  • 源头契约优先用 canonical IrrigationInput(elements/buffers/groups);legacy 数据仍被容忍,入口自动归一。
  • 引擎与 g6 运行时统一经 @/core/topo/canonical 访问器读取 canonical 字段(kind/onChannel/standalone/geometry), 不携带任何 legacy 影子。

通用拓扑数据 (TopoData)

interface TopoData {
  nodes: TopoNode[]      // kind: entity/aux/buffer; shape/风格经归一
  edges: TopoEdge[]      // source/target + connectionType
  combos?: TopoCombo[]   // memberIds
  groups?: TopoGroup[]   // 高亮围框
}

内核只消费 TopoData,对业务不可知;替换行业只需新增 adapter + applier + presets,内核零改动。


边样式自定义

边由转换器根据渠道/点位关系自动生成,可用以下层级覆盖样式(优先级从高到低):

  1. 演进级:EvolutionManager/TimelineApplier 按报警级别、流向叠加报警色/渐变(见「数据演进」)。
  2. 渠道级 (Canal):在 IrrigationElement.style 配置该渠(kind ∈ river/canal/ditch)下所有自动生成边的默认样式。
  3. 全局级 (Theme):通过 theme 配置默认样式。

可配置样式属性(与 G6 对齐)

interface EdgeStyle {
  stroke?: string        // 线条颜色,如 '#ff4d4f'
  lineWidth?: number     // 线条宽度
  lineDash?: number[]    // 虚线配置,如 [5, 5]
  opacity?: number       // 透明度
  endArrow?: boolean     // 是否显示结束箭头
  labelText?: string     // 自定义标签内容
  labelFontSize?: number // 标签字号
  labelFill?: string     // 标签颜色
  router?: object        // 正交路由配置(分叉连接线)
}

使用示例

// 在渠道上配置默认红色加粗样式
const canal: IrrigationElement = {
  id: 'main-canal',
  name: '输沙渠',
  kind: 'canal',
  geometry: 'line',
  style: { stroke: '#ff4d4f', lineWidth: 6 },
}

工具栏

内置工具栏

通过 showToolbar 属性启用内置工具栏:

<IrrigationGraph
  :show-toolbar="true"
  :enabled-toolbar-actions="{
    zoomIn: true,
    zoomOut: true,
    fitView: true,
    save: true,
    export: true,
    print: true,
    layout: true
  }"
/>

自定义工具栏

使用 toolbar 插槽自定义工具栏:

<IrrigationGraph ref="graphRef">
  <template #toolbar="{ zoomIn, zoomOut, fitView }">
    <div class="my-toolbar">
      <button @click="zoomIn">放大</button>
      <button @click="zoomOut">缩小</button>
      <button @click="fitView">适应</button>
      <button @click="graphRef?.changeLayout('horizontal')">横向</button>
      <button @click="graphRef?.changeLayout('vertical')">纵向</button>
    </div>
  </template>
</IrrigationGraph>

主题配置

默认样式常量

// 渠道/边的默认宽度
export const CANAL_WIDTH = 10

// 渠道默认颜色
export const CANAL_COLOR = '#1890ff'

辅助节点样式

辅助节点(分叉点、结束点、汇合点)样式与渠道保持一致:

  • 形状:正方形(rect)
  • 尺寸:10 × 10 像素(与渠道宽度一致)
  • 颜色:与渠道颜色一致(#1890ff)
  • 无边框、无圆角

节点形状类型

支持以下节点形状:

| 形状 | 值 | 说明 | |------|------|------| | 矩形 | 'rect' | 默认形状,可设置圆角 radius | | 圆形 | 'circle' | 圆形节点 | | 菱形 | 'diamond' | 菱形节点 | | 椭圆 | 'ellipse' | 椭圆节点 | | 图片 | 'image' | 使用图片作为节点,需配置 img 属性 | | 自定义 | 'custom' | 自定义节点形状 |

默认节点样式映射

| 实体类型 | 默认形状 | 填充色 | 边框色 | |----------|----------|--------|--------| | sluice (闸门) | rect | #fffbe6 | #faad14 | | pumpStation (泵站) | rect | #f6ffed | #52c41a | | reservoir (水库) | circle | #e6fffb | #13c2c2 | | dispatchUnit (调度单元) | diamond | #f9f0ff | #722ed1 | | dispatchNode (调度节点) | circle | #fff0f6 | #eb2f96 | | auxiliary (辅助节点) | rect | #1890ff | #1890ff |

图片类型节点

点位节点支持使用图片,通过 IrrigationElement.style 配置 shape: 'image' 与 img:

const gateWithImage: IrrigationElement = {
  id: 'gate-1',
  name: '进水闸',
  discriminator: 'entity',
  kind: 'sluice',
  geometry: 'point',
  onChannel: { channelId: 'canal-1', chainage: 0 },
  standalone: false,
  style: { shape: 'image', img: '/head.png', width: 48, height: 48 },
}

const pumpWithImage: IrrigationElement = {
  id: 'pump-1',
  name: '泵站',
  discriminator: 'entity',
  kind: 'pumpStation',
  geometry: 'point',
  onChannel: { channelId: 'canal-1', chainage: 100 },
  standalone: false,
  style: { shape: 'image', img: '/pump-station.png', width: 48, height: 48 },
}

// 普通矩形节点
const gateWithRect: IrrigationElement = {
  id: 'gate-2',
  name: '普通闸门',
  discriminator: 'entity',
  kind: 'sluice',
  geometry: 'point',
  onChannel: { channelId: 'canal-1', chainage: 200 },
  standalone: false,
  style: {
    shape: 'rect',
    width: 48,
    height: 36,
    fill: '#fffbe6',
    stroke: '#faad14',
  },
}

// 转换后的 G6 节点:nodeShape 由 style.shape 得到,img 由 style.img 得到
const nodeData: IrrigationNodeData = {
  id: 'gate-1',
  nodeShape: 'image',
  style: { img: '/head.png', width: 48, height: 48, stroke: '#faad14' },
  data: { label: '进水闸' },
  businessData: gateWithImage, // 保留原始业务数据
}

边/渠道样式

默认渠道样式:

  • 宽度:10 像素
  • 颜色:#1890ff
  • 无箭头:默认不显示箭头

连接类型样式:

| 连接类型 | 颜色 | 宽度 | 样式 | |----------|------|------|------| | flow (水流) | #1890ff | 10px | 实线 | | control (控制) | #faad14 | 3px | 虚线 [8, 4] | | supply (供水) | #52c41a | 3px | 虚线 [6, 4] |

主题配置接口

interface ThemeConfig {
  name?: 'light' | 'dark' | 'custom'
  nodeStyle?: Partial<Record<EntityType, NodeStyleConfig>>
  edgeStyle?: EdgeStyleConfig
  backgroundColor?: string
}

interface NodeStyleConfig {
  fill?: string          // 填充色
  stroke?: string        // 边框色
  lineWidth?: number     // 边框宽度
  opacity?: number       // 透明度
  radius?: number        // 圆角半径
  width?: number         // 宽度
  height?: number        // 高度
  shadowColor?: string   // 阴影颜色
  shadowBlur?: number    // 阴影模糊度
}

interface EdgeStyleConfig {
  stroke?: string        // 线条颜色
  lineWidth?: number     // 线条宽度
  lineDash?: number[]    // 虚线配置
  opacity?: number       // 透明度
  endArrow?: boolean     // 结束箭头
  startArrow?: boolean   // 起始箭头
}

使用示例

<IrrigationGraph
  :theme="{
    name: 'custom',
    backgroundColor: '#f5f5f5',
    nodeStyle: {
      sluice: { fill: '#1890ff', stroke: '#0050b3' },
      pumpStation: { fill: '#52c41a', stroke: '#237804' },
      reservoir: { fill: '#722ed1', stroke: '#391085' }
    },
    edgeStyle: {
      stroke: '#333',
      lineWidth: 2
    }
  }"
/>

Hook 使用

可以直接使用 useIrrigationGraph Hook 获得更灵活的控制:

<script setup lang="ts">
import { ref } from 'vue'
import { useIrrigationGraph } from 'irr-gh'

const containerRef = ref<HTMLElement | null>(null)

const {
  graph,
  manager,
  state,
  initialized,
  loading,
  init,
  render,
  renderFromData,
  changeLayoutDirection,
  zoomIn,
  zoomOut,
  fitView,
  getData,
  save,
  load,
  toImage,
  downloadImage,
  print,
  on,
  off,
  destroy
} = useIrrigationGraph({
  container: containerRef,
  width: 1200,
  height: 800,
  data: myData,
  layoutDirection: 'horizontal',
  autoInit: true,
  options: {
    enableDrag: true,
    enableZoom: true,
    enableMinimap: true
  }
})
</script>

<template>
  <div ref="containerRef" style="width: 100%; height: 600px;"></div>
</template>

Hook 配置选项

interface UseIrrigationGraphOptions {
  container: Ref<HTMLElement | null>  // 容器元素引用
  width?: number                      // 画布宽度
  height?: number                     // 画布高度
  data?: IrrigationData               // 初始业务数据
  graphData?: IrrigationGraphData     // 初始G6数据
  options?: Partial<GraphOptions>     // 图配置选项
  autoInit?: boolean                  // 是否自动初始化(默认true)
  layoutDirection?: LayoutDirection   // 初始布局方向(默认horizontal)
}

布局辅助工具

组件暴露了 layoutUtils 对象,提供布局辅助函数:

// 通过组件实例访问
graphRef.value?.layoutUtils

// 可用方法
layoutUtils.getNodePosition(node)           // 获取节点位置
layoutUtils.setNodePosition(node, x, y)     // 设置节点位置
layoutUtils.alignNodesHorizontally(nodes, alignType)  // 水平对齐
layoutUtils.alignNodesVertically(nodes, alignType)    // 垂直对齐
layoutUtils.distributeNodesHorizontally(nodes, spacing) // 水平等距分布
layoutUtils.distributeNodesVertically(nodes, spacing)   // 垂直等距分布
layoutUtils.moveNodes(nodes, deltaX, deltaY)  // 批量移动节点
layoutUtils.getNodesBoundingBox(nodes)        // 获取节点边界框
layoutUtils.centerNodesToPosition(nodes, x, y) // 居中节点到位置
layoutUtils.centerNodesToCanvas(nodes, width, height) // 居中节点到画布
layoutUtils.flipNodes(nodes, axis)            // 翻转节点
layoutUtils.rotateNodes90(nodes, clockwise)   // 旋转90度
layoutUtils.getNodesByCanalId(data, canalId)  // 按渠道ID获取节点
layoutUtils.getConnectedNodes(data, nodeId)   // 获取相邻节点
layoutUtils.autoAdjustSpacing(nodes, direction, spacing) // 自动调整间距

数据演进

组件内置了数据演进功能,支持展示随时间变化的节点状态(如水位报警)和边状态(如流向、流量渐变)。

核心特性

  • 时序播放:支持自动/手动播放时间序列数据。
  • 报警级别映射:根据 alarmLevel 自动更新节点填充色、边框色和呼吸灯效果(阴影)。
  • 边渐变色流向:支持根据流向(正向/反向)自动计算边渐变方向,并根据状态实时更新。
  • 自定义数据面板:演进过程中的业务数据可在常显面板中实时展示。

演进配置 (EvolutionConfig)

interface EvolutionConfig {
  enabled?: boolean           // 是否启用
  interval?: number          // 播放速度(毫秒/帧),默认 2000
  loop?: boolean              // 是否循环播放
  autoPlay?: boolean          // 是否自动播放
  alarmStyles?: Partial<AlarmStyleMap> // 覆盖默认报警样式
  defaultGradient?: EdgeGradientConfig // 边默认渐变配置
}

数据结构

interface EvolutionData {
  timePoints: EvolutionTimePoint[]
}

interface EvolutionTimePoint {
  timestamp: number | string  // 时间戳或 ISO 字符串
  label?: string              // 时间显示标签,如 "08:00"
  nodeStates?: Record<string, NodeEvolutionState> // 节点状态映射
  edgeStates?: Record<string, EdgeEvolutionState> // 边状态映射
}

interface NodeEvolutionState {
  alarmLevel?: 'normal' | 'warning' | 'error' | 'critical'
  data?: Record<string, unknown> // 实时业务数据
  style?: NodeEvolutionStyle     // 自定义样式覆盖
}

interface EdgeEvolutionState {
  flowDirection?: 'forward' | 'backward' // 流向
  gradient?: { startColor: string; endColor: string } // 渐变配置
}

使用示例

<script setup>
import { ref } from 'vue'
import { createEvolutionMockData } from './mock'

const evolutionConfig = {
  enabled: true,
  interval: 1500,
  loop: true
}

const evolutionData = createEvolutionMockData()
</script>

<template>
  <IrrigationGraph 
    :data="baseData" 
    :evolution="evolutionConfig"
    :evolution-data="evolutionData"
  />
</template>

完整示例

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { IrrigationGraph } from 'irr-gh'
import 'irr-gh/style.css'
import type { IrrigationData } from 'irr-gh'

const graphRef = ref()

// 完整示例数据
const demoData: IrrigationData = {
  canals: [
    { id: 'main', name: '总干渠', type: 'canal', level: 1 },
    { id: 'branch-A', name: 'A支渠', type: 'canal', parentId: 'main', bankSide: 'left', level: 2 },
    { id: 'branch-B', name: 'B支渠', type: 'canal', parentId: 'main', bankSide: 'right', level: 2 }
  ],
  points: [
    // 总干渠上的点位
    { id: 'intake', name: '进水闸', type: 'sluice', canalId: 'main', chainage: 0 },
    { id: 'fork-A', name: 'A分叉', type: 'auxiliary', auxiliaryType: 'fork', canalId: 'main', chainage: 100, branchId: 'branch-A' },
    { id: 'pump-1', name: '1号泵站', type: 'pumpStation', canalId: 'main', chainage: 200 },
    { id: 'fork-B', name: 'B分叉', type: 'auxiliary', auxiliaryType: 'fork', canalId: 'main', chainage: 300, branchId: 'branch-B' },
    { id: 'outlet', name: '出水闸', type: 'sluice', canalId: 'main', chainage: 400 },
    
    // A支渠上的点位
    { id: 'sluice-A1', name: 'A1闸门', type: 'sluice', canalId: 'branch-A', chainage: 0 },
    { id: 'reservoir-A', name: 'A水库', type: 'reservoir', canalId: 'branch-A', chainage: 50 },
    
    // B支渠上的点位
    { id: 'sluice-B1', name: 'B1闸门', type: 'sluice', canalId: 'branch-B', chainage: 0 },
    { id: 'dispatch-B', name: 'B调度单元', type: 'dispatchUnit', canalId: 'branch-B', chainage: 50 }
  ]
}

// 事件处理
function handleReady(graph: unknown) {
  console.log('图渲染完成')
}

function handleItemClick({ type, item }: { type: string; item: unknown }) {
  console.log(`点击了${type}:`, item)
}

function handleCanvasClick(event: MouseEvent) {
  console.log('画布点击', event)
}

// API 操作示例
function switchToHorizontal() {
  graphRef.value?.changeLayout('horizontal')
}

function switchToVertical() {
  graphRef.value?.changeLayout('vertical')
}

function exportImage() {
  graphRef.value?.downloadImage({ fileName: 'irrigation-diagram' })
}
</script>

<template>
  <div class="app">
    <IrrigationGraph
      ref="graphRef"
      :data="demoData"
      :enable-minimap="true"
      :fit-view="true"
      :show-toolbar="true"
      :enabled-toolbar-actions="{
        zoomIn: true,
        zoomOut: true,
        fitView: true,
        save: true,
        export: true,
        print: true,
        layout: true
      }"
      @ready="handleReady"
      @item-click="handleItemClick"
      @canvas-click="handleCanvasClick"
    />
  </div>
</template>

<style>
.app {
  width: 100vw;
  height: 100vh;
}
</style>

类型导出

// 通用拓扑契约(topograph/core 入口)
import type {
  TopoData, TopoNode, TopoEdge, TopoCombo, TopoGroup,
  LayoutEngine, LayoutStrategy,
} from 'irr-gh/core'

// 灌溉适配层(topograph/irrigation 入口)
import {
  normalizeIrrigation,
  toTopoData, toTopoDataFromIrrigation,
  type IrrigationInput, IrrigationElement, IrrigationCombo, IrrigationGroup,
} from 'irr-gh/irrigation'

// 根入口(组件 + 图管理 + Hook)
import {
  IrrigationGraph, EvolutionTimeline,
  IrrigationGraphManager, createGraphManager,
  useIrrigationGraph, useConfigPanel,
  // 图数据类型
  type IrrigationGraphData, IrrigationNodeData, IrrigationEdgeData,
  // 演进数据类型
  type EvolutionData, EvolutionConfig, EvolutionTimePoint, EvolutionEventParams,
  // 配置与 Props
  type IrrigationGraphProps, GraphOptions, LayoutConfig, ThemeConfig, DataPanelConfig,
  // 布局与交互
  type LayoutDirection, LayoutSpacing, InteractionEventType, InteractionEventHandler,
} from 'irr-gh'

License

MIT