kre-form
Version:
本项目由 Angular8+ 编写的表单设计器和表单应用,所有的配置都是 JSON 结构体组成,PC 端是基于 ANT 组件库封装
604 lines (598 loc) • 21.2 kB
JavaScript
import { Graph, Shape, Addon } from '@antv/x6';
import { NzMessageService } from 'ng-zorro-antd/message';
import { Component, Input, Output, EventEmitter, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class DataFlowComponent {
/**
* @param {?} message
*/
constructor(message) {
this.message = message;
this.drawerEvent = new EventEmitter();
this.modalEvent = new EventEmitter();
this.data = {
// 节点
nodes: [
{
id: "node1",
// String,可选,节点的唯一标识
x: 40,
// Number,必选,节点位置的 x 值
y: 40,
// Number,必选,节点位置的 y 值
width: 80,
// Number,可选,节点大小的 width 值
height: 40,
// Number,可选,节点大小的 height 值
label: "hello",
},
{
id: "node2",
// String,节点的唯一标识
x: 160,
// Number,必选,节点位置的 x 值
y: 180,
// Number,必选,节点位置的 y 值
width: 80,
// Number,可选,节点大小的 width 值
height: 40,
// Number,可选,节点大小的 height 值
label: "world",
},
],
// 边
edges: [
{
source: "node1",
// String,必须,起始节点 id
target: "node2",
},
],
};
this.title = "my-app";
}
/**
* @return {?}
*/
ngOnInit() {
this.graphShape();
this.grahpInit();
this.getHistoryGrahp();
this.graphEvent();
// console.log("节点", this.graph.getNodes());
// console.log("cell-----");
// console.log("节点和边", this.graph.getCells());
// console.log("根节点,即没有输入边的节点", this.graph.getRootNodes());
// console.log("叶子节点,即没有输出边的节点", this.graph.getLeafNodes());
// console.log("数量", this.graph.getCellCount());
// console.log(this.graph);
// console.log(this.stencil.graphs.group1.getNodes());
/** @type {?} */
let nodes = this.graph.getLeafNodes();
for (let i = 0; i < nodes.length; i++) {
/** @type {?} */
let node = nodes[i];
if (node["label"] !== "输出") {
this.message.error("界面必须包含输出");
break;
}
}
}
/**
* @return {?}
*/
grahpInit() {
/** @type {?} */
const that = this;
// #region 初始化画布
this.graph = new Graph({
container: (/** @type {?} */ (document.getElementById("graph-container"))),
grid: true,
history: true,
minimap: {
enabled: true,
container: (/** @type {?} */ (document.getElementById("minimap"))),
width: 198,
height: 198,
padding: 10,
},
mousewheel: {
enabled: true,
zoomAtMousePosition: true,
modifiers: "ctrl",
minScale: 0.5,
maxScale: 3,
},
connecting: {
router: "manhattan",
connector: {
name: "rounded",
args: {
radius: 8,
},
},
anchor: "center",
connectionPoint: "anchor",
allowBlank: false,
snap: {
radius: 20,
},
/**
* @return {?}
*/
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: "#A2B1C3",
strokeWidth: 2,
targetMarker: {
name: "block",
width: 12,
height: 8,
},
},
},
zIndex: 0,
});
},
// 在移动边的时候判断连接是否有效
/**
* @param {?} __0
* @return {?}
*/
validateConnection({ sourceView, targetView, sourceMagnet, targetMagnet, sourceCell, targetCell, }) {
if (targetMagnet && targetCell) {
/** @type {?} */
const incomingEdges = (/** @type {?} */ (that.graph)).getIncomingEdges(targetCell) || [];
if (incomingEdges.length >= (/** @type {?} */ (targetView)).cell.getData().out) {
that.message.error("超出out数量");
return false;
}
}
// 不允许连接到自己
if (sourceView === targetView) {
// that.message.error("不允许连接到自己");
return false;
}
// 只能从输出链接桩创建连接
if (!sourceMagnet ||
sourceMagnet.getAttribute("port-group") === "left") {
// that.message.error("只能从输出链接桩创建连接");
return false;
}
// 只能连接到输入链接桩
if (!targetMagnet ||
targetMagnet.getAttribute("port-group") === "right") {
// that.message.error("只能连接到输入链接桩");
return false;
}
return !!targetMagnet;
},
},
highlighting: {
magnetAdsorbed: {
name: "stroke",
args: {
attrs: {
fill: "#5F95FF",
stroke: "#5F95FF",
},
},
},
},
resizing: true,
rotating: true,
selecting: {
enabled: true,
rubberband: true,
showNodeSelectionBox: true,
},
snapline: true,
keyboard: true,
clipboard: true,
});
this.stencil = new Addon.Stencil({
target: this.graph,
stencilGraphWidth: 200,
stencilGraphHeight: 180,
collapsable: true,
groups: [
{
title: "数据处理",
name: "group1",
},
{
title: "输入输出",
name: "group2",
},
],
layoutOptions: {
columns: 2,
columnWidth: 80,
rowHeight: 55,
},
/**
* @param {?} currentnode
* @return {?}
*/
validateNode(currentnode) {
/** @type {?} */
const nodes = that.graph.getNodes();
if (nodes.length > 0) {
for (let i = 0; i < nodes.length; i++) {
/** @type {?} */
let node = nodes[i];
if (currentnode["label"] === "输出" && node["label"] === "输出") {
return false;
}
}
}
return true;
},
/**
* @param {?} node
* @return {?}
*/
getDropNode(node) {
/** @type {?} */
const size = node.size();
if (node["label"] === "输入") {
that.getModal(node["label"]);
}
if (node["label"] === "输出") {
r2.attr("body/fill", "#ccc");
}
return node.clone().size(size.width * 1.1, size.height * 1.1);
},
});
(/** @type {?} */ (document.getElementById("stencil"))).appendChild(this.stencil.container);
// #region 初始化图形
// 常规左右两个桩
/** @type {?} */
const ports = {
groups: {
right: {
position: "right",
attrs: {
circle: {
r: 4,
magnet: true,
stroke: "#5F95FF",
strokeWidth: 1,
fill: "#fff",
style: {
visibility: "hidden",
},
},
},
},
left: {
position: "left",
attrs: {
circle: {
r: 4,
magnet: true,
stroke: "#5F95FF",
strokeWidth: 1,
fill: "#fff",
style: {
visibility: "hidden",
},
},
},
},
},
items: [
{
id: "port1",
group: "left",
},
{
id: "port2",
group: "right",
},
],
};
// 输入只有左侧一个桩
/** @type {?} */
const ports2 = {
groups: {
left: {
position: "left",
attrs: {
circle: {
r: 4,
magnet: true,
stroke: "#5F95FF",
strokeWidth: 1,
fill: "#fff",
style: {
visibility: "hidden",
},
},
},
},
},
items: [
{
id: "port1",
group: "left",
},
],
};
// 输出只有右侧一个桩
/** @type {?} */
const ports3 = {
groups: {
right: {
position: "right",
attrs: {
circle: {
r: 4,
magnet: true,
stroke: "#5F95FF",
strokeWidth: 1,
fill: "#fff",
style: {
visibility: "hidden",
},
},
},
},
},
items: [
{
id: "port2",
group: "right",
},
],
};
/** @type {?} */
const r1 = this.graph.createNode({
shape: "custom-rect",
id: "in",
label: "输入",
ports: Object.assign({}, ports3),
data: {
in: 0,
out: 3,
},
});
/** @type {?} */
const r2 = this.graph.createNode({
shape: "custom-rect",
id: "out",
label: "输出",
ports: Object.assign({}, ports2),
data: {
in: 2,
out: 2,
},
});
/** @type {?} */
const nodeArr = [];
this.menuList.forEach((/**
* @param {?} element
* @param {?} i
* @return {?}
*/
(element, i) => {
nodeArr.push(this.graph.createNode({
shape: "custom-rect",
label: element.title,
ports: Object.assign({}, ports),
data: element.data,
}));
}));
this.stencil.load([r1, r2], "group1");
this.stencil.load([...nodeArr], "group2");
}
/**
* @return {?}
*/
graphEvent() {
// delete
this.graph.bindKey("backspace", (/**
* @return {?}
*/
() => {
/** @type {?} */
const cells = this.graph.getSelectedCells();
if (cells.length) {
this.graph.removeCells(cells);
}
}));
// 控制连接桩显示/隐藏
/** @type {?} */
const showPorts = (/**
* @param {?} ports
* @param {?} show
* @return {?}
*/
(ports, show) => {
for (let i = 0, len = ports.length; i < len; i = i + 1) {
ports[i].style.visibility = show ? "visible" : "hidden";
}
});
this.graph.on("node:mouseenter", (/**
* @return {?}
*/
() => {
/** @type {?} */
const container = (/** @type {?} */ (document.getElementById("graph-container")));
/** @type {?} */
const ports = (/** @type {?} */ (container.querySelectorAll(".x6-port-body")));
showPorts(ports, true);
}));
this.graph.on("node:mouseleave", (/**
* @return {?}
*/
() => {
/** @type {?} */
const container = (/** @type {?} */ (document.getElementById("graph-container")));
/** @type {?} */
const ports = (/** @type {?} */ (container.querySelectorAll(".x6-port-body")));
showPorts(ports, false);
}));
// this.graph.on(
// "edge:connected",
// ({ isNew, currentMagnet, currentCell }) => {}
// );
// this.graph.on("edge:added", ({ cell, index, options }) => {});
// this.graph.on(
// "edge:change:*",
// (args: {
// key: string; // 通过 key 来确定改变项
// current: any; // 当前值,类型根据 key 指代的类型确定
// previous: any; // 改变之前的值,类型根据 key 指代的类型确定
// options: any; // 透传的 options
// }) => {
// console.log("key:", args.key);
// }
// );
// this.graph.on("edge:changed", ({ edge, options }) => {});
this.graph.on("node:click", (/**
* @param {?} __0
* @return {?}
*/
({ cell, view }) => {
/** @type {?} */
let node = view.cell;
console.log(node.label);
if (node.label === "输入") {
this.getModal(node.label);
}
else {
this.getDrawer(node.label);
}
}));
}
/**
* @return {?}
*/
getHistoryGrahp() {
/** @type {?} */
let json = localStorage.getItem("graphJSON");
if (json) {
this.graph.fromJSON(JSON.parse(json));
}
}
/**
* @param {?} label
* @return {?}
*/
getModal(label) {
this.modalEvent.emit(label);
}
/**
* @param {?} label
* @return {?}
*/
getDrawer(label) {
this.drawerEvent.emit(label);
}
/**
* @return {?}
*/
graphShape() {
Graph.registerNode("custom-rect", {
inherit: "rect",
width: 66,
height: 30,
attrs: {
body: {
strokeWidth: 1,
stroke: "#eee",
fill: "#fff",
},
text: {
fontSize: 12,
color: "#262626",
},
},
}, true);
}
/**
* @return {?}
*/
handleZoomIn() {
this.graph.zoom(0.1);
console.log(this.graph.zoom());
}
/**
* @return {?}
*/
handleZoomOut() {
this.graph.zoom(-0.1);
}
/**
* @return {?}
*/
handleUndo() {
this.graph.history.undo();
}
/**
* @return {?}
*/
handleRedo() {
this.graph.history.redo();
}
/**
* @return {?}
*/
handleReal() {
this.graph.scale(1);
this.graph.centerContent();
}
/**
* @return {?}
*/
handleFit() {
this.graph.zoomToFit({ padding: 12 });
}
}
DataFlowComponent.decorators = [
{ type: Component, args: [{
selector: "data-flow",
template: "<div class=\"wrap\">\n <div class=\"main\">\n <div class=\"edit-container\">\n <div id=\"container\">\n <div id=\"stencil\"></div>\n <div class=\"graph-wrap\">\n <div class=\"toolbar-wrap\">\n <span (click)=\"handleZoomIn()\" class=\"toolbar-btn\">+</span>\n <span class=\"number\">{{ graph.zoom() | percent }}</span>\n <span (click)=\"handleZoomOut()\" class=\"toolbar-btn\">-</span>\n <span class=\"line\">|</span>\n <span\n (click)=\"handleUndo()\"\n [ngClass]=\"{ on: graph.history.canUndo() }\"\n class=\"toolbar-btn-off\"\n ><</span\n >\n <span\n (click)=\"handleRedo()\"\n [ngClass]=\"{ on: graph.history.canRedo() }\"\n class=\"toolbar-btn-off\"\n >></span\n >\n\n <span (click)=\"handleReal()\">\u5B9E\u9645\u5C3A\u5BF8</span>\n <span (click)=\"handleFit()\">\u9002\u5E94\u753B\u5E03</span>\n </div>\n <div id=\"graph-container\"></div>\n <div id=\"minimap\"></div>\n </div>\n </div>\n </div>\n <div class=\"board\"></div>\n </div>\n</div>\n",
styles: [":host #stencil{width:180px;height:100%;position:relative}:host .graph-wrap{width:calc(100% - 180px)}:host .graph-wrap .toolbar-wrap{height:42px;line-height:42px}:host .graph-wrap .toolbar-wrap span{color:#111;margin:0 10px}:host .graph-wrap .toolbar-wrap .number{display:inline-block;width:42px}:host .graph-wrap .toolbar-wrap .line{color:#eee}:host .graph-wrap .toolbar-wrap .toolbar-btn{cursor:pointer;font-weight:700;padding:0 10px}:host .graph-wrap .toolbar-wrap .toolbar-btn-off{font-weight:700;padding:0 10px;cursor:default;color:#eee}:host .graph-wrap .toolbar-wrap .on{cursor:pointer;color:#111}:host .graph-wrap #graph-container{width:100%;height:100%}:host .graph-wrap #minimap{position:absolute;right:1rem;top:1rem;background-color:#bc8f8f}:host #container{display:flex;height:500px}:host ::ng-deep .x6-widget-stencil-title{display:none}:host ::ng-deep .x6-widget-stencil.collapsable>.x6-widget-stencil-content{top:0}"]
}] }
];
/** @nocollapse */
DataFlowComponent.ctorParameters = () => [
{ type: NzMessageService }
];
DataFlowComponent.propDecorators = {
menuList: [{ type: Input }],
drawerEvent: [{ type: Output }],
modalEvent: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class DataFlowModule {
}
DataFlowModule.decorators = [
{ type: NgModule, args: [{
declarations: [DataFlowComponent],
imports: [CommonModule],
exports: [DataFlowComponent],
entryComponents: [DataFlowComponent],
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { DataFlowComponent, DataFlowModule };
//# sourceMappingURL=kre-form-abc-src-lib-data-flow.js.map