200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > vue+gojs 绘制鱼骨图(因果图)

vue+gojs 绘制鱼骨图(因果图)

时间:2023-11-10 22:39:27

相关推荐

vue+gojs 绘制鱼骨图(因果图)

最近查找js相关的鱼骨图组件,找了半天都没有合适的,自己参考gojs官网demo简单的实现了下,效果如下。

废话少说,直接上代码。

引入gojs

npm install gojs --save

整合到vue

fishbone.vue

<template><div><div id="gos" class="lean"></div><el-button @click="layoutFishbone">layoutFishbone</el-button><el-button @click="layoutBranching">layoutBranching</el-button><el-button @click="layoutNormal">layoutNormal</el-button></div></template><script>import go from 'gojs'import { FishboneLayout, FishboneLink } from '../assets/FishboneLayout.js';export default {data () {return {diagram: '',json: {'text': 'Incorrect Deliveries', 'size': 18, 'weight': 'Bold', 'causes': [{'text': 'Skills', 'size': 14, 'weight': 'Bold', 'causes': [{'text': 'knowledge', 'weight': 'Bold', 'causes': [{'text': 'procedures', 'causes': [{ 'text': 'documentation' }]},{ 'text': 'products' }]},{ 'text': 'literacy', 'weight': 'Bold' }]},{'text': 'Procedures', 'size': 14, 'weight': 'Bold', 'causes': [{'text': 'manual', 'weight': 'Bold', 'causes': [{ 'text': 'consistency' }]},{'text': 'automated', 'weight': 'Bold', 'causes': [{ 'text': 'correctness' },{ 'text': 'reliability' }]}]},{'text': 'Communication', 'size': 14, 'weight': 'Bold', 'causes': [{ 'text': 'ambiguity', 'weight': 'Bold' },{'text': 'sales staff', 'weight': 'Bold', 'causes': [{'text': 'order details', 'causes': [{ 'text': 'lack of knowledge' }]}]},{'text': 'telephone orders', 'weight': 'Bold', 'causes': [{ 'text': 'lack of information' }]},{'text': 'picking slips', 'weight': 'Bold', 'causes': [{ 'text': 'details' },{ 'text': 'legibility' }]}]},{'text': 'Transport', 'size': 14, 'weight': 'Bold', 'causes': [{'text': 'information', 'weight': 'Bold', 'causes': [{ 'text': 'incorrect person' },{'text': 'incorrect addresses', 'causes': [{'text': 'customer data base', 'causes': [{ 'text': 'not up-to-date' },{ 'text': 'incorrect program' }]}]},{ 'text': 'incorrect dept' }]},{'text': 'carriers', 'weight': 'Bold', 'causes': [{ 'text': 'efficiency' },{ 'text': 'methods' }]}]}]}}},mounted () {const $ = go.GraphObject.make;let _this = this;this.diagram = $(go.Diagram, 'gos', { isReadOnly: false })this.diagram.nodeTemplate =$(go.Node,$(go.TextBlock,new go.Binding('text'),new go.Binding('font', '', _this.convertFont)));this.diagram.linkTemplateMap.add('normal',$(go.Link,{ routing: go.Link.Orthogonal, corner: 4 },$(go.Shape)));this.diagram.linkTemplateMap.add('fishbone',$(FishboneLink, // defined above$(go.Shape)));const nodeDataArray = [];_this.walkJson(_this.json, nodeDataArray);this.diagram.model = new go.TreeModel(nodeDataArray);this.layoutFishbone();},methods: {convertFont (data) {let size = data.size;if (size === undefined)size = 13;let weight = data.weight;if (weight === undefined)weight = '';return weight + ' ' + size + 'px sans-serif';},walkJson (obj, arr) {const key = arr.length;obj.key = key;arr.push(obj);const children = obj.causes;if (children) {for (let i = 0; i < children.length; i++) {const o = children[i];o.parent = key;this.walkJson(o, arr);}}},layoutFishbone () {this.diagram.startTransaction('fishbone layout');this.diagram.linkTemplate = this.diagram.linkTemplateMap.getValue('fishbone');this.diagram.layout = go.GraphObject.make(FishboneLayout, {angle: 180,layerSpacing: 10,nodeSpacing: 20,rowSpacing: 10});mitTransaction('fishbone layout');},layoutBranching () {this.diagram.startTransaction('branching layout');this.diagram.linkTemplate = this.diagram.linkTemplateMap.getValue('normal');this.diagram.layout = go.GraphObject.make(go.TreeLayout, {angle: 180,layerSpacing: 20,alignment: go.TreeLayout.AlignmentBusBranching});mitTransaction('branching layout');},layoutNormal () {this.diagram.startTransaction('normal layout');this.diagram.linkTemplate = this.diagram.linkTemplateMap.getValue('normal');this.diagram.layout = go.GraphObject.make(go.TreeLayout, {angle: 180,breadthLimit: 1000,alignment: go.TreeLayout.AlignmentStart});mitTransaction('normal layout');}}}</script><style scoped>.lean {height: 500px;width: 90%;border: 1px solid black;background-color: #dae4e4;}</style>

FishboneLayout.js

import go from "gojs";/*** FishboneLayout is a custom {@link Layout} derived from {@link TreeLayout} for creating "fishbone" diagrams.* A fishbone diagram also requires a {@link Link} class that implements custom routing, {@link FishboneLink}.** This only works for angle === 0 or angle === 180.** This layout assumes Links are automatically routed in the way needed by fishbone diagrams,* by using the FishboneLink class instead of go.Link.** If you want to experiment with this extension, try the <a href="../../extensionsTS/Fishbone.html">Fishbone Layout</a> sample.* @category Layout Extension*/export class FishboneLayout extends go.TreeLayout {/*** Constructs a FishboneLayout and sets the following properties:* - {@link #alignment} = {@link TreeLayout.AlignmentBusBranching}* - {@link #setsPortSpot} = false* - {@link #setsChildPortSpot} = false*/constructor() {super();this.alignment = go.TreeLayout.AlignmentBusBranching;this.setsPortSpot = false;this.setsChildPortSpot = false;}/*** Create and initialize a {@link LayoutNetwork} with the given nodes and links.* This override creates dummy vertexes, when necessary, to allow for proper positioning within the fishbone.* @param {Diagram|Group|Iterable.<Part>} coll A {@link Diagram} or a {@link Group} or a collection of {@link Part}s.* @return {LayoutNetwork}*/makeNetwork(coll) {// assert(this.angle === 0 || this.angle === 180);// assert(this.alignment === go.TreeLayout.AlignmentBusBranching);// assert(this.path !== go.TreeLayout.PathSource);// call base method for standard behaviorconst net = super.makeNetwork(coll);// make a copy of the collection of TreeVertexes// because we will be modifying the TreeNetwork.vertexes collection in the loopconst verts = new go.List().addAll(net.vertexes.iterator);verts.each(function(v) {// ignore leaves of treeif (v.destinationEdges.count === 0) return;if (v.destinationEdges.count % 2 === 1) {// if there's an odd number of real children, add two dummiesconst dummy = net.createVertex();dummy.bounds = new go.Rect();dummy.focus = new go.Point();net.addVertex(dummy);net.linkVertexes(v, dummy, null);}// make sure there's an odd number of children, including at least one dummy;// commitNodes will move the parent node to where this dummy child node is placedconst dummy2 = net.createVertex();dummy2.bounds = v.bounds;dummy2.focus = v.focus;net.addVertex(dummy2);net.linkVertexes(v, dummy2, null);});return net;}/*** Add a direction property to each vertex and modify {@link TreeVertex#layerSpacing}.*/assignTreeVertexValues(v) {super.assignTreeVertexValues(v);v["_direction"] = 0; // add this property to each TreeVertexif (v.parent !== null) {// The parent node will be moved to where the last dummy will be;// reduce the space to account for the future hole.if (v.angle === 0 || v.angle === 180) {v.layerSpacing -= v.bounds.width;} else {v.layerSpacing -= v.bounds.height;}}}/*** Assigns {@link Link#fromSpot}s and {@link Link#toSpot}s based on branching and angle* and moves vertexes based on dummy locations.*/commitNodes() {if (work === null) return;// vertex Angle is set by BusBranching "inheritance";// assign spots assuming overall Angle === 0 or 180// and links are always connecting horizontal with work.edges.each(function(e) {const link = e.link;if (link === null) return;link.fromSpot = go.Spot.None;link.toSpot = go.Spot.None;const v = e.fromVertex;const w = e.toVertex;if (v.angle === 0) {link.fromSpot = go.Spot.Left;} else if (v.angle === 180) {link.fromSpot = go.Spot.Right;}if (w.angle === 0) {link.toSpot = go.Spot.Left;} else if (w.angle === 180) {link.toSpot = go.Spot.Right;}});// move the parent node to the location of the last dummylet vit = work.vertexes.iterator;while (vit.next()) {const v = vit.value;const len = v.children.length;if (len === 0) continue; // ignore leaf nodesif (v.parent === null) continue; // don't move root nodeconst dummy2 = v.children[len - 1];v.centerX = dummy2.centerX;v.centerY = dummy2.centerY;}const layout = this;vit = work.vertexes.iterator;while (vit.next()) {const v = vit.value;if (v.parent === null) {layout.shift(v);}}// now actually change the Node.location of all mitNodes();}/*** This override stops links from being committed since the work is done by the {@link FishboneLink} class.*/commitLinks() {}/*** Shifts subtrees within the fishbone based on angle and node spacing.*/shift(v) {const p = v.parent;if (p !== null && (v.angle === 90 || v.angle === 270)) {const g = p.parent;if (g !== null) {const shift = v.nodeSpacing;if (g["_direction"] > 0) {if (g.angle === 90) {if (p.angle === 0) {v["_direction"] = 1;if (v.angle === 270) this.shiftAll(2, -shift, p, v);} else if (p.angle === 180) {v["_direction"] = -1;if (v.angle === 90) this.shiftAll(-2, shift, p, v);}} else if (g.angle === 270) {if (p.angle === 0) {v["_direction"] = 1;if (v.angle === 90) this.shiftAll(2, -shift, p, v);} else if (p.angle === 180) {v["_direction"] = -1;if (v.angle === 270) this.shiftAll(-2, shift, p, v);}}} else if (g["_direction"] < 0) {if (g.angle === 90) {if (p.angle === 0) {v["_direction"] = 1;if (v.angle === 90) this.shiftAll(2, -shift, p, v);} else if (p.angle === 180) {v["_direction"] = -1;if (v.angle === 270) this.shiftAll(-2, shift, p, v);}} else if (g.angle === 270) {if (p.angle === 0) {v["_direction"] = 1;if (v.angle === 270) this.shiftAll(2, -shift, p, v);} else if (p.angle === 180) {v["_direction"] = -1;if (v.angle === 90) this.shiftAll(-2, shift, p, v);}}}} else {// g === null: V is a child of the tree ROOTconst dir = p.angle === 0 ? 1 : -1;v["_direction"] = dir;this.shiftAll(dir, 0, p, v);}}for (let i = 0; i < v.children.length; i++) {const c = v.children[i];this.shift(c);}}/*** Shifts a subtree.*/shiftAll(direction, absolute, root, v) {// assert(root.angle === 0 || root.angle === 180);let locx = v.centerX;locx += (direction * Math.abs(root.centerY - v.centerY)) / 2;locx += absolute;v.centerX = locx;for (let i = 0; i < v.children.length; i++) {const c = v.children[i];this.shiftAll(direction, absolute, root, c);}}}/*** Custom {@link Link} class for {@link FishboneLayout}.* @category Part Extension*/export class FishboneLink extends go.Link {computeAdjusting() {return this.adjusting;}/*** Determines the points for this link based on spots and maintains horizontal lines.*/computePoints() {const result = putePoints();if (result) {// insert middle point to maintain horizontal linesif (this.fromSpot.equals(go.Spot.Right) ||this.fromSpot.equals(go.Spot.Left)) {let p1;// deal with root node being on the "wrong" sideconst fromnode = this.fromNode;const fromport = this.fromPort;if (fromnode !== null &&fromport !== null &&fromnode.findLinksInto().count === 0) {// pretend the link is coming from the opposite direction than the declared FromSpotconst fromctr = fromport.getDocumentPoint(go.Spot.Center);const fromfar = fromctr.copy();fromfar.x += this.fromSpot.equals(go.Spot.Left) ? 99999 : -99999;p1 = this.getLinkPointFromPoint(fromnode,fromport,fromctr,fromfar,true).copy();// update the route pointsthis.setPoint(0, p1);let endseg = this.fromEndSegmentLength;if (isNaN(endseg)) endseg = fromport.fromEndSegmentLength;p1.x += this.fromSpot.equals(go.Spot.Left) ? endseg : -endseg;this.setPoint(1, p1);} else {p1 = this.getPoint(1); // points 0 & 1 should be OK already}const tonode = this.toNode;const toport = this.toPort;if (tonode !== null && toport !== null) {const toctr = toport.getDocumentPoint(go.Spot.Center);const far = toctr.copy();far.x += this.fromSpot.equals(go.Spot.Left) ? -99999 / 2 : 99999 / 2;far.y += toctr.y < p1.y ? 99999 : -99999;const p2 = this.getLinkPointFromPoint(tonode,toport,toctr,far,false);this.setPoint(2, p2);let dx = Math.abs(p2.y - p1.y) / 2;if (this.fromSpot.equals(go.Spot.Left)) dx = -dx;this.insertPoint(2, new go.Point(p2.x + dx, p1.y));}} else if (this.toSpot.equals(go.Spot.Right) ||this.toSpot.equals(go.Spot.Left)) {const p1 = this.getPoint(1); // points 1 & 2 should be OK alreadyconst fromnode = this.fromNode;const fromport = this.fromPort;if (fromnode !== null && fromport !== null) {const parentlink = fromnode.findLinksInto().first();const fromctr = fromport.getDocumentPoint(go.Spot.Center);const far = fromctr.copy();far.x +=parentlink !== null && parentlink.fromSpot.equals(go.Spot.Left)? -99999 / 2: 99999 / 2;far.y += fromctr.y < p1.y ? 99999 : -99999;const p0 = this.getLinkPointFromPoint(fromnode,fromport,fromctr,far,true);this.setPoint(0, p0);let dx = Math.abs(p1.y - p0.y) / 2;if (parentlink !== null && parentlink.fromSpot.equals(go.Spot.Left))dx = -dx;this.insertPoint(1, new go.Point(p0.x + dx, p1.y));}}}return result;}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。