add comment

This commit is contained in:
Timi
2026-09-11 11:19:28 +08:00
parent 4f1ac9ef0e
commit 726a9218e0
@@ -12,6 +12,8 @@ import type { Customer } from "@/types/Customer";
use([GraphChart, TooltipComponent, CanvasRenderer]);
// ---------- 图表数据类型 ----------
type RelationNode = {
id: string;
name: string;
@@ -107,20 +109,45 @@ const props = defineProps<{
customers: Customer[];
}>();
// ---------- 图表状态 ----------
const chartElement = ref<HTMLElement>();
const chartOption = computed<EChartsOption>(() => buildChartOption(props.customers));
// ---------- 图表配置 ----------
// 节点使用圆角矩形路径,尺寸需要与边界计算中的 nodeSize 保持一致
const nodeSymbol = "path://M 6 0 H 50 A 6 6 0 0 1 56 6 V 18 A 6 6 0 0 1 50 24 H 6 A 6 6 0 0 1 0 18 V 6 A 6 6 0 0 1 6 0 Z";
// 节点尺寸,单位为 ECharts 图表坐标单位
const nodeSize: [number, number] = [56, 24];
// 节点圆角半径,用于计算连线与节点边界的交点
const nodeCornerRadius = 6;
// 箭头占用的节点边缘长度,用于避免连线覆盖目标节点
const relationArrowLength = 4;
// 箭头尖端沿连线方向的额外偏移量,用于修正 ECharts 箭头定位
const relationArrowTipOffset = 2;
// 节点与连线之间的额外间距,单位为 ECharts 图表坐标单位
const relationEdgeGap = 0;
// 节点标签的基础字号,缩放时会在 labelLayout 中按图表缩放比例调整
const nodeLabelFontSize = 10;
// 节点标签可用宽度,超出后由 ECharts 按 truncate 规则截断
const nodeLabelWidth = 48;
// 聚焦节点时的平移动画时长,单位为毫秒
const centerTransitionDuration = 300;
let chart: ECharts | undefined;
let centerAnimationFrame: number | undefined;
// ---------- 图表缩放与关系图内部模型 ----------
function getChartZoom(): number {
const seriesModel = getRelationSeriesModel();
const modelZoom = seriesModel?.get("zoom");
@@ -143,6 +170,8 @@ function getNodeGraphicScale(node: RelationGraphNode): number {
return typeof scale === "number" && 0 < scale ? scale : 1;
}
// ---------- 关系边界与箭头 ----------
function getRoundedRectBoundary(
directionX: number,
directionY: number,
@@ -276,7 +305,7 @@ function adjustRelationEdges(): void {
return;
}
// 保留 ECharts 写入的原始中心点,避免下一次布局继续累积缩短距离。
// 保留 ECharts 写入的原始中心点,避免下一次布局继续累积缩短距离
sourcePoint[0] = nextSourceX;
sourcePoint[1] = nextSourceY;
targetPoint[0] = nextTargetX;
@@ -290,6 +319,8 @@ function getRelationGraph(): RelationGraph | undefined {
return getRelationSeriesModel()?.getGraph();
}
// ---------- 节点交互 ----------
function focusCustomer(customer: Customer): void {
const id = customer.id;
if (!id || !chart) {
@@ -374,6 +405,8 @@ function clearFocus(): void {
chart.dispatchAction({ type: "unfocusNodeAdjacency", seriesIndex: 0 }, { silent: true });
}
// ---------- 图表数据 ----------
function buildChartOption(list: Customer[]): EChartsOption {
const customerMap = new Map<string, Customer>();
for (const customer of list) {
@@ -403,6 +436,7 @@ function buildChartOption(list: Customer[]): EChartsOption {
nodeList.push({
id,
name: getCustomerName(customer),
// 横向按邀请层级排列,纵向按同层节点均匀分布
x: depth * 220 + 120,
y: (index - levelOffset) * 96 + 1200,
draggable: true,
@@ -439,6 +473,7 @@ function buildChartOption(list: Customer[]): EChartsOption {
}
return {
// 节点和连线的初始动画时长,单位为毫秒
animationDuration: 400,
tooltip: {
formatter: params => {
@@ -462,19 +497,24 @@ function buildChartOption(list: Customer[]): EChartsOption {
{
type: "graph",
layout: "force",
// 允许拖拽节点和缩放画布,关系较多时仍可查看完整图谱
roam: true,
roamTrigger: "global",
draggable: true,
nodeScaleRatio: 0,
// 限制画布缩放范围,避免节点过小或超出可视区域
scaleLimit: {
min: .5,
max: 4
},
focusNodeAdjacency: true,
force: {
// 使用接口计算出的节点坐标作为初始布局,避免首次渲染随机跳动
initLayout: "none",
// repulsion 控制节点间排斥力,edgeLength 控制连线期望长度
repulsion: 1000,
edgeLength: 640,
// gravity 控制节点向中心聚拢的力度,layoutAnimation 保持布局变化平滑
gravity: .01,
layoutAnimation: true
},
@@ -508,6 +548,7 @@ function buildChartOption(list: Customer[]): EChartsOption {
}
},
stateAnimation: {
// 节点状态切换动画时长,单位为毫秒
duration: 300,
easing: "cubicOut"
}
@@ -516,6 +557,7 @@ function buildChartOption(list: Customer[]): EChartsOption {
};
}
// 根据邀请人链路计算节点层级,层级越深表示被邀请关系越下游
function getCustomerDepth(customer: Customer, customerMap: Map<string, Customer>, depthMap: Map<string, number>, visiting: Set<string>): number {
const id = customer.id;
if (!id) {
@@ -546,6 +588,7 @@ function getCustomerName(customer: Customer): string {
return customer.name || customer.code || customer.id || "未命名客户";
}
// 邀请人数越多,节点颜色越醒目;阈值需与关系页图例保持一致
function getNodeColor(customer: Customer): string {
const invitedCount = customer.invitedCount ?? 0;
if (10 <= invitedCount) {
@@ -563,6 +606,8 @@ function getNodeColor(customer: Customer): string {
return "#94a3b8";
}
// ---------- 图表渲染与生命周期 ----------
function renderChart(): void {
if (!chartElement.value || !props.customers.length) {
return;
@@ -624,9 +669,9 @@ defineExpose({
.chart {
width: 100%;
height: 100%;
cursor: grab;
touch-action: none;
overscroll-behavior: none;
cursor: grab;
&:active {
cursor: grabbing;