Appearance
BizRelationGraph 关系图谱
零依赖力导向关系图谱(SVG 自绘,无需引入三方图库):nodes / edges 声明即渲染,库仑斥力 + 弹簧引力 + 向心力自动布局;节点拖拽即时重排、hover 高亮相邻、连线可点击、collapsible 双击收起下游网络。股权穿透、担保链、知识图谱、组织关系网等节点关系可视化。
基础用法
暂无节点数据
股权穿透场景:股东 / 机构 / 子孙公司分层自动布局,
show-edge-label 展示持股关系;拖动节点重排,hover 高亮相邻。<script setup lang="ts">
/** 基础用法:股权穿透——股东 / 法人 / 关联企业节点自布局,拖拽重排、hover 高亮相邻 */
import { ElMessage } from 'element-plus'
import { BizRelationGraph } from 'vue3-ep-biz-lib'
import type { BizGraphEdge, BizGraphNode } from 'vue3-ep-biz-lib'
const nodes: BizGraphNode[] = [
{ id: 'core', label: '星辰科技', group: '核心企业', size: 22 },
{ id: 's1', label: '王建国', group: '股东', size: 14 },
{ id: 's2', label: '晨曦资本', group: '机构', size: 18 },
{ id: 's3', label: '李梅', group: '股东', size: 14 },
{ id: 'sub1', label: '星辰智造', group: '子公司', size: 16 },
{ id: 'sub2', label: '星辰云服务', group: '子公司', size: 16 },
{ id: 'sub3', label: '星链供应链', group: '孙公司', size: 13 },
{ id: 'legal', label: '赵敏(法人)', group: '法人', size: 14 },
]
const edges: BizGraphEdge[] = [
{ source: 's1', target: 'core', label: '持股 45%' },
{ source: 's2', target: 'core', label: '持股 30%' },
{ source: 's3', target: 'core', label: '持股 15%' },
{ source: 'legal', target: 'core', label: '任职' },
{ source: 'core', target: 'sub1', label: '全资' },
{ source: 'core', target: 'sub2', label: '持股 80%' },
{ source: 'sub1', target: 'sub3', label: '全资' },
]
const onNodeClick = (node: BizGraphNode): void => {
ElMessage.info(`节点:${node.label}(${node.group ?? '未分组'})`)
}
</script>
<template>
<BizRelationGraph
:nodes="nodes"
:edges="edges"
:height="420"
show-edge-label
@node-click="onNodeClick"
/>
</template>
显示代码
折叠与自定义配色
暂无节点数据
双击「华东大区」试试收起下游网络(中心带白点的为已折叠节点)
collapsible 开启后双击节点收起 / 展开下游;group-colors 按分组配色(缺省 EP 五色轮转);distance 调节疏密。<script setup lang="ts">
/** 折叠与配色:双击节点收起 / 展开下游;group-colors 自定义分组配色 */
import { ref } from 'vue'
import { BizRelationGraph } from 'vue3-ep-biz-lib'
import type { BizGraphEdge, BizGraphNode } from 'vue3-ep-biz-lib'
const nodes: BizGraphNode[] = [
{ id: 'root', label: '总部', size: 22 },
{ id: 'a', label: '华东大区', size: 17 },
{ id: 'b', label: '华南大区', size: 17 },
{ id: 'a1', label: '上海仓' },
{ id: 'a2', label: '杭州仓' },
{ id: 'a11', label: '浦东站' },
{ id: 'a12', label: '虹桥站' },
{ id: 'b1', label: '深圳仓' },
{ id: 'b2', label: '广州仓' },
]
const edges: BizGraphEdge[] = [
{ source: 'root', target: 'a' },
{ source: 'root', target: 'b' },
{ source: 'a', target: 'a1' },
{ source: 'a', target: 'a2' },
{ source: 'a1', target: 'a11' },
{ source: 'a1', target: 'a12' },
{ source: 'b', target: 'b1' },
{ source: 'b', target: 'b2' },
]
/** 折叠中的节点 id 集合(仅用于提示统计) */
const folded = ref<string[]>([])
const onFold = (node: BizGraphNode): void => {
const idx = folded.value.indexOf(node.id)
if (idx >= 0) folded.value.splice(idx, 1)
else folded.value.push(node.id)
}
const groupColors = {
'': 'var(--el-color-primary)',
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 8px">
<BizRelationGraph
:nodes="nodes"
:edges="edges"
:height="400"
:group-colors="groupColors"
collapsible
:distance="110"
@node-dblclick="onFold"
/>
<div style="font-size: 12px; color: var(--el-text-color-secondary)">
双击「华东大区」试试收起下游网络(中心带白点的为已折叠节点){{ folded.length > 0 ? `,当前已折叠 ${folded.length} 处` : '' }}
</div>
</div>
</template>
显示代码
大节点量与疏密
暂无节点数据
33 个节点 / 32 条连线——节点多时调小 distance 防止溢出画布
30+ 节点自动布局无压力,
distance 调小防拥挤溢出画布。<script setup lang="ts">
/** 大节点量:30+ 节点自动布局无压力,distance 调节疏密防拥挤 */
import { BizRelationGraph } from 'vue3-ep-biz-lib'
import type { BizGraphEdge, BizGraphNode } from 'vue3-ep-biz-lib'
/** 确定性生成:1 个核心 + 4 个分区 + 每分区 7 个终端(共 33 节点) */
const groups = ['华东', '华北', '华南', '西南'] as const
const nodes: BizGraphNode[] = [
{ id: 'center', label: '物联中枢', group: '核心', size: 24 },
...groups.map((g) => ({ id: `hub-${g}`, label: `${g}网关`, group: '网关', size: 16 })),
...groups.flatMap((g) =>
Array.from({ length: 7 }, (_, i) => ({
id: `dev-${g}-${i}`,
label: `${g}-${String(i + 1).padStart(2, '0')} 号终端`,
group: '终端',
size: 10,
})),
),
]
const edges: BizGraphEdge[] = [
...groups.map((g) => ({ source: 'center', target: `hub-${g}` })),
...groups.flatMap((g) =>
Array.from({ length: 7 }, (_, i) => ({ source: `hub-${g}`, target: `dev-${g}-${i}` })),
),
]
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 8px">
<BizRelationGraph :nodes="nodes" :edges="edges" :height="420" :distance="95" :group-colors="{ 核心: 'var(--el-color-danger)', 网关: 'var(--el-color-primary)', 终端: 'var(--el-color-info)' }" />
<div style="font-size: 12px; color: var(--el-text-color-secondary)">
{{ nodes.length }} 个节点 / {{ edges.length }} 条连线——节点多时调小 distance 防止溢出画布
</div>
</div>
</template>
显示代码
事件联动
暂无节点数据
点击节点 / 连线查看事件(连线点击热区为线身)
node-click / node-dblclick / edge-click 接入日志,点击连线查看关系详情。<script setup lang="ts">
/** 事件联动:node-click / node-dblclick / edge-click 接入日志,点击连线查看担保关系详情 */
import { ref } from 'vue'
import { ElTag } from 'element-plus'
import { BizRelationGraph } from 'vue3-ep-biz-lib'
import type { BizGraphEdge, BizGraphNode } from 'vue3-ep-biz-lib'
interface LogEntry { id: number; kind: 'node' | 'dbl' | 'edge'; text: string }
const nodes: BizGraphNode[] = [
{ id: 'a', label: '恒信贸易', group: '借款企业', size: 20 },
{ id: 'b', label: '隆晟投资', group: '担保方', size: 16 },
{ id: 'c', label: '华远制造', group: '担保方', size: 16 },
{ id: 'd', label: '城东支行', group: '放款银行', size: 18 },
]
const edges: BizGraphEdge[] = [
{ source: 'b', target: 'a', label: '连带担保' },
{ source: 'c', target: 'a', label: '一般担保' },
{ source: 'd', target: 'a', label: '放款 800 万' },
]
const logs = ref<LogEntry[]>([])
let seq = 0
const pushLog = (kind: LogEntry['kind'], text: string): void => {
seq += 1
logs.value = [{ id: seq, kind, text }, ...logs.value].slice(0, 5)
}
const onNodeClick = (node: BizGraphNode): void => {
pushLog('node', `点击节点「${node.label}」(${node.group ?? '-'})`)
}
const onNodeDblclick = (node: BizGraphNode): void => {
pushLog('dbl', `双击节点「${node.label}」`)
}
const onEdgeClick = (edge: BizGraphEdge): void => {
pushLog('edge', `点击连线 ${edge.source} → ${edge.target}:${edge.label ?? '关联'}`)
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 8px">
<BizRelationGraph
:nodes="nodes"
:edges="edges"
:height="360"
show-edge-label
@node-click="onNodeClick"
@node-dblclick="onNodeDblclick"
@edge-click="onEdgeClick"
/>
<div style="min-height: 26px; border: 1px solid var(--el-border-color-lighter); border-radius: 4px; padding: 4px 8px; font-size: 12px">
<template v-if="logs.length > 0">
<div v-for="log in logs" :key="log.id" style="display: flex; gap: 6px; align-items: center; line-height: 22px">
<ElTag size="small" :type="log.kind === 'node' ? 'primary' : log.kind === 'edge' ? 'warning' : 'info'">{{ log.kind }}</ElTag>
<span style="color: var(--el-text-color-regular)">{{ log.text }}</span>
</div>
</template>
<span v-else style="color: var(--el-text-color-secondary)">点击节点 / 连线查看事件(连线点击热区为线身)</span>
</div>
</div>
</template>
显示代码
静态展示
暂无节点数据
draggable=false 禁用拖拽(报告截图 / 只读汇报大屏)。<script setup lang="ts">
/** 静态展示:draggable=false 禁用拖拽——报告截图 / 汇报大屏等只读场景 */
import { BizRelationGraph } from 'vue3-ep-biz-lib'
import type { BizGraphEdge, BizGraphNode } from 'vue3-ep-biz-lib'
const nodes: BizGraphNode[] = [
{ id: 'std', label: '集团标准', group: '标准', size: 20 },
{ id: 'm1', label: '制造标准', group: '执行', size: 15 },
{ id: 'm2', label: '服务标准', group: '执行', size: 15 },
{ id: 'd1', label: '车间细则' },
{ id: 'd2', label: '门店细则' },
]
const edges: BizGraphEdge[] = [
{ source: 'std', target: 'm1', label: '派生' },
{ source: 'std', target: 'm2', label: '派生' },
{ source: 'm1', target: 'd1' },
{ source: 'm2', target: 'd2' },
]
</script>
<template>
<BizRelationGraph
:nodes="nodes"
:edges="edges"
:height="340"
:draggable="false"
:distance="120"
/>
</template>
显示代码
编程式重排
暂无节点数据
节点以随机初速打撒后重新力导向收敛
relayout() 打撒节点重新力导向收敛(切换数据集 / 布局不满意时)。<script setup lang="ts">
/** 编程式重排:relayout() 打撒节点重新收敛——切换数据集或布局不满意时使用 */
import { ref } from 'vue'
import { ElButton } from 'element-plus'
import { BizRelationGraph } from 'vue3-ep-biz-lib'
import type { BizGraphEdge, BizGraphNode } from 'vue3-ep-biz-lib'
const graphRef = ref<{ relayout: () => void }>()
const nodes: BizGraphNode[] = [
{ id: 'n1', label: '风险主体', group: '主体', size: 20 },
{ id: 'n2', label: '实际控制人', group: '自然人', size: 14 },
{ id: 'n3', label: '关联企业 A', group: '关联', size: 15 },
{ id: 'n4', label: '关联企业 B', group: '关联', size: 15 },
{ id: 'n5', label: '对外投资 C', group: '关联', size: 13 },
{ id: 'n6', label: '历史股东', group: '自然人', size: 13 },
]
const edges: BizGraphEdge[] = [
{ source: 'n2', target: 'n1', label: '控制' },
{ source: 'n3', target: 'n1' },
{ source: 'n4', target: 'n1' },
{ source: 'n1', target: 'n5', label: '投资' },
{ source: 'n6', target: 'n3' },
]
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 8px">
<BizRelationGraph ref="graphRef" :nodes="nodes" :edges="edges" :height="360" show-edge-label />
<div>
<ElButton size="small" type="primary" plain @click="graphRef?.relayout()">relayout() 重新布局</ElButton>
<span style="margin-left: 12px; font-size: 12px; color: var(--el-text-color-secondary)">
节点以随机初速打撒后重新力导向收敛
</span>
</div>
</div>
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| nodes | BizGraphNode[] | —(必填) | 节点(id / label / group / size) |
| edges | BizGraphEdge[] | —(必填) | 连线(source / target / label) |
| height | number | 460 | 画布高度 px |
| draggable | boolean | true | 节点可拖拽 |
| collapsible | boolean | false | 双击节点收起 / 展开下游 |
| show-edge-label | boolean | false | 展示连线关系文案 |
| group-colors | Record<string, string> | EP 五色轮转 | 分组配色 |
| distance | number | 130 | 弹簧目标距离 px(调疏密) |
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| node-click | (node) | 点击节点 |
| node-dblclick | (node) | 双击节点(开启 collapsible 时兼做折叠开关) |
| edge-click | (edge) | 点击连线 |
Expose
| 属性/方法 | 说明 |
|---|---|
| relayout() | 重新布局(打撒重排) |