@agilehub/vue-knowledge-graph 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,517 @@
1
+
2
+ (function(){if(typeof document<"u"&&!document.getElementById("vue-knowledge-graph-styles")){var e=document.createElement("style");e.id="vue-knowledge-graph-styles",e.textContent=".graph-container[data-v-232ee924]{width:100%;height:100%}.kg-toolbar[data-v-f8202e9e]{z-index:100;background:#fff;border:1px solid #e2e8f0;border-radius:10px;align-items:center;gap:6px;padding:8px;font-family:Segoe UI,PingFang SC,system-ui,sans-serif;display:flex;position:absolute;top:16px;right:16px;box-shadow:0 2px 8px #0000000f}.kg-toolbar[data-v-f8202e9e]:hover{box-shadow:0 4px 12px #0000001a}.kg-toolbar.kg-collapsed[data-v-f8202e9e]{padding:6px;right:12px}.kg-btn-group[data-v-f8202e9e]{align-items:center;gap:8px;display:flex}.kg-btn[data-v-f8202e9e]{color:#334155;cursor:pointer;background:0 0;border:none;border-radius:6px;outline:none;align-items:center;gap:5px;padding:6px 12px;font-size:12px;font-weight:500;transition:all .2s;display:flex}.kg-btn[data-v-f8202e9e]:hover{color:#0f172a;background:#e2e8f0}.kg-btn[data-v-f8202e9e]:focus-visible,.kg-toggle[data-v-f8202e9e]:focus-visible{outline-offset:2px;outline:2px solid #60a5fa}.kg-icon[data-v-f8202e9e]{font-size:13px;line-height:1}.kg-layout-control[data-v-f8202e9e]{border-left:1px solid #e2e8f0;align-items:center;gap:7px;padding-left:10px;display:flex}.kg-layout-label[data-v-f8202e9e]{color:#64748b;white-space:nowrap;font-size:11px;font-weight:650}.kg-layout-select[data-v-f8202e9e]{color:#1e293b;background:#fff;border:1px solid #cbd5e1;border-radius:6px;outline:none;width:128px;min-height:31px;padding:5px 26px 5px 9px;font-size:12px}.kg-layout-select[data-v-f8202e9e]:hover,.kg-layout-select[data-v-f8202e9e]:focus{border-color:#60a5fa;box-shadow:0 0 0 2px #dbeafe}.kg-toggle[data-v-f8202e9e]{color:#64748b;background:0 0;border:none;border-radius:4px;padding:4px 8px;font-size:11px}.kg-toggle[data-v-f8202e9e]:hover{color:#0f172a;background:#e2e8f0}@media (width<=640px){.kg-toolbar[data-v-f8202e9e]{max-width:calc(100% - 24px)}.kg-btn-group[data-v-f8202e9e]{gap:4px}.kg-btn[data-v-f8202e9e]{padding:6px 8px}.kg-btn-label[data-v-f8202e9e]{display:none}.kg-layout-control[data-v-f8202e9e]{gap:4px;padding-left:6px}.kg-layout-label[data-v-f8202e9e]{display:none}.kg-layout-select[data-v-f8202e9e]{width:120px}}\n/*$vite$:1*/",document.head.appendChild(e)}})();
3
+ import { Fragment as e, computed as t, createElementBlock as n, createElementVNode as r, defineComponent as i, nextTick as a, normalizeClass as o, onMounted as s, onUnmounted as c, openBlock as l, ref as u, renderList as d, toDisplayString as f, vShow as p, watch as m, withDirectives as h } from "vue";
4
+ import { CanvasEvent as g, Graph as _, NodeEvent as v } from "@antv/g6";
5
+ //#region src/module/g6Graph.ts
6
+ var y = [
7
+ {
8
+ value: "force",
9
+ label: "力导向",
10
+ description: "通用网络布局,适合结构未知或关系密集的图谱",
11
+ category: "network"
12
+ },
13
+ {
14
+ value: "circular",
15
+ label: "环形",
16
+ description: "节点沿圆环排列,适合观察整体结构",
17
+ category: "network"
18
+ },
19
+ {
20
+ value: "concentric",
21
+ label: "同心圆",
22
+ description: "按连接度分层,突出核心节点和外围节点",
23
+ category: "network"
24
+ },
25
+ {
26
+ value: "antv-dagre",
27
+ label: "有向层级",
28
+ description: "按边的方向排列层级,适合 DAG 或流程数据",
29
+ category: "hierarchy"
30
+ },
31
+ {
32
+ value: "indented",
33
+ label: "缩进树",
34
+ description: "目录式层级布局,适合近树结构",
35
+ category: "hierarchy"
36
+ },
37
+ {
38
+ value: "mindmap",
39
+ label: "思维导图",
40
+ description: "从中心向两侧分支,适合近树结构",
41
+ category: "hierarchy"
42
+ }
43
+ ], b = class {
44
+ graph = null;
45
+ container = null;
46
+ resizeHandler = null;
47
+ createGraph(e, t) {
48
+ let { container: n, width: r, height: i } = t;
49
+ this.graph && this.destroy(), this.container = n, this.graph = new _({
50
+ container: n,
51
+ width: r || n.offsetWidth,
52
+ height: i || n.offsetHeight,
53
+ ...e
54
+ }), this.setupResizeHandler();
55
+ }
56
+ setupResizeHandler() {
57
+ this.cleanupResizeHandler(), this.resizeHandler = () => {
58
+ this.container && this.resize(this.container.offsetWidth, this.container.offsetHeight);
59
+ }, window.addEventListener("resize", this.resizeHandler);
60
+ }
61
+ cleanupResizeHandler() {
62
+ this.resizeHandler &&= (window.removeEventListener("resize", this.resizeHandler), null);
63
+ }
64
+ run(e) {
65
+ e.catch(() => void 0);
66
+ }
67
+ hasGraph() {
68
+ return this.graph !== null;
69
+ }
70
+ resize(e, t) {
71
+ if (!this.graph || e <= 0 || t <= 0) return;
72
+ let [n, r] = this.graph.getSize();
73
+ (n !== e || r !== t) && this.graph.resize(e, t);
74
+ }
75
+ fitView(e = 20) {
76
+ this.graph && this.run(this.graph.fitView({ when: "always" }, !1));
77
+ }
78
+ destroy() {
79
+ this.cleanupResizeHandler(), this.graph?.destroy(), this.graph = null, this.container = null;
80
+ }
81
+ }, x = class extends b {
82
+ currentLayoutType = "force";
83
+ nodeClickHandler = null;
84
+ canvasElement = null;
85
+ canvasPointerDownHandler = null;
86
+ canvasPointerMoveHandler = null;
87
+ canvasPointerUpHandler = null;
88
+ canvasPointerId = null;
89
+ lastCanvasPoint = {
90
+ x: 0,
91
+ y: 0
92
+ };
93
+ isCanvasDragging = !1;
94
+ canvasDefaultCursor = "default";
95
+ getLayoutConfig(e, t, n) {
96
+ let r = [t / 2, n / 2], i = (e) => Math.min(Math.max((e.label?.length ?? 4) * 14 + 28, 72), 220), a = () => 42;
97
+ switch (e) {
98
+ case "circular": return {
99
+ type: e,
100
+ center: r,
101
+ radius: Math.min(t, n) * .35,
102
+ startAngle: Math.PI / 2,
103
+ endAngle: Math.PI * 2.5,
104
+ ordering: "degree"
105
+ };
106
+ case "concentric": return {
107
+ type: e,
108
+ center: r,
109
+ preventOverlap: !0,
110
+ nodeSize: 42,
111
+ sortBy: "degree",
112
+ equidistant: !0
113
+ };
114
+ case "antv-dagre": return {
115
+ type: e,
116
+ rankdir: "TB",
117
+ align: "UL",
118
+ nodesep: 42,
119
+ ranksep: 110
120
+ };
121
+ case "indented": return {
122
+ type: e,
123
+ direction: "LR",
124
+ indent: 36,
125
+ getWidth: i,
126
+ getHeight: a,
127
+ getHGap: () => 18,
128
+ getVGap: () => 12
129
+ };
130
+ case "mindmap": return {
131
+ type: e,
132
+ direction: "H",
133
+ preLayout: !1,
134
+ getWidth: i,
135
+ getHeight: a,
136
+ getHGap: () => 30,
137
+ getVGap: () => 18
138
+ };
139
+ default: return {
140
+ type: "force",
141
+ center: r,
142
+ linkDistance: 280,
143
+ nodeStrength: 1e3,
144
+ edgeStrength: 200,
145
+ gravity: 10
146
+ };
147
+ }
148
+ }
149
+ getColorByType(e) {
150
+ return {
151
+ 人物: "#ff4d4f",
152
+ 地点: "#52c41a",
153
+ 组织: "#1890ff",
154
+ 概念: "#faad14",
155
+ 事件: "#722ed1",
156
+ entity: "#13c2c2"
157
+ }[e || "entity"] || "#13c2c2";
158
+ }
159
+ getColorByRelation(e) {
160
+ return {
161
+ 因果: "#e74c3c",
162
+ 所属: "#2ecc71",
163
+ 引用: "#3498db",
164
+ 相关: "#9b59b6"
165
+ }[e || "相关"] || "#9b59b6";
166
+ }
167
+ init(e, t, n) {
168
+ let r = e.width || e.container.offsetWidth, i = e.height || e.container.offsetHeight, a = this.processData(t, n);
169
+ this.currentLayoutType = e.layoutType ?? "force", this.createGraph({
170
+ data: a,
171
+ animation: !1,
172
+ zoomRange: [.1, 5],
173
+ layout: this.getLayoutConfig(this.currentLayoutType, r, i),
174
+ behaviors: [
175
+ {
176
+ type: "zoom-canvas",
177
+ sensitivity: 1
178
+ },
179
+ { type: "drag-element" },
180
+ {
181
+ type: "hover-activate",
182
+ degree: 1
183
+ },
184
+ {
185
+ type: "click-select",
186
+ degree: 1
187
+ }
188
+ ],
189
+ node: {
190
+ type: "circle",
191
+ style: {
192
+ cursor: "pointer",
193
+ labelPlacement: "bottom",
194
+ labelFill: "#000000",
195
+ labelFontSize: 16,
196
+ labelFontWeight: "bold"
197
+ },
198
+ state: {
199
+ active: {
200
+ lineWidth: 3,
201
+ shadowBlur: 12,
202
+ shadowColor: "#40a9ff"
203
+ },
204
+ selected: {
205
+ lineWidth: 4,
206
+ stroke: "#ff4d4f"
207
+ },
208
+ inactive: {
209
+ opacity: .25,
210
+ labelOpacity: .35
211
+ }
212
+ }
213
+ },
214
+ edge: {
215
+ type: "line",
216
+ style: {
217
+ cursor: "pointer",
218
+ endArrow: !0,
219
+ labelAutoRotate: !0,
220
+ labelFontSize: 12
221
+ },
222
+ state: {
223
+ active: { lineWidth: 3 },
224
+ selected: {
225
+ lineWidth: 3,
226
+ stroke: "#ff4d4f"
227
+ },
228
+ inactive: {
229
+ opacity: .2,
230
+ labelOpacity: .2
231
+ }
232
+ }
233
+ }
234
+ }, e), this.graph && (this.nodeClickHandler = e.onNodeClick ?? null, this.setupEvents(), this.run(this.graph.render().then(() => {
235
+ this.setupCanvasDrag(), this.scheduleFitView();
236
+ })));
237
+ }
238
+ update(e, t) {
239
+ this.graph && (this.graph.setData(this.processData(e, t)), this.run(this.graph.render().then(() => this.scheduleFitView())));
240
+ }
241
+ resetLayout() {
242
+ if (!this.graph) return;
243
+ let e = this.graph.getNodeData().map((e) => {
244
+ let t = { ...e.style };
245
+ return delete t.x, delete t.y, {
246
+ ...e,
247
+ style: t
248
+ };
249
+ });
250
+ this.graph.setData({
251
+ nodes: e,
252
+ edges: this.graph.getEdgeData()
253
+ }), this.graph.setLayout(this.getCurrentLayout()), this.run(this.graph.layout().then(() => this.scheduleFitView()));
254
+ }
255
+ switchLayout(e) {
256
+ !this.graph || !this.container || (this.currentLayoutType = e, this.graph.setLayout(this.getCurrentLayout()), this.run(this.graph.layout().then(() => this.scheduleFitView())));
257
+ }
258
+ getLayoutType() {
259
+ return this.currentLayoutType;
260
+ }
261
+ focusNode(e) {
262
+ !this.graph || !this.graph.getNodeData().some(({ id: t }) => t === e) || this.run(this.graph.focusElement(e, {
263
+ duration: 500,
264
+ easing: "ease-in-out"
265
+ }));
266
+ }
267
+ getCurrentLayout() {
268
+ let e = this.container?.offsetWidth || this.graph?.getSize()[0] || 0, t = this.container?.offsetHeight || this.graph?.getSize()[1] || 0;
269
+ return this.getLayoutConfig(this.currentLayoutType, e, t);
270
+ }
271
+ processData(e, t) {
272
+ let n = {};
273
+ for (let e of t) n[e.source] = (n[e.source] || 0) + 1, n[e.target] = (n[e.target] || 0) + 1;
274
+ return {
275
+ nodes: e.map((e) => ({
276
+ id: e.id,
277
+ data: {
278
+ ...e,
279
+ label: e.label,
280
+ entityType: e.type
281
+ },
282
+ style: {
283
+ x: e.x,
284
+ y: e.y,
285
+ size: e.size || Math.min(15 + (n[e.id] || 0) * 3, 40),
286
+ fill: e.style?.fill || this.getColorByType(e.type),
287
+ stroke: e.style?.stroke || "#ffffff",
288
+ lineWidth: e.style?.lineWidth || 2,
289
+ labelText: e.label
290
+ }
291
+ })),
292
+ edges: t.map((e, t) => {
293
+ let n = e.style?.stroke || this.getColorByRelation(e.relation_type);
294
+ return {
295
+ id: e.id || `edge-${e.source}-${e.target}-${t}`,
296
+ source: e.source,
297
+ target: e.target,
298
+ data: {
299
+ label: e.label,
300
+ relationType: e.relation_type
301
+ },
302
+ style: {
303
+ stroke: n,
304
+ lineWidth: e.style?.lineWidth || 2,
305
+ endArrowFill: n,
306
+ labelText: e.label,
307
+ labelFill: n
308
+ }
309
+ };
310
+ })
311
+ };
312
+ }
313
+ scheduleFitView() {
314
+ let e = () => {
315
+ a(() => {
316
+ requestAnimationFrame(() => this.fitView(20));
317
+ });
318
+ };
319
+ e(), window.setTimeout(e, 300), window.setTimeout(e, 1200);
320
+ }
321
+ setupEvents() {
322
+ if (!this.graph || !this.container) return;
323
+ let e = this.graph;
324
+ e.on(v.CLICK, (e) => {
325
+ this.nodeClickHandler?.(e.target.id);
326
+ }), e.on(v.DRAG, (t) => {
327
+ let n = t.target.id, r = .3, i = e.getNeighborNodesData(n).map((e) => ({
328
+ id: e.id,
329
+ style: {
330
+ x: (e.style?.x || 0) + t.dx * r,
331
+ y: (e.style?.y || 0) + t.dy * r
332
+ }
333
+ }));
334
+ i.length !== 0 && (e.updateNodeData(i), this.run(e.draw()));
335
+ });
336
+ }
337
+ setupCanvasDrag() {
338
+ if (!this.graph) return;
339
+ let e = this.graph, t = e.getCanvas(), n = t.getContextService().getDomElement();
340
+ n && (this.canvasElement = n, this.canvasDefaultCursor = t.getConfig().cursor || "default", this.canvasPointerDownHandler = (e) => {
341
+ e.button === 0 && this.canvasPointerId === null && (this.canvasPointerId = e.pointerId, this.isCanvasDragging = !0, this.lastCanvasPoint = {
342
+ x: e.clientX,
343
+ y: e.clientY
344
+ }, n.setPointerCapture(e.pointerId), t.setCursor("grabbing"));
345
+ }, this.canvasPointerMoveHandler = (t) => {
346
+ if (!this.isCanvasDragging || this.canvasPointerId !== null && t.pointerId !== this.canvasPointerId) return;
347
+ let n = [t.clientX - this.lastCanvasPoint.x, t.clientY - this.lastCanvasPoint.y];
348
+ this.lastCanvasPoint = {
349
+ x: t.clientX,
350
+ y: t.clientY
351
+ }, this.run(e.translateBy(n, !1)), t.preventDefault?.();
352
+ }, this.canvasPointerUpHandler = (e) => {
353
+ if (this.canvasPointerId !== null && e.pointerId !== this.canvasPointerId) return;
354
+ let r = this.isCanvasDragging;
355
+ this.isCanvasDragging = !1, this.canvasPointerId = null, r && n.hasPointerCapture?.(e.pointerId) && n.releasePointerCapture(e.pointerId), r && t.setCursor(this.canvasDefaultCursor);
356
+ }, e.on(g.POINTER_DOWN, this.canvasPointerDownHandler), window.addEventListener("pointermove", this.canvasPointerMoveHandler, !0), window.addEventListener("pointerup", this.canvasPointerUpHandler, !0), window.addEventListener("pointercancel", this.canvasPointerUpHandler, !0));
357
+ }
358
+ destroy() {
359
+ this.graph && this.canvasPointerDownHandler && this.graph.off(g.POINTER_DOWN, this.canvasPointerDownHandler), this.canvasPointerMoveHandler && window.removeEventListener("pointermove", this.canvasPointerMoveHandler, !0), this.canvasPointerUpHandler && (window.removeEventListener("pointerup", this.canvasPointerUpHandler, !0), window.removeEventListener("pointercancel", this.canvasPointerUpHandler, !0)), this.canvasElement = null, this.canvasPointerDownHandler = null, this.canvasPointerMoveHandler = null, this.canvasPointerUpHandler = null, this.canvasPointerId = null, this.isCanvasDragging = !1, this.nodeClickHandler = null, super.destroy();
360
+ }
361
+ }, S = /*@__PURE__*/ i({
362
+ __name: "GraphVisualization",
363
+ props: {
364
+ nodes: {},
365
+ edges: {},
366
+ width: { default: 0 },
367
+ height: { default: 0 },
368
+ layoutType: { default: "force" }
369
+ },
370
+ emits: ["nodeClick"],
371
+ setup(e, { expose: t, emit: r }) {
372
+ let i = e, o = r, d = u(null), f = null, p = null, h = null, g = () => d.value ? {
373
+ width: d.value.clientWidth,
374
+ height: d.value.clientHeight
375
+ } : {
376
+ width: 0,
377
+ height: 0
378
+ }, _ = () => {
379
+ d.value && (d.value.clientHeight === 0 && (d.value.style.height = "100%"), a(() => {
380
+ let { width: e, height: t } = g();
381
+ e !== 0 && t !== 0 && (f ||= new x(), f.init({
382
+ container: d.value,
383
+ width: i.width || e,
384
+ height: i.height || t,
385
+ layoutType: i.layoutType,
386
+ onNodeClick: (e) => {
387
+ let t = i.nodes.find((t) => t.id === e);
388
+ t && o("nodeClick", t);
389
+ }
390
+ }, i.nodes, i.edges));
391
+ }));
392
+ }, v = () => {
393
+ if (!f) {
394
+ _();
395
+ return;
396
+ }
397
+ f.update(i.nodes, i.edges);
398
+ };
399
+ return t({
400
+ fitView: () => {
401
+ f?.fitView(20);
402
+ },
403
+ resetLayout: () => {
404
+ f?.resetLayout();
405
+ },
406
+ switchLayout: (e) => {
407
+ f?.switchLayout(e);
408
+ },
409
+ getLayoutType: () => f?.getLayoutType() ?? "force",
410
+ focusNode: (e) => {
411
+ f?.focusNode(e);
412
+ }
413
+ }), m(() => [i.nodes, i.edges], () => {
414
+ f ? v() : _();
415
+ }, { deep: !0 }), m(() => i.layoutType, (e) => {
416
+ f?.switchLayout(e);
417
+ }), s(() => {
418
+ (i.nodes.length > 0 || i.edges.length > 0) && _(), p = new ResizeObserver((e) => {
419
+ for (let t of e) {
420
+ let { width: e, height: n } = t.contentRect;
421
+ h && clearTimeout(h), h = setTimeout(() => {
422
+ e > 0 && n > 0 && f ? f.resize(e, n) : e > 0 && n > 0 && _();
423
+ }, 100);
424
+ }
425
+ }), d.value && (p.observe(d.value), (i.nodes.length > 0 || i.edges.length > 0) && setTimeout(() => {
426
+ f || _();
427
+ }, 100));
428
+ }), c(() => {
429
+ h && clearTimeout(h), p?.disconnect(), p = null, f?.destroy(), f = null;
430
+ }), (e, t) => (l(), n("div", {
431
+ ref_key: "containerRef",
432
+ ref: d,
433
+ class: "graph-container"
434
+ }, null, 512));
435
+ }
436
+ }), C = (e, t) => {
437
+ let n = e.__vccOpts || e;
438
+ for (let [e, r] of t) n[e] = r;
439
+ return n;
440
+ }, w = /*#__PURE__*/ C(S, [["__scopeId", "data-v-232ee924"]]), T = [
441
+ "title",
442
+ "aria-label",
443
+ "aria-expanded"
444
+ ], E = { class: "kg-btn-group" }, D = ["title"], O = ["value"], k = ["label"], A = ["value"], j = /*#__PURE__*/ C(/* @__PURE__ */ i({
445
+ __name: "GraphToolbar",
446
+ props: { layoutType: { default: "force" } },
447
+ emits: [
448
+ "fitView",
449
+ "resetLayout",
450
+ "switchLayout",
451
+ "switchToForce",
452
+ "switchToCircular"
453
+ ],
454
+ setup(i, { emit: a }) {
455
+ let s = i, c = a, g = u(!1), _ = u(s.layoutType), v = [{
456
+ category: "network",
457
+ label: "关系网络"
458
+ }, {
459
+ category: "hierarchy",
460
+ label: "层级与树形"
461
+ }], b = t(() => v.map((e) => ({
462
+ ...e,
463
+ options: y.filter((t) => t.category === e.category)
464
+ })).filter((e) => e.options.length > 0)), x = t(() => y.find((e) => e.value === _.value)), S = (e) => {
465
+ let t = e.target.value;
466
+ y.some((e) => e.value === t) && (_.value = t, c("switchLayout", t), t === "force" && c("switchToForce"), t === "circular" && c("switchToCircular"));
467
+ };
468
+ m(() => s.layoutType, (e) => {
469
+ _.value = e;
470
+ });
471
+ let C = () => {
472
+ g.value = !g.value;
473
+ };
474
+ return (t, i) => (l(), n("div", { class: o(["kg-toolbar", { "kg-collapsed": g.value }]) }, [r("button", {
475
+ onClick: C,
476
+ class: "kg-btn kg-toggle",
477
+ title: g.value ? "展开工具栏" : "收起工具栏",
478
+ "aria-label": g.value ? "展开工具栏" : "收起工具栏",
479
+ "aria-expanded": !g.value
480
+ }, f(g.value ? "▶" : "◀"), 9, T), h(r("div", E, [
481
+ r("button", {
482
+ onClick: i[0] ||= (e) => c("fitView"),
483
+ class: "kg-btn",
484
+ title: "适应画布"
485
+ }, [...i[2] ||= [r("span", { class: "kg-icon" }, "⊡", -1), r("span", { class: "kg-btn-label" }, "适应画布", -1)]]),
486
+ r("button", {
487
+ onClick: i[1] ||= (e) => c("resetLayout"),
488
+ class: "kg-btn",
489
+ title: "重置布局"
490
+ }, [...i[3] ||= [r("span", { class: "kg-icon" }, "↻", -1), r("span", { class: "kg-btn-label" }, "重置布局", -1)]]),
491
+ r("label", {
492
+ class: "kg-layout-control",
493
+ title: x.value?.description
494
+ }, [i[4] ||= r("span", { class: "kg-layout-label" }, "图类型", -1), r("select", {
495
+ class: "kg-layout-select",
496
+ value: _.value,
497
+ "aria-label": "选择图布局",
498
+ onChange: S
499
+ }, [(l(!0), n(e, null, d(b.value, (t) => (l(), n("optgroup", {
500
+ key: t.category,
501
+ label: t.label
502
+ }, [(l(!0), n(e, null, d(t.options, (e) => (l(), n("option", {
503
+ key: e.value,
504
+ value: e.value
505
+ }, f(e.label), 9, A))), 128))], 8, k))), 128))], 40, O)], 8, D)
506
+ ], 512), [[p, !g.value]])], 2));
507
+ }
508
+ }), [["__scopeId", "data-v-f8202e9e"]]), M = (e) => {
509
+ e.component("GraphVisualization", w), e.component("GraphToolbar", j);
510
+ }, N = {
511
+ install: M,
512
+ version: "0.1.0"
513
+ };
514
+ //#endregion
515
+ export { y as GRAPH_LAYOUT_OPTIONS, j as GraphToolbar, w as GraphVisualization, x as KnowledgeGraph, N as default, M as install };
516
+
517
+ //# sourceMappingURL=vue-knowledge-graph.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vue-knowledge-graph.mjs","names":[],"sources":["../src/module/g6Graph.ts","../src/components/GraphVisualization.vue","../src/components/GraphVisualization.vue","../src/components/GraphToolbar.vue","../src/components/GraphToolbar.vue","../src/index.ts"],"sourcesContent":["import {\n CanvasEvent,\n Graph,\n NodeEvent,\n type Canvas,\n type EdgeData,\n type GraphOptions,\n type IElementEvent,\n type IElementDragEvent,\n type IPointerEvent,\n type NodeData,\n type SingleLayoutOptions,\n} from '@antv/g6'\nimport { nextTick } from 'vue'\nimport type { GraphEdge, GraphNode } from '../types'\n\nexport type LayoutType =\n | 'force'\n | 'circular'\n | 'concentric'\n | 'antv-dagre'\n | 'indented'\n | 'mindmap'\n\nexport type GraphLayoutCategory = 'network' | 'hierarchy'\n\nexport interface GraphLayoutOption {\n value: LayoutType\n label: string\n description: string\n category: GraphLayoutCategory\n}\n\nexport const GRAPH_LAYOUT_OPTIONS: readonly GraphLayoutOption[] = [\n {\n value: 'force',\n label: '力导向',\n description: '通用网络布局,适合结构未知或关系密集的图谱',\n category: 'network',\n },\n {\n value: 'circular',\n label: '环形',\n description: '节点沿圆环排列,适合观察整体结构',\n category: 'network',\n },\n {\n value: 'concentric',\n label: '同心圆',\n description: '按连接度分层,突出核心节点和外围节点',\n category: 'network',\n },\n {\n value: 'antv-dagre',\n label: '有向层级',\n description: '按边的方向排列层级,适合 DAG 或流程数据',\n category: 'hierarchy',\n },\n {\n value: 'indented',\n label: '缩进树',\n description: '目录式层级布局,适合近树结构',\n category: 'hierarchy',\n },\n {\n value: 'mindmap',\n label: '思维导图',\n description: '从中心向两侧分支,适合近树结构',\n category: 'hierarchy',\n },\n]\n\nexport interface GraphInitOptions {\n container: HTMLElement\n width?: number\n height?: number\n layoutType?: LayoutType\n onNodeClick?: (nodeId: string) => void\n}\n\n/** 负责 G6 实例的生命周期和通用视口操作。 */\nexport class BaseG6Graph {\n protected graph: Graph | null = null\n protected container: HTMLElement | null = null\n\n private resizeHandler: (() => void) | null = null\n\n protected createGraph(\n config: Omit<GraphOptions, 'container' | 'width' | 'height'>,\n options: GraphInitOptions,\n ) {\n const { container, width, height } = options\n\n if (this.graph) this.destroy()\n\n this.container = container\n this.graph = new Graph({\n container,\n width: width || container.offsetWidth,\n height: height || container.offsetHeight,\n ...config,\n })\n this.setupResizeHandler()\n }\n\n private setupResizeHandler() {\n this.cleanupResizeHandler()\n this.resizeHandler = () => {\n if (!this.container) return\n this.resize(this.container.offsetWidth, this.container.offsetHeight)\n }\n window.addEventListener('resize', this.resizeHandler)\n }\n\n private cleanupResizeHandler() {\n if (!this.resizeHandler) return\n window.removeEventListener('resize', this.resizeHandler)\n this.resizeHandler = null\n }\n\n protected run(action: Promise<unknown>) {\n void action.catch(() => undefined)\n }\n\n public hasGraph(): boolean {\n return this.graph !== null\n }\n\n public resize(width: number, height: number) {\n if (!this.graph || width <= 0 || height <= 0) return\n const [currentWidth, currentHeight] = this.graph.getSize()\n if (currentWidth === width && currentHeight === height) return\n this.graph.resize(width, height)\n }\n\n public fitView(_padding = 20) {\n if (this.graph) this.run(this.graph.fitView({ when: 'always' }, false))\n }\n\n public destroy() {\n this.cleanupResizeHandler()\n this.graph?.destroy()\n this.graph = null\n this.container = null\n }\n}\n\n/** 知识图谱实现:实体/关系配色、布局切换和图谱交互。 */\nexport class KnowledgeGraph extends BaseG6Graph {\n private currentLayoutType: LayoutType = 'force'\n private nodeClickHandler: ((nodeId: string) => void) | null = null\n private canvasElement: HTMLCanvasElement | null = null\n private canvasPointerDownHandler: ((event: IPointerEvent) => void) | null = null\n private canvasPointerMoveHandler: ((event: PointerEvent) => void) | null = null\n private canvasPointerUpHandler: ((event: PointerEvent) => void) | null = null\n private canvasPointerId: number | null = null\n private lastCanvasPoint = { x: 0, y: 0 }\n private isCanvasDragging = false\n private canvasDefaultCursor: NonNullable<ReturnType<Canvas['getConfig']>['cursor']> = 'default'\n\n private getLayoutConfig(\n type: LayoutType,\n width: number,\n height: number,\n ): SingleLayoutOptions {\n const center: [number, number] = [width / 2, height / 2]\n const getNodeWidth = (node: { label?: string }) =>\n Math.min(Math.max((node.label?.length ?? 4) * 14 + 28, 72), 220)\n const getNodeHeight = () => 42\n\n switch (type) {\n case 'circular':\n return {\n type,\n center,\n radius: Math.min(width, height) * 0.35,\n startAngle: Math.PI / 2,\n endAngle: Math.PI * 2.5,\n ordering: 'degree',\n }\n case 'concentric':\n return {\n type,\n center,\n preventOverlap: true,\n nodeSize: 42,\n sortBy: 'degree',\n equidistant: true,\n }\n case 'antv-dagre':\n return {\n type,\n rankdir: 'TB',\n align: 'UL',\n nodesep: 42,\n ranksep: 110,\n }\n case 'indented':\n return {\n type,\n direction: 'LR',\n indent: 36,\n getWidth: getNodeWidth,\n getHeight: getNodeHeight,\n getHGap: () => 18,\n getVGap: () => 12,\n }\n case 'mindmap':\n return {\n type,\n direction: 'H',\n preLayout: false,\n getWidth: getNodeWidth,\n getHeight: getNodeHeight,\n getHGap: () => 30,\n getVGap: () => 18,\n }\n case 'force':\n default:\n return {\n type: 'force',\n center,\n linkDistance: 280,\n nodeStrength: 1000,\n edgeStrength: 200,\n gravity: 10,\n }\n }\n }\n\n private getColorByType(entityType?: string): string {\n const colors: Record<string, string> = {\n 人物: '#ff4d4f',\n 地点: '#52c41a',\n 组织: '#1890ff',\n 概念: '#faad14',\n 事件: '#722ed1',\n entity: '#13c2c2',\n }\n return colors[entityType || 'entity'] || '#13c2c2'\n }\n\n private getColorByRelation(relationType?: string): string {\n const colors: Record<string, string> = {\n 因果: '#e74c3c',\n 所属: '#2ecc71',\n 引用: '#3498db',\n 相关: '#9b59b6',\n }\n return colors[relationType || '相关'] || '#9b59b6'\n }\n\n public init(options: GraphInitOptions, nodes: GraphNode[], edges: GraphEdge[]) {\n const width = options.width || options.container.offsetWidth\n const height = options.height || options.container.offsetHeight\n const data = this.processData(nodes, edges)\n\n this.currentLayoutType = options.layoutType ?? 'force'\n this.createGraph(\n {\n data,\n animation: false,\n zoomRange: [0.1, 5],\n layout: this.getLayoutConfig(this.currentLayoutType, width, height),\n behaviors: [\n { type: 'zoom-canvas', sensitivity: 1 },\n { type: 'drag-element' },\n { type: 'hover-activate', degree: 1 },\n { type: 'click-select', degree: 1 },\n ],\n node: {\n type: 'circle',\n style: {\n cursor: 'pointer',\n labelPlacement: 'bottom',\n labelFill: '#000000',\n labelFontSize: 16,\n labelFontWeight: 'bold',\n },\n state: {\n active: { lineWidth: 3, shadowBlur: 12, shadowColor: '#40a9ff' },\n selected: { lineWidth: 4, stroke: '#ff4d4f' },\n inactive: { opacity: 0.25, labelOpacity: 0.35 },\n },\n },\n edge: {\n type: 'line',\n style: {\n cursor: 'pointer',\n endArrow: true,\n labelAutoRotate: true,\n labelFontSize: 12,\n },\n state: {\n active: { lineWidth: 3 },\n selected: { lineWidth: 3, stroke: '#ff4d4f' },\n inactive: { opacity: 0.2, labelOpacity: 0.2 },\n },\n },\n },\n options,\n )\n\n if (!this.graph) return\n this.nodeClickHandler = options.onNodeClick ?? null\n this.setupEvents()\n this.run(this.graph.render().then(() => {\n this.setupCanvasDrag()\n this.scheduleFitView()\n }))\n }\n\n public update(nodes: GraphNode[], edges: GraphEdge[]) {\n if (!this.graph) return\n this.graph.setData(this.processData(nodes, edges))\n this.run(this.graph.render().then(() => this.scheduleFitView()))\n }\n\n public resetLayout() {\n if (!this.graph) return\n\n const nodes = this.graph.getNodeData().map((node) => {\n const style = { ...node.style }\n delete style.x\n delete style.y\n return { ...node, style }\n })\n this.graph.setData({ nodes, edges: this.graph.getEdgeData() })\n this.graph.setLayout(this.getCurrentLayout())\n this.run(this.graph.layout().then(() => this.scheduleFitView()))\n }\n\n public switchLayout(type: LayoutType) {\n if (!this.graph || !this.container) return\n this.currentLayoutType = type\n this.graph.setLayout(this.getCurrentLayout())\n this.run(this.graph.layout().then(() => this.scheduleFitView()))\n }\n\n public getLayoutType(): LayoutType {\n return this.currentLayoutType\n }\n\n public focusNode(nodeId: string) {\n if (!this.graph || !this.graph.getNodeData().some(({ id }) => id === nodeId)) return\n this.run(this.graph.focusElement(nodeId, { duration: 500, easing: 'ease-in-out' }))\n }\n\n private getCurrentLayout() {\n const width = this.container?.offsetWidth || this.graph?.getSize()[0] || 0\n const height = this.container?.offsetHeight || this.graph?.getSize()[1] || 0\n return this.getLayoutConfig(this.currentLayoutType, width, height)\n }\n\n private processData(nodes: GraphNode[], edges: GraphEdge[]): {\n nodes: NodeData[]\n edges: EdgeData[]\n } {\n const degree: Record<string, number> = {}\n for (const edge of edges) {\n degree[edge.source] = (degree[edge.source] || 0) + 1\n degree[edge.target] = (degree[edge.target] || 0) + 1\n }\n\n return {\n nodes: nodes.map((node) => ({\n id: node.id,\n data: {\n ...node,\n label: node.label,\n entityType: node.type,\n },\n style: {\n x: node.x,\n y: node.y,\n size: node.size || Math.min(15 + (degree[node.id] || 0) * 3, 40),\n fill: node.style?.fill || this.getColorByType(node.type),\n stroke: node.style?.stroke || '#ffffff',\n lineWidth: node.style?.lineWidth || 2,\n labelText: node.label,\n },\n })),\n edges: edges.map((edge, index) => {\n const color = edge.style?.stroke || this.getColorByRelation(edge.relation_type)\n return {\n id: edge.id || `edge-${edge.source}-${edge.target}-${index}`,\n source: edge.source,\n target: edge.target,\n data: { label: edge.label, relationType: edge.relation_type },\n style: {\n stroke: color,\n lineWidth: edge.style?.lineWidth || 2,\n endArrowFill: color,\n labelText: edge.label,\n labelFill: color,\n },\n }\n }),\n }\n }\n\n private scheduleFitView() {\n const fit = () => {\n nextTick(() => {\n requestAnimationFrame(() => this.fitView(20))\n })\n }\n fit()\n window.setTimeout(fit, 300)\n window.setTimeout(fit, 1200)\n }\n\n private setupEvents() {\n if (!this.graph || !this.container) return\n const graph = this.graph\n\n graph.on(NodeEvent.CLICK, (event: IElementEvent) => {\n this.nodeClickHandler?.(event.target.id)\n })\n\n graph.on(NodeEvent.DRAG, (event: IElementDragEvent) => {\n const nodeId = event.target.id\n const factor = 0.3\n const updates = graph.getNeighborNodesData(nodeId).map((node) => ({\n id: node.id,\n style: {\n x: (node.style?.x || 0) + event.dx * factor,\n y: (node.style?.y || 0) + event.dy * factor,\n },\n }))\n if (updates.length === 0) return\n graph.updateNodeData(updates)\n this.run(graph.draw())\n })\n }\n\n private setupCanvasDrag() {\n if (!this.graph) return\n const graph = this.graph\n const canvas = graph.getCanvas()\n const canvasElement = canvas.getContextService().getDomElement() as HTMLCanvasElement | null\n if (!canvasElement) return\n\n this.canvasElement = canvasElement\n this.canvasDefaultCursor = canvas.getConfig().cursor || 'default'\n this.canvasPointerDownHandler = (event) => {\n if (event.button !== 0 || this.canvasPointerId !== null) return\n\n this.canvasPointerId = event.pointerId\n this.isCanvasDragging = true\n this.lastCanvasPoint = { x: event.clientX, y: event.clientY }\n canvasElement.setPointerCapture(event.pointerId)\n canvas.setCursor('grabbing')\n }\n this.canvasPointerMoveHandler = (event) => {\n if (\n !this.isCanvasDragging ||\n (this.canvasPointerId !== null && event.pointerId !== this.canvasPointerId)\n ) {\n return\n }\n\n const offset: [number, number] = [\n event.clientX - this.lastCanvasPoint.x,\n event.clientY - this.lastCanvasPoint.y,\n ]\n this.lastCanvasPoint = { x: event.clientX, y: event.clientY }\n this.run(graph.translateBy(offset, false))\n event.preventDefault?.()\n }\n this.canvasPointerUpHandler = (event) => {\n if (this.canvasPointerId !== null && event.pointerId !== this.canvasPointerId) {\n return\n }\n\n const wasDragging = this.isCanvasDragging\n this.isCanvasDragging = false\n this.canvasPointerId = null\n if (wasDragging && canvasElement.hasPointerCapture?.(event.pointerId)) {\n canvasElement.releasePointerCapture(event.pointerId)\n }\n if (wasDragging) canvas.setCursor(this.canvasDefaultCursor)\n }\n\n graph.on(CanvasEvent.POINTER_DOWN, this.canvasPointerDownHandler)\n window.addEventListener('pointermove', this.canvasPointerMoveHandler, true)\n window.addEventListener('pointerup', this.canvasPointerUpHandler, true)\n window.addEventListener('pointercancel', this.canvasPointerUpHandler, true)\n }\n\n public override destroy() {\n if (this.graph && this.canvasPointerDownHandler) {\n this.graph.off(CanvasEvent.POINTER_DOWN, this.canvasPointerDownHandler)\n }\n if (this.canvasPointerMoveHandler) {\n window.removeEventListener('pointermove', this.canvasPointerMoveHandler, true)\n }\n if (this.canvasPointerUpHandler) {\n window.removeEventListener('pointerup', this.canvasPointerUpHandler, true)\n window.removeEventListener('pointercancel', this.canvasPointerUpHandler, true)\n }\n\n this.canvasElement = null\n this.canvasPointerDownHandler = null\n this.canvasPointerMoveHandler = null\n this.canvasPointerUpHandler = null\n this.canvasPointerId = null\n this.isCanvasDragging = false\n this.nodeClickHandler = null\n super.destroy()\n }\n}\n","<template>\n <div\n ref=\"containerRef\"\n class=\"graph-container\"\n ></div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'\nimport type { GraphNode, GraphEdge } from '../types'\nimport { KnowledgeGraph, type LayoutType } from '../module/g6Graph'\n\ninterface Props {\n nodes: GraphNode[]\n edges: GraphEdge[]\n width?: number\n height?: number\n layoutType?: LayoutType\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n width: 0,\n height: 0,\n layoutType: 'force',\n})\n\nconst emit = defineEmits<{\n nodeClick: [node: GraphNode]\n}>()\n\nconst containerRef = ref<HTMLElement | null>(null)\nlet graph: KnowledgeGraph | null = null\nlet resizeObserver: ResizeObserver | null = null\nlet resizeTimeout: ReturnType<typeof setTimeout> | null = null\n\nconst getContainerSize = () => {\n if (!containerRef.value) return { width: 0, height: 0 }\n return {\n width: containerRef.value.clientWidth,\n height: containerRef.value.clientHeight\n }\n}\n\nconst initGraph = () => {\n if (!containerRef.value) return\n\n // 确保容器有高度\n if (containerRef.value.clientHeight === 0) {\n containerRef.value.style.height = '100%'\n }\n\n nextTick(() => {\n const { width, height } = getContainerSize()\n if (width === 0 || height === 0) {\n return\n }\n\n if (!graph) {\n graph = new KnowledgeGraph()\n }\n\n graph.init(\n {\n container: containerRef.value!,\n width: props.width || width,\n height: props.height || height,\n layoutType: props.layoutType,\n onNodeClick: (nodeId) => {\n const node = props.nodes.find((item) => item.id === nodeId)\n if (node) emit('nodeClick', node)\n },\n },\n props.nodes,\n props.edges\n )\n })\n}\n\nconst updateGraph = () => {\n if (!graph) {\n initGraph()\n return\n }\n\n graph.update(props.nodes, props.edges)\n}\n\nconst fitView = () => {\n graph?.fitView(20)\n}\n\nconst resetLayout = () => {\n graph?.resetLayout()\n}\n\nconst switchLayout = (type: LayoutType) => {\n graph?.switchLayout(type)\n}\n\nconst getLayoutType = () => graph?.getLayoutType() ?? 'force'\n\nconst focusNode = (nodeId: string) => {\n graph?.focusNode(nodeId)\n}\n\ndefineExpose({\n fitView,\n resetLayout,\n switchLayout,\n getLayoutType,\n focusNode\n})\n\nwatch(\n () => [props.nodes, props.edges],\n () => {\n if (graph) {\n updateGraph()\n } else {\n initGraph()\n }\n },\n { deep: true }\n)\n\nwatch(\n () => props.layoutType,\n (type) => {\n graph?.switchLayout(type)\n }\n)\n\nonMounted(() => {\n if (props.nodes.length > 0 || props.edges.length > 0) {\n initGraph()\n }\n\n // 使用 ResizeObserver 监听容器尺寸变化\n resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect\n // 防止 resize 循环:使用防抖\n if (resizeTimeout) clearTimeout(resizeTimeout)\n resizeTimeout = setTimeout(() => {\n if (width > 0 && height > 0 && graph) {\n graph.resize(width, height)\n } else if (width > 0 && height > 0) {\n // 首次初始化\n initGraph()\n }\n }, 100)\n }\n })\n\n if (containerRef.value) {\n resizeObserver.observe(containerRef.value)\n\n // 如果数据已存在但尺寸为 0,延迟尝试初始化\n if ((props.nodes.length > 0 || props.edges.length > 0)) {\n setTimeout(() => {\n if (!graph) initGraph()\n }, 100)\n }\n }\n})\n\nonUnmounted(() => {\n if (resizeTimeout) clearTimeout(resizeTimeout)\n resizeObserver?.disconnect()\n resizeObserver = null\n graph?.destroy()\n graph = null\n})\n</script>\n\n<style scoped>\n.graph-container {\n width: 100%;\n height: 100%;\n}\n</style>\n","<template>\n <div\n ref=\"containerRef\"\n class=\"graph-container\"\n ></div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'\nimport type { GraphNode, GraphEdge } from '../types'\nimport { KnowledgeGraph, type LayoutType } from '../module/g6Graph'\n\ninterface Props {\n nodes: GraphNode[]\n edges: GraphEdge[]\n width?: number\n height?: number\n layoutType?: LayoutType\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n width: 0,\n height: 0,\n layoutType: 'force',\n})\n\nconst emit = defineEmits<{\n nodeClick: [node: GraphNode]\n}>()\n\nconst containerRef = ref<HTMLElement | null>(null)\nlet graph: KnowledgeGraph | null = null\nlet resizeObserver: ResizeObserver | null = null\nlet resizeTimeout: ReturnType<typeof setTimeout> | null = null\n\nconst getContainerSize = () => {\n if (!containerRef.value) return { width: 0, height: 0 }\n return {\n width: containerRef.value.clientWidth,\n height: containerRef.value.clientHeight\n }\n}\n\nconst initGraph = () => {\n if (!containerRef.value) return\n\n // 确保容器有高度\n if (containerRef.value.clientHeight === 0) {\n containerRef.value.style.height = '100%'\n }\n\n nextTick(() => {\n const { width, height } = getContainerSize()\n if (width === 0 || height === 0) {\n return\n }\n\n if (!graph) {\n graph = new KnowledgeGraph()\n }\n\n graph.init(\n {\n container: containerRef.value!,\n width: props.width || width,\n height: props.height || height,\n layoutType: props.layoutType,\n onNodeClick: (nodeId) => {\n const node = props.nodes.find((item) => item.id === nodeId)\n if (node) emit('nodeClick', node)\n },\n },\n props.nodes,\n props.edges\n )\n })\n}\n\nconst updateGraph = () => {\n if (!graph) {\n initGraph()\n return\n }\n\n graph.update(props.nodes, props.edges)\n}\n\nconst fitView = () => {\n graph?.fitView(20)\n}\n\nconst resetLayout = () => {\n graph?.resetLayout()\n}\n\nconst switchLayout = (type: LayoutType) => {\n graph?.switchLayout(type)\n}\n\nconst getLayoutType = () => graph?.getLayoutType() ?? 'force'\n\nconst focusNode = (nodeId: string) => {\n graph?.focusNode(nodeId)\n}\n\ndefineExpose({\n fitView,\n resetLayout,\n switchLayout,\n getLayoutType,\n focusNode\n})\n\nwatch(\n () => [props.nodes, props.edges],\n () => {\n if (graph) {\n updateGraph()\n } else {\n initGraph()\n }\n },\n { deep: true }\n)\n\nwatch(\n () => props.layoutType,\n (type) => {\n graph?.switchLayout(type)\n }\n)\n\nonMounted(() => {\n if (props.nodes.length > 0 || props.edges.length > 0) {\n initGraph()\n }\n\n // 使用 ResizeObserver 监听容器尺寸变化\n resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect\n // 防止 resize 循环:使用防抖\n if (resizeTimeout) clearTimeout(resizeTimeout)\n resizeTimeout = setTimeout(() => {\n if (width > 0 && height > 0 && graph) {\n graph.resize(width, height)\n } else if (width > 0 && height > 0) {\n // 首次初始化\n initGraph()\n }\n }, 100)\n }\n })\n\n if (containerRef.value) {\n resizeObserver.observe(containerRef.value)\n\n // 如果数据已存在但尺寸为 0,延迟尝试初始化\n if ((props.nodes.length > 0 || props.edges.length > 0)) {\n setTimeout(() => {\n if (!graph) initGraph()\n }, 100)\n }\n }\n})\n\nonUnmounted(() => {\n if (resizeTimeout) clearTimeout(resizeTimeout)\n resizeObserver?.disconnect()\n resizeObserver = null\n graph?.destroy()\n graph = null\n})\n</script>\n\n<style scoped>\n.graph-container {\n width: 100%;\n height: 100%;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport {\n GRAPH_LAYOUT_OPTIONS,\n type GraphLayoutCategory,\n type LayoutType,\n} from '../module/g6Graph'\n\ninterface Props {\n layoutType?: LayoutType\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n layoutType: 'force'\n})\n\nconst emit = defineEmits<{\n fitView: []\n resetLayout: []\n switchLayout: [type: LayoutType]\n switchToForce: []\n switchToCircular: []\n}>()\n\nconst collapsed = ref(false)\nconst selectedLayout = ref<LayoutType>(props.layoutType)\n\nconst layoutGroups: Array<{ category: GraphLayoutCategory; label: string }> = [\n { category: 'network', label: '关系网络' },\n { category: 'hierarchy', label: '层级与树形' },\n]\n\nconst groupedLayouts = computed(() =>\n layoutGroups\n .map((group) => ({\n ...group,\n options: GRAPH_LAYOUT_OPTIONS.filter((layout) => layout.category === group.category),\n }))\n .filter((group) => group.options.length > 0)\n)\n\nconst currentLayout = computed(\n () => GRAPH_LAYOUT_OPTIONS.find((layout) => layout.value === selectedLayout.value)\n)\n\nconst handleLayoutChange = (event: Event) => {\n const type = (event.target as HTMLSelectElement).value as LayoutType\n if (!GRAPH_LAYOUT_OPTIONS.some((layout) => layout.value === type)) return\n\n selectedLayout.value = type\n emit('switchLayout', type)\n if (type === 'force') emit('switchToForce')\n if (type === 'circular') emit('switchToCircular')\n}\n\nwatch(\n () => props.layoutType,\n (type) => {\n selectedLayout.value = type\n }\n)\n\nconst toggleCollapse = () => {\n collapsed.value = !collapsed.value\n}\n</script>\n\n<template>\n <div\n class=\"kg-toolbar\"\n :class=\"{ 'kg-collapsed': collapsed }\"\n >\n <button\n @click=\"toggleCollapse\"\n class=\"kg-btn kg-toggle\"\n :title=\"collapsed ? '展开工具栏' : '收起工具栏'\"\n :aria-label=\"collapsed ? '展开工具栏' : '收起工具栏'\"\n :aria-expanded=\"!collapsed\"\n >\n {{ collapsed ? '▶' : '◀' }}\n </button>\n <div\n class=\"kg-btn-group\"\n v-show=\"!collapsed\"\n >\n <button\n @click=\"emit('fitView')\"\n class=\"kg-btn\"\n title=\"适应画布\"\n >\n <span class=\"kg-icon\">⊡</span>\n <span class=\"kg-btn-label\">适应画布</span>\n </button>\n <button\n @click=\"emit('resetLayout')\"\n class=\"kg-btn\"\n title=\"重置布局\"\n >\n <span class=\"kg-icon\">↻</span>\n <span class=\"kg-btn-label\">重置布局</span>\n </button>\n <label\n class=\"kg-layout-control\"\n :title=\"currentLayout?.description\"\n >\n <span class=\"kg-layout-label\">图类型</span>\n <select\n class=\"kg-layout-select\"\n :value=\"selectedLayout\"\n aria-label=\"选择图布局\"\n @change=\"handleLayoutChange\"\n >\n <optgroup\n v-for=\"group in groupedLayouts\"\n :key=\"group.category\"\n :label=\"group.label\"\n >\n <option\n v-for=\"layout in group.options\"\n :key=\"layout.value\"\n :value=\"layout.value\"\n >\n {{ layout.label }}\n </option>\n </optgroup>\n </select>\n </label>\n </div>\n </div>\n</template>\n\n<style scoped>\n.kg-toolbar {\n position: absolute;\n top: 16px;\n right: 16px;\n background: #fff;\n border: 1px solid #e2e8f0;\n border-radius: 10px;\n padding: 8px;\n z-index: 100;\n display: flex;\n gap: 6px;\n align-items: center;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n font-family: 'Segoe UI', 'PingFang SC', system-ui, sans-serif;\n}\n\n.kg-toolbar:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n.kg-toolbar.kg-collapsed {\n padding: 6px;\n right: 12px;\n}\n\n.kg-btn-group {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.kg-btn {\n padding: 6px 12px;\n background: transparent;\n border: none;\n border-radius: 6px;\n color: #334155;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 5px;\n outline: none;\n}\n\n.kg-btn:hover {\n background: #e2e8f0;\n color: #0f172a;\n}\n\n.kg-btn:focus-visible,\n.kg-toggle:focus-visible {\n outline: 2px solid #60a5fa;\n outline-offset: 2px;\n}\n\n.kg-icon {\n font-size: 13px;\n line-height: 1;\n}\n\n.kg-layout-control {\n display: flex;\n align-items: center;\n gap: 7px;\n padding-left: 10px;\n border-left: 1px solid #e2e8f0;\n}\n\n.kg-layout-label {\n color: #64748b;\n font-size: 11px;\n font-weight: 650;\n white-space: nowrap;\n}\n\n.kg-layout-select {\n width: 128px;\n min-height: 31px;\n padding: 5px 26px 5px 9px;\n border: 1px solid #cbd5e1;\n border-radius: 6px;\n background: #fff;\n color: #1e293b;\n font-size: 12px;\n outline: none;\n}\n\n.kg-layout-select:hover,\n.kg-layout-select:focus {\n border-color: #60a5fa;\n box-shadow: 0 0 0 2px #dbeafe;\n}\n\n.kg-toggle {\n padding: 4px 8px;\n background: transparent;\n color: #64748b;\n border: none;\n border-radius: 4px;\n font-size: 11px;\n}\n\n.kg-toggle:hover {\n background: #e2e8f0;\n color: #0f172a;\n}\n\n@media (max-width: 640px) {\n .kg-toolbar {\n max-width: calc(100% - 24px);\n }\n\n .kg-btn-group {\n gap: 4px;\n }\n\n .kg-btn {\n padding: 6px 8px;\n }\n\n .kg-btn-label {\n display: none;\n }\n\n .kg-layout-control {\n padding-left: 6px;\n gap: 4px;\n }\n\n .kg-layout-label {\n display: none;\n }\n\n .kg-layout-select {\n width: 120px;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport {\n GRAPH_LAYOUT_OPTIONS,\n type GraphLayoutCategory,\n type LayoutType,\n} from '../module/g6Graph'\n\ninterface Props {\n layoutType?: LayoutType\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n layoutType: 'force'\n})\n\nconst emit = defineEmits<{\n fitView: []\n resetLayout: []\n switchLayout: [type: LayoutType]\n switchToForce: []\n switchToCircular: []\n}>()\n\nconst collapsed = ref(false)\nconst selectedLayout = ref<LayoutType>(props.layoutType)\n\nconst layoutGroups: Array<{ category: GraphLayoutCategory; label: string }> = [\n { category: 'network', label: '关系网络' },\n { category: 'hierarchy', label: '层级与树形' },\n]\n\nconst groupedLayouts = computed(() =>\n layoutGroups\n .map((group) => ({\n ...group,\n options: GRAPH_LAYOUT_OPTIONS.filter((layout) => layout.category === group.category),\n }))\n .filter((group) => group.options.length > 0)\n)\n\nconst currentLayout = computed(\n () => GRAPH_LAYOUT_OPTIONS.find((layout) => layout.value === selectedLayout.value)\n)\n\nconst handleLayoutChange = (event: Event) => {\n const type = (event.target as HTMLSelectElement).value as LayoutType\n if (!GRAPH_LAYOUT_OPTIONS.some((layout) => layout.value === type)) return\n\n selectedLayout.value = type\n emit('switchLayout', type)\n if (type === 'force') emit('switchToForce')\n if (type === 'circular') emit('switchToCircular')\n}\n\nwatch(\n () => props.layoutType,\n (type) => {\n selectedLayout.value = type\n }\n)\n\nconst toggleCollapse = () => {\n collapsed.value = !collapsed.value\n}\n</script>\n\n<template>\n <div\n class=\"kg-toolbar\"\n :class=\"{ 'kg-collapsed': collapsed }\"\n >\n <button\n @click=\"toggleCollapse\"\n class=\"kg-btn kg-toggle\"\n :title=\"collapsed ? '展开工具栏' : '收起工具栏'\"\n :aria-label=\"collapsed ? '展开工具栏' : '收起工具栏'\"\n :aria-expanded=\"!collapsed\"\n >\n {{ collapsed ? '▶' : '◀' }}\n </button>\n <div\n class=\"kg-btn-group\"\n v-show=\"!collapsed\"\n >\n <button\n @click=\"emit('fitView')\"\n class=\"kg-btn\"\n title=\"适应画布\"\n >\n <span class=\"kg-icon\">⊡</span>\n <span class=\"kg-btn-label\">适应画布</span>\n </button>\n <button\n @click=\"emit('resetLayout')\"\n class=\"kg-btn\"\n title=\"重置布局\"\n >\n <span class=\"kg-icon\">↻</span>\n <span class=\"kg-btn-label\">重置布局</span>\n </button>\n <label\n class=\"kg-layout-control\"\n :title=\"currentLayout?.description\"\n >\n <span class=\"kg-layout-label\">图类型</span>\n <select\n class=\"kg-layout-select\"\n :value=\"selectedLayout\"\n aria-label=\"选择图布局\"\n @change=\"handleLayoutChange\"\n >\n <optgroup\n v-for=\"group in groupedLayouts\"\n :key=\"group.category\"\n :label=\"group.label\"\n >\n <option\n v-for=\"layout in group.options\"\n :key=\"layout.value\"\n :value=\"layout.value\"\n >\n {{ layout.label }}\n </option>\n </optgroup>\n </select>\n </label>\n </div>\n </div>\n</template>\n\n<style scoped>\n.kg-toolbar {\n position: absolute;\n top: 16px;\n right: 16px;\n background: #fff;\n border: 1px solid #e2e8f0;\n border-radius: 10px;\n padding: 8px;\n z-index: 100;\n display: flex;\n gap: 6px;\n align-items: center;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n font-family: 'Segoe UI', 'PingFang SC', system-ui, sans-serif;\n}\n\n.kg-toolbar:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n.kg-toolbar.kg-collapsed {\n padding: 6px;\n right: 12px;\n}\n\n.kg-btn-group {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.kg-btn {\n padding: 6px 12px;\n background: transparent;\n border: none;\n border-radius: 6px;\n color: #334155;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 5px;\n outline: none;\n}\n\n.kg-btn:hover {\n background: #e2e8f0;\n color: #0f172a;\n}\n\n.kg-btn:focus-visible,\n.kg-toggle:focus-visible {\n outline: 2px solid #60a5fa;\n outline-offset: 2px;\n}\n\n.kg-icon {\n font-size: 13px;\n line-height: 1;\n}\n\n.kg-layout-control {\n display: flex;\n align-items: center;\n gap: 7px;\n padding-left: 10px;\n border-left: 1px solid #e2e8f0;\n}\n\n.kg-layout-label {\n color: #64748b;\n font-size: 11px;\n font-weight: 650;\n white-space: nowrap;\n}\n\n.kg-layout-select {\n width: 128px;\n min-height: 31px;\n padding: 5px 26px 5px 9px;\n border: 1px solid #cbd5e1;\n border-radius: 6px;\n background: #fff;\n color: #1e293b;\n font-size: 12px;\n outline: none;\n}\n\n.kg-layout-select:hover,\n.kg-layout-select:focus {\n border-color: #60a5fa;\n box-shadow: 0 0 0 2px #dbeafe;\n}\n\n.kg-toggle {\n padding: 4px 8px;\n background: transparent;\n color: #64748b;\n border: none;\n border-radius: 4px;\n font-size: 11px;\n}\n\n.kg-toggle:hover {\n background: #e2e8f0;\n color: #0f172a;\n}\n\n@media (max-width: 640px) {\n .kg-toolbar {\n max-width: calc(100% - 24px);\n }\n\n .kg-btn-group {\n gap: 4px;\n }\n\n .kg-btn {\n padding: 6px 8px;\n }\n\n .kg-btn-label {\n display: none;\n }\n\n .kg-layout-control {\n padding-left: 6px;\n gap: 4px;\n }\n\n .kg-layout-label {\n display: none;\n }\n\n .kg-layout-select {\n width: 120px;\n }\n}\n</style>\n","import type { App } from 'vue'\r\nimport GraphVisualization from './components/GraphVisualization.vue'\r\nimport GraphToolbar from './components/GraphToolbar.vue'\r\nimport { GRAPH_LAYOUT_OPTIONS, KnowledgeGraph } from './module/g6Graph'\r\n\r\n// 导出类型\r\nexport type { GraphNode, GraphEdge } from './types'\r\nexport type { GraphInitOptions } from './module/g6Graph'\r\nexport type {\r\n GraphLayoutCategory,\r\n GraphLayoutOption,\r\n LayoutType,\r\n} from './module/g6Graph'\r\n\r\n// Vue 插件 install 方法\r\nconst install = (app: App) => {\r\n app.component('GraphVisualization', GraphVisualization)\r\n app.component('GraphToolbar', GraphToolbar)\r\n}\r\n\r\n// 具名导出(ES Modules)\r\nexport {\r\n install,\r\n GRAPH_LAYOUT_OPTIONS,\r\n GraphVisualization,\r\n GraphToolbar,\r\n KnowledgeGraph,\r\n}\r\n\r\n// 自动导出(UMD)- 默认导出\r\nexport default {\r\n install,\r\n version: __PACKAGE_VERSION__,\r\n}\r\n"],"mappings":"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"}