gpt4 book ai didi

javascript - Visjs 使用 Canvas 外的按钮动态添加节点

转载 作者:行者123 更新时间:2023-12-04 13:59:46 28 4
gpt4 key购买 nike

我正在尝试这样做
http://visjs.org/examples/network/data/dynamicData.html

但我无法使功能正常工作。我在 Angular 6。
我希望能够在 Canvas 外添加一个带有按钮的新节点,并且当我在将其添加到 Canvas 之前单击添加以获得输入字段时

单击添加时出现此错误 = 无法读取未定义的属性“添加”

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Network, DataSet, Node, Edge, IdType } from 'vis';
declare var vis

@Component({
selector: 'app-micro-grid-management',
templateUrl: './micro-grid-management.component.html',
styleUrls: ['./micro-grid-management.component.css']
})
export class MicroGridManagement implements OnInit {

constructor(private router: Router, private route: ActivatedRoute) {

}

public nodes: Node;
public edges: Edge;
public network: Network;
ngOnInit() {
let nodes, edges, network
nodes = new vis.DataSet();
nodes.on('*', function () {
document.getElementById('nodes').innerHTML = JSON
.stringify(nodes.get(), null, 4);
});
nodes.add([
{
id: 1,
label: 'PV Panels',
currentP: '100 kW',
setpp: '100 kW',
currentq: '2 kVar',
setq: '2 kVar',
shape: 'square', color: '#ef6c00'
},
{
id: 2,
label: 'Wind Turbines',
currentP: '100 kW',
setpp: '100 kW',
currentq: '2 kVar',
setq: '2 kVar',
shape: 'square', color: '#ef5350'
},
{
id: 3,
label: 'Gensets',
currentP: '100 kW',
setpp: '100 kW',
currentq: '2 kVar',
setq: '2 kVar',
shape: 'square', color: '#00bfa5',
font: { strokeWidth: 1, strokeColor: 'white' }
},
{
id: 4,
label: 'Battery',
currentP: '100 kW',
setpp: '100 kW',
currentq: '2 kVar',
setq: '2 kVar',
shape: 'square', color: '#c2185b'
},
{
id: 5,
label: 'Fuelscells',
currentP: '100 kW',
setpp: '100 kW',
currentq: '2 kVar',
setq: '2 kVar',
shape: 'square', color: '#5c6bc0'
}
]);

edges = new vis.DataSet();
edges.on('*', function () {
document.getElementById('edges').innerHTML = JSON
.stringify(edges.get(), null, 4);
});
edges.add([
{ id: '1', from: '1', to: '2' },
{ id: '2', from: '1', to: '3' },
{ id: '3', from: '2', to: '4' },
{ id: '4', from: '2', to: '5' }
]);

let container = document.getElementById('network');
let data = {
nodes: nodes,
edges: edges
};

let options = {};
network = new vis.Network(container, data, options);
}

toJSON(obj) {
return JSON.stringify(obj, null, 4);
}
addNode() {
try{
this.nodes.add({
id: document.getElementById('node-id'),
label: document.getElementById('node-label')
});
}
catch (err) {
alert(err);
}
}

updateNode() {
id: document.getElementById('node-id');
label: document.getElementById('node-label');
}
}
<table>
<tr>
<td>
<h2>Node</h2>
<table>
<tr>
<td></td>
<td><label for="node-id">Id</label></td>
<td><input id="node-id" type="text" value="6"></td>
</tr>
<tr>
<td></td>
<td><label for="node-label">Label</label></td>
<td><input id="node-label" type="text" value="Node 6"></td>
</tr>
<tr>
<td></td>
<td>Action</td>
<td>
<button id="node-add" onclick="addNode();">Add</button>
<button id="node-update" (click)="updateNode();">Update</button>
<button id="node-remove" (click)="removeNode();">Remove</button>
</td>
</tr>
</table>
</td>
<td>
<h2>Edge</h2>
<table>
<tr>
<td></td>
<td><label for="edge-id">Id</label></td>
<td><input id="edge-id" type="text" value="5"></td>
</tr>
<tr>
<td></td>
<td><label for="edge-from">From</label></td>
<td><input id="edge-from" type="text" value="3"></td>
</tr>
<tr>
<td></td>
<td><label for="edge-to">To</label></td>
<td><input id="edge-to" type="text" value="4"></td>
</tr>
<tr>
<td></td>
<td>Action</td>
<td>
<button id="edge-add" (click)="addEdge();">Add</button>
<button id="edge-update" (click)="updateEdge();">Update</button>
<button id="edge-remove" (click)="removeEdge();">Remove</button>
</td>
</tr>
</table>
</td>
</tr>

</table>

<h1>View</h1>
<table class="view">
<colgroup>
<col width="25%">
<col width="25%">
<col width="50%">
</colgroup>
<tr>
<td>
<h2>Nodes</h2>
<pre id="nodes"></pre>
</td>

<td>
<h2>Edges</h2>
<pre id="edges"></pre>
</td>

<td>
<h2>Network</h2>

<div id="network"></div>
</td>
</tr>
</table>

最佳答案

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { Network, DataSet, Options } from 'vis';
import { BehaviorSubject } from 'rxjs';
import { RouterUIModel } from '../../../router/routers/router.model';
import { Circuit } from '../../../circuit/circuits/circuit.model';
import { RouterService } from '../../../router/routers/router.service';
import { CircuitService } from '../../../circuit/circuits/circuit.service';
import { MatDialog } from '@angular/material';
import { SaveGraphComponent } from '../save-graph/save-graph.component';
import { CircuitGraphModel, NetworkGraph, RouterGraphModel, NetworkModel, PositionJson } from '../network-graph.model';
import { GetSavedGraphComponent } from '../get-saved-graphs/get-saved-graphs.component';
import { NetworkGraphService } from '../network-graph.service';
import { TranslateService } from '@ngx-translate/core';
import { LayoutUtilsService } from '../../../../../../core/services/layout-utils.service';
import { AggregationDefinition } from '../../../circuit/aggregation-definition/aggregation-definition.model';
import { AggregationDefinitionService } from '../../../circuit/aggregation-definition/aggregation-definition.service';
import { AlertifyService } from '../../../../../../core/services/alertify.service';

@Component({
selector: 'network-graph-list',
templateUrl: './network-graph-list.component.html',
styleUrls: ['./network-graph-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NetworkGraphListComponent implements OnInit {
network: Network = null;
nodes: any = new DataSet();
edges: any = new DataSet();

fromRouterId: number = 0;
fromRouterNode: RouterUIModel = new RouterUIModel();

toRouterId: number = 0;
toRouterNode: RouterUIModel = new RouterUIModel();

routerNode: RouterUIModel = new RouterUIModel();

routerSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
changeSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);

circuitToAggSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
aggToCircuitSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);

circuitEdgeId: number = 0;
circuitEdge: Circuit = new Circuit();

aggDefId: number = 0;
aggDef: AggregationDefinition;

networkGraphId: number = 0;

container: any;

constructor(
private cdr: ChangeDetectorRef,
private routerService: RouterService,
private circuitService: CircuitService,
private dialog: MatDialog,
private translate: TranslateService,
private networkGraphService: NetworkGraphService,
private layoutUtilsService: LayoutUtilsService,
private aggDefService: AggregationDefinitionService,
private alertifyService: AlertifyService
) {
}

ngOnInit(): void {
this.routerSubject.subscribe((routerSubjectId: number) => {
this.routerService.getById(routerSubjectId).subscribe(res => {
this.routerNode = res;
});
});

this.draw();
this.cdr.detectChanges();
}

draw(positions?: PositionJson[]) {
try {
this.container = document.getElementById("NetworkGraph")!;
var options = {
physics: {
enabled: false
},
interaction: {
hover: true
}
};
this.network = new Network(this.container, {nodes: this.nodes,edges: this.edges}, options);

if(positions != null){
for(var position of positions){
this.network.moveNode(position.nodeId,position.nodeX,position.nodeY);
}
}

this.network.setOptions({
});

// this.network.on("selectNode", function (params) {
// console.log("selectNode Event:", params);
// });
// this.network.on("selectEdge", function (params) {
// console.log("selectEdge Event:", params);
// });
// this.network.on("deselectNode", function (params) {
// console.log("deselectNode Event:", params);
// });
// this.network.on("deselectEdge", function (params) {
// console.log("deselectEdge Event:", params);
// });

this.network.on("hoverNode", (res) => {
this.showTooltip(res);
});

this.network.on("blurNode", (res) => {
this.removeTooltip(res);
});
}
catch(err){
console.log(err);
}

this.cdr.detectChanges();
}

showTooltip(res){
this.container.appendChild(this.makeUlTooltip(["abc","def"]));
this.container.innerHtml += this.makeUlTooltip(["abc","def"]);
}

removeTooltip(res){
document.getElementById("nodeTooltip").remove();
}

makeUlTooltip(array: string[]) {
var list = document.createElement('ul');
list.style.backgroundColor = "green";
list.style.display = "block";
list.style.position = "absolute";
list.id = "nodeTooltip";
list.style.zIndex = "100";

for (var i = 0; i < array.length; i++) {
var item = document.createElement('li');
item.appendChild(document.createTextNode(array[i]));
list.appendChild(item);
}

return list;
}

addNode() {
try {
if(this.routerNode === null){
this.alertifyService.error("Please Select Router.");
return;
}
else if(!Object.values(this.nodes._data as Object).some(o => o.id === this.routerNode.id)) {
this.nodes.add({
id: this.routerNode.id,
label: this.routerNode.name,
shape: "circle",
color: "#3F51B5",
font: { size: 16, color: "white", face: "arial" },
margin: { top: 15, right: 15, bottom: 15, left: 15 },
});

this.nextRouterSubjectFunction();
this.nextChangeSubjectFunction();
}
else {
this.alertifyService.error("Router is in the Graphic.");
return;
}

} catch (err) {
console.log(err);
}

this.cdr.detectChanges();
}

removeNode() {
try {
if(this.routerNode == null){
this.alertifyService.error("Router Not Selected.");
return;
}
if(!Object.values(this.nodes._data as Object).some(o => o.id === this.routerNode.id)){
this.alertifyService.error("Router Not Found.");
return;
}
else {
this.nodes.remove({ id: this.routerNode.id });

for(var releatedFromNodeEdge of Object.values(this.edges._data as Object).filter(o => o.from === this.routerNode.id)){
this.edges.remove({ id: releatedFromNodeEdge.id });
}
for(var releatedToNodeEdge of Object.values(this.edges._data as Object).filter(o => o.to === this.routerNode.id)){
this.edges.remove({ id: releatedToNodeEdge.id });
}

this.draw(this.preparePosition());
this.nextRouterSubjectFunction();
this.nextChangeSubjectFunction();
}
} catch (err) {
console.log(err);
}

this.cdr.detectChanges();
}

addEdge() {
try {
if(Object.keys(this.circuitEdge).length === 0){
this.alertifyService.error("Please Select Circuit.");
return;
}
else if(!Object.values(this.edges._data as Object).some(o => o.id === this.circuitEdge.id)){
this.edges.add({
id: this.circuitEdge.id,
from: this.fromRouterNode.id,
to: this.toRouterNode.id,
length: 400,
arrows: { from: { enabled: false, scaleFactor : 1}, to: { enabled: true, scaleFactor: 0.5 }},
label: this.circuitEdge.name+"\nInput Bps: "+this.circuitEdge.dataUtilization.inputBps+" bps"+"\nOutput Bps: "+this.circuitEdge.dataUtilization.outputBps+" bps",
color: { color: "red" },
type: this.circuitEdge.type
});
}
else {
this.alertifyService.error("Circuit is in the Graphic.");
return;
}
} catch (err) {
console.log(err);
}
this.cdr.detectChanges();
}

removeEdge() {
try {
if(Object.keys(this.circuitEdge).length === 0){
this.alertifyService.error("Please Select Circuit.");
return;
}
if(!Object.values(this.edges._data as Object).some(o => o.id === this.circuitEdge.id)){
this.alertifyService.error("Circuit Not Found.");
return;
}
else {
this.edges.remove({ id: this.circuitEdge.id });
this.draw(this.preparePosition());
}
} catch (err) {
console.log(err);
}
this.cdr.detectChanges();
}

saveGraph(){
if(this.networkGraphId > 0){
this.alertifyService.error("Network Graph Already Saved.");
return;
}
var circuitGraphModels: CircuitGraphModel[] = [];
for(var circuit of Object.values(this.edges._data as Object)){
var circuitGraphModel: CircuitGraphModel = new CircuitGraphModel();
circuitGraphModel.id = circuit.id;
circuitGraphModel.label = circuit.label;
circuitGraphModel.to = circuit.to;
circuitGraphModel.from = circuit.from;
circuitGraphModel.arrows = JSON.stringify(circuit.arrows);
circuitGraphModel.type = circuit.type;
circuitGraphModels.push(circuitGraphModel);
}

var routerGraphModels: RouterGraphModel[] = [];
for(var router of Object.values(this.nodes._data as Object)){
var routerGraphModel: RouterGraphModel = new RouterGraphModel();
routerGraphModel.id = router.id;
routerGraphModel.label = router.label;
routerGraphModels.push(routerGraphModel);
}

var networkModel: NetworkModel = new NetworkModel();
networkModel.clear();
networkModel.circuitGraphModels = circuitGraphModels;
networkModel.routerGraphModels = routerGraphModels;
networkModel.positionJsons = this.preparePosition();

var networkGraph: NetworkGraph = new NetworkGraph();
networkGraph.clear();
networkGraph.networkModel = networkModel;

const dialogRef = this.dialog.open(SaveGraphComponent, { width: "500px", data: { networkGraph } });
dialogRef.afterClosed().subscribe(res => {
if(res === 0){
return;
}
else if(res.Data === 0){
return;
}
else if(res.data > 0){
this.networkGraphId = res.data;
}
else {
return;
}
});
}

updateGraph(){
if(this.networkGraphId === 0){
this.alertifyService.error("Please Save Graph");
return;
}
var circuitGraphModels: CircuitGraphModel[] = [];
for(var circuit of Object.values(this.edges._data as Object)){
var circuitGraphModel: CircuitGraphModel = new CircuitGraphModel();
circuitGraphModel.id = circuit.id;
circuitGraphModel.label = circuit.label;
circuitGraphModel.to = circuit.to;
circuitGraphModel.from = circuit.from;
circuitGraphModel.arrows = JSON.stringify(circuit.arrows);
circuitGraphModel.type = circuit.type;
circuitGraphModels.push(circuitGraphModel);

}

var routerGraphModels: RouterGraphModel[] = [];
for(var router of Object.values(this.nodes._data as Object)){
var routerGraphModel: RouterGraphModel = new RouterGraphModel();
routerGraphModel.id = router.id;
routerGraphModel.label = router.label;
routerGraphModels.push(routerGraphModel);
}

var networkModel: NetworkModel = new NetworkModel();
networkModel.clear();
networkModel.circuitGraphModels = circuitGraphModels;
networkModel.routerGraphModels = routerGraphModels;
networkModel.positionJsons = this.preparePosition();

var networkGraph: NetworkGraph = new NetworkGraph();
networkGraph.clear();
networkGraph.id = this.networkGraphId;
networkGraph.networkModel = networkModel;
this.networkGraphService.getById(networkGraph.id).subscribe(ng => {
networkGraph.name = ng.name;
const dialogRef = this.dialog.open(SaveGraphComponent, { width: "500px", data: { networkGraph } });
dialogRef.afterClosed().subscribe(res => {
if(res === 0){
return;
}
else if(res.Data === 0){
return;
}
else if(res.data > 0){
this.networkGraphId = res.data;
}
else {
return;
}
});
});
}

getSavedGraphs(){
const dialogRef = this.dialog.open(GetSavedGraphComponent, { width: "500px", data: { } });
dialogRef.afterClosed().subscribe(networkGraph => {
if(networkGraph === 0){
return;
}
else {
this.networkGraphId = networkGraph.id;
this.nextChangeSubjectFunction();
this.nodes = new DataSet();
for(var routerNode of networkGraph.networkModel.routerGraphModels){
this.nodes.add({
id: routerNode.id,
label: routerNode.label,
shape: "circle",
color: "#3F51B5",
font: { size: 16, color: "white", face: "arial" },
margin: { top: 15, right: 15, bottom: 15, left: 15 }
});
}

this.edges = new DataSet();
for(var circuitEdge of networkGraph.networkModel.circuitGraphModels){
this.edges.add({
id: circuitEdge.id,
from: circuitEdge.from,
to: circuitEdge.to,
length: 400,
arrows: JSON.parse(circuitEdge.arrows),
label: circuitEdge.label,
color: { color: "red" },
type: circuitEdge.type
});
}
}
this.draw(networkGraph.networkModel.positionJsons);
this.nextChangeSubjectFunction();
});
}

preparePosition(): PositionJson[] {
var positionJsons: PositionJson[] = [];
for(var position of Object.keys(this.network.getPositions() as Object)){
var positionJson: PositionJson = new PositionJson();
positionJson.nodeId = Number(position);
positionJson.nodeX = this.network.getPositions()[position].x;
positionJson.nodeY = this.network.getPositions()[position].y;
positionJsons.push(positionJson);
}
return positionJsons;
}

deleteGraph(){
const _title: string = this.translate.instant('COMMON.DELETE.TITLE');
const _description: string = this.translate.instant('COMMON.DELETE.DESC');
const _waitDescription: string = this.translate.instant('COMMON.DELETE.WAIT_DESC');

const dialogRef = this.layoutUtilsService.deleteElement(_title, _description, _waitDescription);

dialogRef.componentInstance.yesClick.subscribe((res) => {
this.networkGraphService.delete(this.networkGraphId).subscribe(() => {
if(this.networkGraphId > 0){
dialogRef.close();
this.networkGraphId = 0;
this.nodes = new DataSet();
this.edges = new DataSet();
this.draw();
this.nextChangeSubjectFunction();
}
else {
dialogRef.close();
this.alertifyService.error("Network Graph Not Saved.");
}
});
});
}

refreshGraph(){
this.networkGraphId = 0;
this.nodes = new DataSet();
this.edges = new DataSet();
this.draw();
this.nextChangeSubjectFunction();
}

nextRouterSubjectFunction(){
this.routerSubject.next(0);
}

nextChangeSubjectFunction(){
this.changeSubject.next(0);
}

changeCircuitEdge(){
this.nextAggToCircuitSubjectFunction();
this.circuitService.getCircuitById(this.circuitEdgeId).subscribe(res => {
this.circuitEdge = res;
this.circuitEdge.type = "Circuit";
});
}

changeAggregationDefinition(){
this.nextCircuitToAggSubjectFunction();
this.aggDefService.getById(this.aggDefId).subscribe(res => {
this.circuitEdge = res;
this.circuitEdge.type = "Aggregation";
});
}

changeFromRouter(){
this.routerService.getById(this.fromRouterId).subscribe(res => {
this.fromRouterNode = res;
});
}

changeToRouter(){
this.routerService.getById(this.toRouterId).subscribe(res => {
this.toRouterNode = res;
});
}

nextCircuitToAggSubjectFunction(){
this.circuitToAggSubject.next(0);
}

nextAggToCircuitSubjectFunction(){
this.aggToCircuitSubject.next(0);
}
}
html,
body {
font: 11pt arial;
}

h1 {
font-size: 150%;
margin: 5px 0;
}

h2 {
font-size: 100%;
margin: 5px 0;
}

table.view {
width: 100%;
}

table td {
vertical-align: top;
}

table table {
background-color: #f5f5f5;
border: 1px solid #e5e5e5;
}

table table td {
vertical-align: middle;
}

input[type="text"],
pre {
border: 1px solid lightgray;
}

pre {
margin: 0;
padding: 5px;
font-size: 10pt;
}

#NetworkGraph {
width: 100%;
height: 700px;
}
<ng-container mPortletHeadTitle>
<div class="m-portlet__head-title">
<h4 class="m-portlet__head-text">
<span translate="Network Graph"></span>
</h4>
</div>
</ng-container>
<br />
<div class="row">
<div class="col-xl-2">
<div class="mat-table__wrapper">
<tbody>
<tr>
<td>
<table>
<thead>
<tr>
<th>
<h6 class="m-portlet__head-text">
<span translate="Routers"></span>
</h6>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<router-subject-autocomplete
[modelSubject]="routerSubject"
[placeholder]="'Router'"
></router-subject-autocomplete>
</td>
</tr>
<tr>
<td>
<button
id="node-add"
(click)="addNode()"
mat-raised-button
matTooltip="{{ 'Add' | translate }}"
[style.background-color]="'#E60000'"
[style.color]="'white'"
type="button"
>
<span translate="Add"></span>
</button>
&nbsp;
<button
id="node-remove"
(click)="removeNode()"
mat-raised-button
matTooltip="{{
'Delete' | translate
}}"
[style.background-color]="'#E60000'"
[style.color]="'white'"
type="button"
>
<span translate="Delete"></span>
</button>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<br />
<hr />
<br />
<tr>
<td>
<table>
<thead>
<tr>
<th>
<h6 class="m-portlet__head-text">
<span translate="Circuits"></span>
</h6>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<circuit-autocomplete
[(model)]="circuitEdgeId"
[circuitToAggSubject]="circuitToAggSubject"
(modelChange)="changeCircuitEdge()"
></circuit-autocomplete>
</td>
</tr>
<tr>
<td>
<agg-def-single-autocomplete
[(model)]="aggDefId"
[aggToCircuitSubject]="aggToCircuitSubject"
(modelChange)="
changeAggregationDefinition()
"
></agg-def-single-autocomplete>
</td>
</tr>
<tr>
<td>
<router-node-autocomplete
[(model)]="fromRouterId"
[changeSubject]="changeSubject"
(modelChange)="changeFromRouter()"
[(nodes)]="nodes"
[placeholder]="'From Router'"
></router-node-autocomplete>
</td>
</tr>
<tr>
<td>
<router-node-autocomplete
[(model)]="toRouterId"
[changeSubject]="changeSubject"
(modelChange)="changeToRouter()"
[(nodes)]="nodes"
[placeholder]="'To Router'"
></router-node-autocomplete>
</td>
</tr>
<tr>
<td>
<button
id="edge-add"
(click)="addEdge()"
mat-raised-button
matTooltip="{{ 'Add' | translate }}"
[style.background-color]="'#E60000'"
[style.color]="'white'"
type="button"
>
<span translate="Add"></span>
</button>
&nbsp;
<button
id="edge-remove"
(click)="removeEdge()"
mat-raised-button
matTooltip="{{
'Delete' | translate
}}"
[style.background-color]="'#E60000'"
[style.color]="'white'"
type="button"
>
<span translate="Delete"></span>
</button>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</div>
</div>

<div class="col-xl-10">
<h1>Network</h1>
<table class="view" style="background-color: white">
<colgroup>
<col width="50%" />
</colgroup>
<tbody>
<tr>
<td>
<div style="padding: 15px">
<button
(click)="saveGraph()"
mat-icon-button
matTooltip="{{ 'Save Graph' | translate }}"
color="primary"
type="button"
>
<mat-icon>create</mat-icon></button
>&nbsp;
<button
(click)="updateGraph()"
mat-icon-button
matTooltip="{{ 'Update Graph' | translate }}"
color="primary"
type="button"
>
<mat-icon>update</mat-icon></button
>&nbsp;
<button
mat-icon-button
color="primary"
matTooltip="{{ 'saved graphs' | translate }}"
(click)="getSavedGraphs()"
>
<mat-icon>file_copy</mat-icon></button
>&nbsp;
<button
mat-icon-button
color="warn"
matTooltip="{{
'COMMON.BUTTONS.DELETE' | translate
}}"
type="button"
(click)="deleteGraph()"
>
<mat-icon>delete</mat-icon>
</button>
&nbsp;
<button
mat-icon-button
color="primary"
matTooltip="{{
'refresh' | translate
}}"
type="button"
(click)="refreshGraph()"
>
<mat-icon>sync</mat-icon>
</button>
&nbsp;
</div>
<div id="NetworkGraph"></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>

关于javascript - Visjs 使用 Canvas 外的按钮动态添加节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51982634/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com