vue-safe-force-graph 2.0.49 → 2.0.51
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.
- package/README.md +232 -75
- package/lib/vue-safe-force-graph.js +21 -12
- package/lib/vue-safe-force-graph.umd.cjs +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,36 +1,30 @@
|
|
|
1
1
|
# vue-safe-force-graph
|
|
2
2
|
|
|
3
|
-
vue-safe-force-graph
|
|
3
|
+
vue-safe-force-graph 是一个使用 Vue3、d3.js 、 canvas 开发的可视化溯源关系图。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
|
-
You can install vue-safe-force-graph via npm:
|
|
5
|
+
## 安装
|
|
10
6
|
|
|
11
7
|
`npm install vue-safe-force-graph`
|
|
12
8
|
|
|
13
|
-
##
|
|
9
|
+
## 使用方式
|
|
10
|
+
可以在组件中引入 或者可以在全局引入注册组件。
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
### 方式一:组件中引入
|
|
13
|
+
在vue组件中引入Graph组件和样式:
|
|
16
14
|
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
import ForceGraph, { Graph, Render } from 'vue-safe-force-graph'
|
|
15
|
+
```
|
|
16
|
+
import { Graph } from 'vue-safe-force-graph'
|
|
20
17
|
import 'vue-safe-force-graph/lib/style.css'
|
|
21
|
-
const app = createApp(App)
|
|
22
|
-
app.use(ForceGraph, {
|
|
23
|
-
disableQapm: true, // ture或者false。 true则禁用 Qapm
|
|
24
|
-
})
|
|
25
18
|
```
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
完整index.vue示例如下:
|
|
28
21
|
|
|
29
22
|
```vue
|
|
30
23
|
<template>
|
|
31
|
-
<
|
|
24
|
+
<div class="graph-container">
|
|
25
|
+
<Graph
|
|
32
26
|
:graph-api="graphApi"
|
|
33
|
-
module="
|
|
27
|
+
:module="module"
|
|
34
28
|
:start-node="startNode"
|
|
35
29
|
:enter-start-node="true"
|
|
36
30
|
:has-level="true"
|
|
@@ -38,41 +32,93 @@ app.use(ForceGraph, {
|
|
|
38
32
|
:vcode="vcode"
|
|
39
33
|
:handle-select-node-event="handleSelectNode"
|
|
40
34
|
:custom-context-menu-event="handleEvent"
|
|
35
|
+
:singles-node-max-num="500"
|
|
41
36
|
@handle-data-loaded="handleDataLoaded"
|
|
42
37
|
/>
|
|
38
|
+
</div>
|
|
43
39
|
</template>
|
|
40
|
+
<script setup>
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
getGraphConfig, getGraphRequest, getNodesDetail, getSnapshotDetail,
|
|
44
|
+
} from 'src/api/graph'
|
|
45
|
+
import { ref, watch } from 'vue'
|
|
46
|
+
import { useRoute } from 'vue-router'
|
|
47
|
+
import request from '@/utils/request'
|
|
48
|
+
import { Graph } from 'vue-safe-force-graph'
|
|
49
|
+
import 'vue-safe-force-graph/lib/style.css'
|
|
50
|
+
|
|
51
|
+
const module = ref('graph')
|
|
52
|
+
const startNode = ref([])
|
|
53
|
+
const vcode = ref(null)
|
|
54
|
+
const snapshotId = ref(null)
|
|
55
|
+
const forceGraph = ref(null)
|
|
44
56
|
|
|
45
|
-
|
|
57
|
+
function addSnapshot(params) {
|
|
58
|
+
return request({
|
|
59
|
+
url: `/graph/v1/snapshot/add?module=${module.value}`,
|
|
60
|
+
method: 'post',
|
|
61
|
+
data: params
|
|
62
|
+
})
|
|
63
|
+
}
|
|
46
64
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
const graphApi = {
|
|
66
|
+
getGraphConfig: getGraphConfig,
|
|
67
|
+
getGraphRequest: getGraphRequest,
|
|
68
|
+
getNodesDetail: getNodesDetail,
|
|
69
|
+
addSnapshot1: addSnapshot,
|
|
70
|
+
getSnapshotDetail: getSnapshotDetail,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const route = useRoute()
|
|
74
|
+
|
|
75
|
+
snapshotId.value = route.query.snapshot_id
|
|
76
|
+
vcode.value = route.query.vcode
|
|
77
|
+
|
|
78
|
+
if (route.query.type && route.query.id) {
|
|
79
|
+
startNode.value = [
|
|
80
|
+
{
|
|
81
|
+
type: route.query.type,
|
|
82
|
+
id: route.query.id
|
|
54
83
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const handleHighLight = (type, prop, value) => {
|
|
88
|
+
console.log('forceGraph', forceGraph.value)
|
|
89
|
+
forceGraph.value.handleCustomSelect(type, prop, value)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const handleEvent = (data) => {
|
|
93
|
+
console.log('监听菜单事件', data)
|
|
94
|
+
}
|
|
95
|
+
const handleSelectNode = (data) => {
|
|
96
|
+
console.log('handleSelectNode', data)
|
|
97
|
+
}
|
|
98
|
+
function handleDataLoaded(data) {
|
|
99
|
+
console.log('handleDataLoaded', data)
|
|
71
100
|
}
|
|
72
101
|
</script>
|
|
102
|
+
<style lang="scss">
|
|
103
|
+
.graph-container {
|
|
104
|
+
height: calc(100vh - 66px);
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
73
107
|
```
|
|
74
108
|
|
|
75
|
-
|
|
109
|
+
|
|
110
|
+
### 方式二:全局引入
|
|
111
|
+
在main.js中引入并注册。
|
|
112
|
+
```js
|
|
113
|
+
// main.js
|
|
114
|
+
import { createApp } from 'vue'
|
|
115
|
+
import Graph from 'vue-safe-force-graph'
|
|
116
|
+
import 'vue-safe-force-graph/lib/style.css'
|
|
117
|
+
const app = createApp(App)
|
|
118
|
+
app.use(Graph)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
在自己的业务组件中使用,全局注册的组件名为`ForceGraphByCanvas`,示例如下 :
|
|
76
122
|
|
|
77
123
|
```vue
|
|
78
124
|
<template>
|
|
@@ -90,43 +136,152 @@ export default {
|
|
|
90
136
|
/>
|
|
91
137
|
</template>
|
|
92
138
|
|
|
93
|
-
<script>
|
|
94
|
-
|
|
95
|
-
|
|
139
|
+
<script setup>
|
|
140
|
+
// 此处js代码参考上面组件引入的实例
|
|
141
|
+
</script>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 组件属性
|
|
145
|
+
| 属性名 | 说明 | 类型 | 默认值 | 是否必填 |
|
|
146
|
+
| ------------------------ | ------------------------------------------------------------ | -------------- | ------------------------------------------------------------ | -------- |
|
|
147
|
+
| graph-api | 图数据相关接口,包含config、节点数据、节点详情接口 | Object | 无 | 是 |
|
|
148
|
+
| module | 当前图模块 用于 获取config接口时的传参。 this.graphApi?.getGraphConfig({name: this.module}).then(() => {}) | String | 无 | 是 |
|
|
149
|
+
| start-node | 开始节点,如 startNode = [{type: "ip",id: "193.36.119.50"}] | Array | 无 | 是 |
|
|
150
|
+
| enter-start-node | 起始节点时是否出现查询添加接口 | Boolean | false | 否 |
|
|
151
|
+
| has-level | 是否有level等级概念 | Boolean | true | 否 |
|
|
152
|
+
| snapshot_id | 快照id | String | 无 | 否 |
|
|
153
|
+
| vcode | 快照密码 | String | 无 | 否 |
|
|
154
|
+
| dark-mode | 是否为深色模式 | Boolean | false | 否 |
|
|
155
|
+
| showPoint | 图左侧的legend图例部分,是否需要`收藏 备注 标签 ` | Boolean | true | 否 |
|
|
156
|
+
| isShowRightMenu | 是否展示右侧详情 部分 | Boolean | true | 否 |
|
|
157
|
+
| hideButtonList | 需要隐藏的顶部按钮, 如 hideButtonList = ["新增节点"]。全部可选项为`[ "新增节点", "删除", "保存快照", "保存图片", "数据导出", "数据导入", "撤销", "恢复", "自动布局", "全局固定", "全局解除固定", "树型布局", "斥力布局","展开折叠", "功能介绍", ]` | Array | 无 | 否 |
|
|
158
|
+
| isRenderStartNode | 是否渲染startNode。 | Boolean | true | 否 |
|
|
159
|
+
| isDefaultSelectStartNode | 是否默认选中开始节点startNode | Boolean | true | 否 |
|
|
160
|
+
| graphUrlPath | 用于生成快照后的图页面的地址路径。 例如当生成快照成功后,会生成一个链接,`https://xxx.b.net/${graphUrlPath}?snapshot_id=xxx` | String | '/graph' | 否 |
|
|
161
|
+
| levelList | level危险级别 | Array | [ { level: '未知', // 级别。必传 levelName: '未知', // 必传。级别的名称 用于展示文案 className: 'unknown', // class样式名。必传 iconType: '', // 默认使用图标,传dot使用圆点样式 normalColor: '#666', // 颜色色值。必传 darkColor: '#c6cdd5', // 暗黑模式颜色色值,非必传。不传使用normalColor dangerNumber: 0, // 是否是 危险的局别。 用于统计危险数,对危险的节点增加样式等。和危险数的排序 }, { level: '危险', levelName: '危险', className: 'warn', iconType: '', // 默认使用图标,传dot使用圆点样式 normalColor: '#CF1322', darkColor: '#fb2400', dangerNumber: 1, }, { level: '普通', levelName: '普通', className: 'normal', iconType: '', // 默认使用图标,传dot使用圆点样式 normalColor: '#007EE2', darkColor: '#0094ff', dangerNumber: -1, }, { level: '安全', levelName: '安全', className: 'safe', iconType: '', // 默认使用图标,传dot使用圆点样式 normalColor: '#009A75', darkColor: '#00da90', dangerNumber: -2, }, ] | 否 |
|
|
162
|
+
| showLevelCount | 是否在左侧图例legend的leve图标l旁边展示相关节点的数量。 | Boolean | false | 否 |
|
|
163
|
+
| title | 在图的左上角设置标题,默认无 | String \| Html | 无 | 否 |
|
|
164
|
+
| isShowNodeMessage | 是否展示 渲染多少个节点的 message提示 | Boolean | true | 否 |
|
|
165
|
+
| nodeTypeScale | 图中关系节点图标大小的缩放比例。如缩小0.5倍则传数字0.5 | Number | 1 | 否 |
|
|
166
|
+
| singlesNodeMaxNum | 单个关系类型下的最大节点数量。如果节点数量超过此值,默认聚合隐藏起来 | Number | 100 | 否 |
|
|
167
|
+
| handleSelectNodeEvent | 选择节点事件的props(兼容angular调用,vue可以使用事件方式) | Function | 无 | 否 |
|
|
168
|
+
| customContextMenuEvent | 自定义右键菜单的点击事件,相关配置参考下面的[自定义右键菜单](#自定义右键菜单)。会返回数据 `{ eventName: 'api.name', customConfig: 'api.customConfig', nodes: 'nodes', allNodes: 'this.history.dataNow.nodes', }`(兼容angular调用,vue可以使用事件方式) | Function | 无 | 否 |
|
|
169
|
+
| handleDataLoaded | 图加载数据完成后 的事件通知。会返回图中所有的节点数据。(兼容angular调用,vue可以使用事件方式) | Function | 无 | 否 |
|
|
170
|
+
|
|
171
|
+
### 自定义右键菜单
|
|
172
|
+
|
|
173
|
+
在config接口中配置节点的右键菜单项,图组件拿到数据后渲染到页面上, 并绑定点击事件。点击后emit出去相关的数据。使用者监听该事件,然后自行处理后续流程。
|
|
96
174
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
175
|
+
菜单项配置在下面详情接口中,**data => records => info => extraNodeContextMenus字段**,如
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
{
|
|
179
|
+
'code': '0',
|
|
180
|
+
'msg': 'ok',
|
|
181
|
+
'data': {
|
|
182
|
+
'records': [
|
|
183
|
+
{
|
|
184
|
+
'summary': '',
|
|
185
|
+
'tags': [],
|
|
186
|
+
'info': {
|
|
187
|
+
// 额外右键菜单项
|
|
188
|
+
'extraNodeContextMenus': [
|
|
189
|
+
{
|
|
190
|
+
name: 'dealwith1', // 菜单唯一标识,用于点击事件传出去
|
|
191
|
+
namec: '菜单1 无children', // 菜单展示的文案
|
|
192
|
+
icon: 'button_edit_label', // 右键菜单的图标,不传的话默认一个图标
|
|
193
|
+
customConfig: { // 透传自定义配置
|
|
194
|
+
input: 'xxxxx',
|
|
195
|
+
a: 'ceshi'
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'dealwith2',
|
|
200
|
+
namec: '菜单2 无children',
|
|
201
|
+
customConfig: {
|
|
202
|
+
input: 'xxxxx',
|
|
203
|
+
a: 'ceshi'
|
|
204
|
+
},
|
|
205
|
+
// children 二级菜单
|
|
206
|
+
children: [
|
|
207
|
+
{
|
|
208
|
+
name: 'xxx',
|
|
209
|
+
namec: 'xxx',
|
|
210
|
+
customConfig: {
|
|
211
|
+
input: '333',
|
|
212
|
+
a: 'ces3333hi'
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: 'dealwxxith3',
|
|
219
|
+
namec: 'xxx',
|
|
220
|
+
children: [
|
|
221
|
+
{
|
|
222
|
+
name: 'xxx',
|
|
223
|
+
namec: 'xxx',
|
|
224
|
+
children: [
|
|
225
|
+
{
|
|
226
|
+
name: 'threelevel',
|
|
227
|
+
namec: '三级菜单'
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
'id': '1234',
|
|
235
|
+
'name': '123',
|
|
236
|
+
'type': 'url',
|
|
237
|
+
'basic': {
|
|
238
|
+
'level': '未知',
|
|
239
|
+
'check_time': '',
|
|
240
|
+
'desc': ''
|
|
241
|
+
},
|
|
242
|
+
'extends': null
|
|
243
|
+
},
|
|
244
|
+
'brief': []
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
'summary': '',
|
|
248
|
+
'tags': [],
|
|
249
|
+
'info': {
|
|
250
|
+
'id': '234234',
|
|
251
|
+
'name': '234234',
|
|
252
|
+
'type': 'url',
|
|
253
|
+
'basic': {
|
|
254
|
+
'level': '未知',
|
|
255
|
+
'check_time': '',
|
|
256
|
+
'desc': ''
|
|
257
|
+
},
|
|
258
|
+
'extends': null
|
|
259
|
+
},
|
|
260
|
+
'brief': []
|
|
261
|
+
}
|
|
262
|
+
]
|
|
107
263
|
}
|
|
108
|
-
},
|
|
109
|
-
methods: {
|
|
110
|
-
handleEvent(data) {
|
|
111
|
-
console.log('监听菜单事件', data)
|
|
112
|
-
},
|
|
113
|
-
handleDataLoaded(data) {
|
|
114
|
-
console.log('handleDataLoaded', data)
|
|
115
|
-
},
|
|
116
|
-
handleSelectNode(data, dataNow) {
|
|
117
|
-
console.log('dataNow', dataNow)
|
|
118
|
-
console.log('handleSelectNode', data)
|
|
119
|
-
},
|
|
120
|
-
handleHighLight(type, prop, value) {
|
|
121
|
-
// this.$refs.forceGraph.handleCustomSelect(type, prop, value)
|
|
122
|
-
},
|
|
123
264
|
}
|
|
124
|
-
}
|
|
125
|
-
</script>
|
|
126
265
|
```
|
|
127
266
|
|
|
267
|
+
## 事件
|
|
268
|
+
|
|
269
|
+
| 事件名 | 说明 | 示例 |
|
|
270
|
+
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
|
271
|
+
| handle-data-loaded | 图加载数据完成后 的事件通知。会返回图中所有的节点数据 | `<ForceGraphByCanvas :graph-api="graphApi" :module="module" :start-node="startNode" @handle-data-loaded="handleDataLoaded" @custom-contextmenu-click="handleContextmenu" @handle-select-node="handleSelectNode" />` |
|
|
272
|
+
| custom-contextmenu-click | 自定义右键菜单的点击事件。返回 **eventName** 事件名 、**customConfig**、 **当前点击的nodes** 节点的整个数据, 所有节点**allNodes** | 如上 |
|
|
273
|
+
| handle-select-node | 选择节点事件 | 如上 |
|
|
274
|
+
|
|
275
|
+
### 图标加载方式
|
|
128
276
|
|
|
129
|
-
|
|
277
|
+
图标icon图片有2中加载方式:1从cdn中加载,2打包为base64到npm包中加载。
|
|
278
|
+
|
|
279
|
+
图谱中的图标icon默认是从cdn链接中加载的。如果您存在私有化部署不能访问公网的场景,可以联系作者打包base64版本。
|
|
280
|
+
|
|
281
|
+
###
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
## 开发相关
|
|
130
285
|
|
|
131
286
|
### To run the development server
|
|
132
287
|
|
|
@@ -134,7 +289,7 @@ export default {
|
|
|
134
289
|
|
|
135
290
|
### To build the library
|
|
136
291
|
|
|
137
|
-
`npm run
|
|
292
|
+
`npm run lib`
|
|
138
293
|
|
|
139
294
|
### To publish a new version
|
|
140
295
|
|
|
@@ -145,3 +300,5 @@ export default {
|
|
|
145
300
|
### 开发人员手册
|
|
146
301
|
|
|
147
302
|
[开发请看这里](./开发请看这里.md)
|
|
303
|
+
|
|
304
|
+
如有问题请联系 jiangzhenxiang
|
|
@@ -16372,7 +16372,7 @@ function dl(e) {
|
|
|
16372
16372
|
return Ge(this, null, function* () {
|
|
16373
16373
|
return new Promise((t, n) => {
|
|
16374
16374
|
const r = new Image();
|
|
16375
|
-
r.onload = () => {
|
|
16375
|
+
r.crossOrigin = "anonymous", r.onload = () => {
|
|
16376
16376
|
t(r);
|
|
16377
16377
|
}, r.onerror = (s) => {
|
|
16378
16378
|
n(s);
|
|
@@ -27374,7 +27374,12 @@ const dle = {
|
|
|
27374
27374
|
svgToBlob() {
|
|
27375
27375
|
return Ge(this, null, function* () {
|
|
27376
27376
|
this.$parent.centerAndZoom(), this.$parent.addCanvasBg();
|
|
27377
|
-
const
|
|
27377
|
+
const e = this.$parent.getCanvasStage();
|
|
27378
|
+
e.find("Image").forEach((n) => {
|
|
27379
|
+
const r = n.image();
|
|
27380
|
+
r && (r.crossOrigin = "Anonymous");
|
|
27381
|
+
});
|
|
27382
|
+
const t = yield e.toBlob();
|
|
27378
27383
|
return this.$parent.removeCanvasBg(), this.$parent.reset(), t;
|
|
27379
27384
|
});
|
|
27380
27385
|
},
|
|
@@ -40302,7 +40307,7 @@ const EAe = /* @__PURE__ */ ln(BTe, [["render", xAe], ["__scopeId", "data-v-a8e2
|
|
|
40302
40307
|
utime: "2019-05-06 15:08:10",
|
|
40303
40308
|
children: []
|
|
40304
40309
|
}
|
|
40305
|
-
], AAe = "vue-safe-force-graph", kAe = "2.0.
|
|
40310
|
+
], AAe = "vue-safe-force-graph", kAe = "2.0.51", OAe = "force_graph in security area", NAe = "jason zhang", IAe = "./lib/vue-safe-force-graph.js", PAe = "./lib/vue-safe-force-graph.umd.cjs", RAe = "module", LAe = [
|
|
40306
40311
|
"/lib/"
|
|
40307
40312
|
], DAe = {
|
|
40308
40313
|
".": {
|
|
@@ -42418,7 +42423,7 @@ const dke = {
|
|
|
42418
42423
|
type: Boolean,
|
|
42419
42424
|
default: !0
|
|
42420
42425
|
},
|
|
42421
|
-
//
|
|
42426
|
+
// 是否默认选中startNode
|
|
42422
42427
|
isDefaultSelectStartNode: {
|
|
42423
42428
|
type: Boolean,
|
|
42424
42429
|
default: !0
|
|
@@ -43140,14 +43145,18 @@ const dke = {
|
|
|
43140
43145
|
inputErrorMessage: "请输入20字以内快照名称"
|
|
43141
43146
|
}).then((t) => Ge(this, [t], function* ({ value: e }) {
|
|
43142
43147
|
this.bigLoading = !0, setTimeout(() => Ge(this, null, function* () {
|
|
43143
|
-
|
|
43144
|
-
|
|
43145
|
-
|
|
43146
|
-
|
|
43147
|
-
|
|
43148
|
-
|
|
43149
|
-
|
|
43150
|
-
|
|
43148
|
+
try {
|
|
43149
|
+
const n = yield this.$refs.buttonList.svgToBlob(), r = new FormData();
|
|
43150
|
+
r.append("data", this.history.toString()), r.append("name", e), r.append("thumb", n), this.graphApi.addSnapshot1(r).then((s) => {
|
|
43151
|
+
this.snapshot = s.data, this.snapshot.name = e, this.$refs.snapshot.show();
|
|
43152
|
+
}).catch((s) => {
|
|
43153
|
+
this.onOutdate(s.res);
|
|
43154
|
+
}).finally((s) => {
|
|
43155
|
+
this.bigLoading = !1;
|
|
43156
|
+
});
|
|
43157
|
+
} catch (n) {
|
|
43158
|
+
this.bigLoading = !1, console.log(n);
|
|
43159
|
+
}
|
|
43151
43160
|
}), 100);
|
|
43152
43161
|
})).catch(() => {
|
|
43153
43162
|
on({
|