鱼骨图由日本管理大师石川馨先生所发明,故又名石川图。鱼骨图是一种发现问题“根本原因”的方法,它也可以称之为“Ishikawa”或者“因果图”。其特点是简捷实用,深入直观。它看上去有些象鱼骨,问题或缺陷(即后果)标在"鱼头"外。在鱼骨上长出鱼刺,上面按出现机会多寡列出产生生产问题的可能原因,有助于说明各个原因之间如何相互影响。

这玩意儿就体现了一个什么5w1h的管理方法,经过我将近4周的时间捣鼓。由于原先想实现的过于复杂(主要由于本人数学知识浅薄),后来放弃了原先的两个方案。一是分支斜线扩展;二是分支上下、左右扩展。

不知道写点个啥,直接上代码吧。
---------------------
作者:海阔山遥-未知何处是潇湘
来源:转自CSDN
原文链接:https://blog.csdn.net/gua_381091614/article/details/38982675

  1. /**
  2. * Created by numen_huang on 2014/8/6.
  3. */
  4. $(function () {
  5. if (!mxClient.isBrowserSupported()) {
  6. // Displays an error message if the browser is not supported.
  7. mxUtils.error('Browser is not supported!', 200, false);
  8. }
  9. else {
  10. document.oncontextmenu = function () {
  11. return false;
  12. }
  13. var fish=new fishBone();
  14. fish.init();
  15. }
  16. });
  17. var fishBone=function(){};
  18.  
  19. fishBone.prototype.graph=null;
  20.  
  21. fishBone.prototype.init=function(){
  22. this.resetSourceCode();
  23. this.buildCanvas();
  24. this.setCanvasStyle();
  25. this.buildContextMenu();
  26. this.createFishBone();
  27. };
  28.  
  29. fishBone.prototype.resetSourceCode=function(){
  30. var _this=this;
  31. mxCellRenderer.prototype.createLabel = function(state, value) {
  32. var graph = state.view.graph;
  33. var isEdge = graph.getModel().isEdge(state.cell);
  34. if (state.style[mxConstants.STYLE_FONTSIZE] > 0 || state.style[mxConstants.STYLE_FONTSIZE] == null) {
  35. var isForceHtml = (graph.isHtmlLabel(state.cell) || (value != null && mxUtils.isNode(value))) && graph.dialect == mxConstants.DIALECT_SVG;
  36. var h='';
  37. var spacingRight=-30;
  38. var spacingLeft=1;
  39. if(state.cell.id=='fishboneHead'){
  40. h='horizontal';
  41. spacingRight=spacingLeft=0;
  42. }
  43. if(state.cell.level%2==0 && state.cell.id!='fishboneHead'){
  44. h='horizontal';
  45. if(_this.getSelfTopParent(state.cell).direction=='bottom') {
  46. spacingLeft = -25;
  47. }else{
  48. spacingLeft = 0;
  49. }
  50. }
  51. var background=state.style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR];
  52. var borderColor=state.style[mxConstants.STYLE_LABEL_BORDERCOLOR];
  53. var color=state.style[mxConstants.STYLE_FONTCOLOR];
  54. if(state.cell.level==1){
  55. background='#B3D9D9';
  56. borderColor='#005757';
  57. color='#336666';
  58. }else{
  59. background='#F3F3F3';
  60. borderColor='#005757';
  61. color='#336666';
  62. }
  63. state.text = new mxText(value, null, (state.style[mxConstants.STYLE_ALIGN] || mxConstants.ALIGN_CENTER),
  64. graph.getVerticalAlign(state),color,
  65. state.style[mxConstants.STYLE_FONTFAMILY], state.style[mxConstants.STYLE_FONTSIZE],
  66. state.style[mxConstants.STYLE_FONTSTYLE], state.style[mxConstants.STYLE_SPACING],
  67. spacingLeft, 10,
  68. spacingRight, 10, h,
  69. background, borderColor,
  70. graph.isWrapping(state.cell), graph.isLabelClipped(state.cell), state.style[mxConstants.STYLE_OVERFLOW]);
  71. state.text.opacity = state.style[mxConstants.STYLE_TEXT_OPACITY];
  72. state.text.dialect = (isForceHtml) ? mxConstants.DIALECT_STRICTHTML: state.view.graph.dialect;
  73. this.initializeLabel(state);
  74. var getState = function(evt) {
  75. var result = state;
  76. if (mxClient.IS_TOUCH) {
  77. var x = mxEvent.getClientX(evt);
  78. var y = mxEvent.getClientY(evt);
  79. var pt = mxUtils.convertPoint(graph.container, x, y);
  80. result = graph.view.getState(graph.getCellAt(pt.x, pt.y));
  81. }
  82. return result;
  83. };
  84. var md = (mxClient.IS_TOUCH) ? 'touchstart': 'mousedown';
  85. var mm = (mxClient.IS_TOUCH) ? 'touchmove': 'mousemove';
  86. var mu = (mxClient.IS_TOUCH) ? 'touchend': 'mouseup';
  87. mxEvent.addListener(state.text.node, md, mxUtils.bind(this,
  88. function(evt) {
  89. if (this.isLabelEvent(state, evt)) {
  90. graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, state));
  91. }
  92. }));
  93. mxEvent.addListener(state.text.node, mm, mxUtils.bind(this,
  94. function(evt) {
  95. if (this.isLabelEvent(state, evt)) {
  96. graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
  97. }
  98. }));
  99. mxEvent.addListener(state.text.node, mu, mxUtils.bind(this,
  100. function(evt) {
  101. if (this.isLabelEvent(state, evt)) {
  102. graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
  103. }
  104. }));
  105. mxEvent.addListener(state.text.node, 'dblclick', mxUtils.bind(this,
  106. function(evt) {
  107. if (this.isLabelEvent(state, evt)) {
  108. graph.dblClick(evt, state.cell);
  109. mxEvent.consume(evt);
  110. }
  111. }));
  112. }
  113. };
  114. mxTriangle.prototype.redrawPath = function(path, x, y, w, h) {
  115. if (this.direction == mxConstants.DIRECTION_NORTH) {
  116. path.moveTo(0, h);
  117. path.lineTo(0.5 * w, 0);
  118. path.lineTo(w, h);
  119. } else if (this.direction == mxConstants.DIRECTION_SOUTH) {
  120. path.moveTo(0, 0);
  121. path.lineTo(0.5 * w, h);
  122. path.lineTo(w, 0);
  123. } else if (this.direction == mxConstants.DIRECTION_WEST) {
  124. path.moveTo(0, 0);
  125. path.lineTo(w, 0.5 * h);
  126. path.lineTo(0, h);
  127. }
  128. else if(this.direction == 'east'){
  129. path.moveTo(0, 0);
  130. path.quadTo(w, 0.25 * h,w+1,0.5*h);
  131. path.quadTo(w+1, 0.75 * h,0,h);
  132. }
  133. else{
  134. path.moveTo(0, 0);
  135. path.lineTo(w, 0.5 * h);
  136. path.lineTo(0, h);
  137. }
  138. path.close();
  139. };
  140. mxGraphSelectionModel.prototype.singleSelection = true;
  141. mxGraph.prototype.foldingEnabled=false;
  142. mxGraph.prototype.selectCellForEvent = function(cell, evt) {
  143. if(cell.id!='fishboneEnd' && cell.id!='fishboneBody' && !cell.isEdge()) {
  144. var isSelected = this.isCellSelected(cell);
  145. if (this.isToggleEvent(evt)) {
  146. if (isSelected) {
  147. this.removeSelectionCell(cell);
  148. } else {
  149. this.addSelectionCell(cell);
  150. }
  151. } else if (!isSelected || this.getSelectionCount() != 1) {
  152. this.setSelectionCell(cell);
  153. }
  154. }
  155. };
  156. mxGraph.prototype.moveCells = function(cells, dx, dy, clone, target, evt) {
  157. if(cells==null || cells.length>1){
  158. return false;
  159. }
  160. var cell=cells[0];
  161.  
  162. if (dx != 0 || dy != 0 || clone || target != null) {
  163. this.getModel().beginUpdate();
  164. try {
  165. if (clone) {
  166. return false;
  167. }
  168. if(cell.level==1){
  169. var fishHead=_this.getCellById('fishboneHead');
  170. var fishBody=_this.getCellById('fishboneBody');
  171. var fishEnd=_this.getCellById('fishboneEnd');
  172. if(cell.geometry.x+dx>fishHead.geometry.x-100){
  173. cell.geometry.x+=dx;
  174. var size=cell.geometry.x-fishHead.geometry.x;
  175. fishHead.geometry.x+=size+100;
  176. fishBody.geometry.width+=size+100;
  177. }
  178. else if(cell.geometry.x+dx<fishEnd.geometry.x+fishEnd.geometry.width+100){
  179. cell.geometry.x+=dx;
  180. var size=cell.geometry.x-(fishEnd.geometry.x+fishEnd.geometry.width);
  181. fishBody.geometry.width+=Math.abs(size-100);
  182. fishEnd.geometry.x+=size-100;
  183. fishBody.geometry.x+=size-100;
  184. }
  185. else{
  186. cell.geometry.x+=dx;
  187. }
  188. }
  189. else if(cell.id=='fishboneHead'){
  190. var fishHead=_this.getCellById('fishboneHead');
  191. var fishBody=_this.getCellById('fishboneBody');
  192. var children=_this.getChildSubject(fishHead);
  193.  
  194. var maxX=children.length>0?children[0].geometry.x:fishHead.geometry.x-30;
  195. for(var i=0;i<children.length;i++){
  196. if(children[i].geometry.x>maxX){
  197. maxX=children[i].geometry.x;
  198. }
  199. }
  200. var oldX=fishHead.geometry.x;
  201. fishHead.geometry.x+=dx;
  202. var size=fishHead.geometry.x-oldX;
  203. if(fishHead.geometry.x<maxX+100){
  204. fishHead.geometry.x=maxX+100;
  205. size = fishHead.geometry.x-oldX;
  206. }
  207. fishBody.geometry.width+=size;
  208. }
  209. else if(cell.id=='fishboneEnd'){
  210. var fishHead=_this.getCellById('fishboneHead');
  211. var fishBody=_this.getCellById('fishboneBody');
  212. var fishEnd=_this.getCellById('fishboneEnd');
  213. var children=_this.getChildSubject(fishHead);
  214. var minX=children.length>0?children[0].geometry.x:fishEnd.geometry.x-100;
  215. for(var i=0;i<children.length;i++){
  216. if(children[i].geometry.x<minX){
  217. minX=children[i].geometry.x;
  218. }
  219. }
  220. var oldX=fishEnd.geometry.x;
  221. fishEnd.geometry.x+=dx;
  222. var size=fishEnd.geometry.x-oldX;
  223. if(fishEnd.geometry.x>minX-100){
  224. fishEnd.geometry.x=minX-100;
  225. size=fishEnd.geometry.x-oldX;
  226. }
  227. fishBody.geometry.x=fishEnd.geometry.x+fishEnd.geometry.width;
  228. fishBody.geometry.width-=size;
  229. }
  230. else if (cell.direction == 'top' || cell.direction == 'bottom') {
  231. if(cell.parent.children.length==1){
  232. cell.geometry.x=0;
  233. cell.parent.geometry.width-=dx;
  234. cell.parent.geometry.x+=dx;
  235. if(cell.parent.geometry.width<=70){
  236. cell.parent.geometry.width=70;
  237. cell.parent.geometry.x=-70;
  238. }
  239. }
  240. else{
  241. if(cell.geometry.x==0){
  242. if(dx<0){
  243. for(var i=0;i<cell.parent.children.length;i++){
  244. if(cell.parent.children[i].id!=cell.id){
  245. cell.parent.children[i].geometry.x-=dx;
  246. }
  247. }
  248. cell.parent.geometry.width-=dx;
  249. cell.parent.geometry.x+=dx;
  250. cell.geometry.x=0;
  251. }
  252. else{
  253. if(cell.parent.children.length==1){
  254. if(cell.geometry.x+dx<cell.parent.geometry.width-70){
  255. cell.geometry.x+=dx;
  256. }else{
  257. cell.geometry.x=cell.parent.geometry.width-70;
  258. }
  259. }
  260. else{
  261. var children=cell.parent.children;
  262. var temp1=children[0];//最小y坐标的cell
  263. var t=0;
  264. for (var i = 0; i < children.length; i++) {
  265. if (children[i].geometry.x < temp1.geometry.x) {
  266. temp1 = children[i];
  267. }
  268. if(children[i].geometry.x==0){
  269. t++;
  270. }
  271. }
  272. var temp2=null;//第二小x坐标的cell
  273. for (var i = 0; i < children.length; i++) {
  274. if (children[i].geometry.x > cell.geometry.x && !temp2) {
  275. temp2=children[i];
  276. continue;
  277. }else if(temp2) {
  278. if (children[i].geometry.x < temp2.geometry.x && children[i].id != cell.id) {
  279. temp2 = children[i];
  280. }
  281. }
  282. }
  283. if(temp2 && t==1) {
  284. //没有超过第二小x坐标
  285. if (temp2.geometry.x - dx > 0) {
  286. for (var k = 0; k < cell.parent.children.length; k++) {
  287. if (cell.parent.children[k].id != cell.id) {
  288. cell.parent.children[k].geometry.x -= dx;
  289. }
  290. }
  291. cell.geometry.x += dx;
  292. cell.parent.geometry.width -= dx;
  293. cell.parent.geometry.x += dx;
  294. cell.geometry.x = 0;
  295. }
  296. //超过第二小x坐标
  297. else {
  298. if(cell.parent.geometry.width-temp2.geometry.x==70){
  299. for (var k = 0; k < cell.parent.children.length; k++) {
  300. cell.parent.children[k].geometry.x=0;
  301. }
  302. cell.parent.geometry.width=70;
  303. cell.parent.geometry.x=-70;
  304. }else {
  305. var size = dx - temp2.geometry.x;
  306. var pSize = cell.parent.geometry.width - temp2.geometry.x;
  307. for (var k = 0; k < cell.parent.children.length; k++) {
  308. if (cell.parent.children[k].id != temp2.id && cell.parent.children[k].id != cell.id) {
  309. cell.parent.children[k].geometry.x -= temp2.geometry.x;
  310. }
  311. }
  312. cell.parent.geometry.width -= temp2.geometry.x;
  313. cell.parent.geometry.x += temp2.geometry.x;
  314. temp2.geometry.x = 0;
  315. if(size-pSize>0){
  316. cell.geometry.x = cell.parent.geometry.width-70;
  317. }
  318. else {
  319. cell.geometry.x = size;
  320. }
  321. }
  322. }
  323. }
  324. else if(t>1){
  325. if(cell.geometry.x+dx<cell.parent.geometry.width-70){
  326. cell.geometry.x+=dx;
  327. }else{
  328. cell.geometry.x=cell.parent.geometry.width-70;
  329. }
  330. }
  331. }
  332. }
  333. }
  334. else{
  335. if(cell.geometry.x+dx<0){
  336. var size = 0-(cell.geometry.x+dx);
  337. for (var i = 0; i < cell.parent.children.length; i++) {
  338. if (cell.parent.children[i].id !=cell.id) {
  339. cell.parent.children[i].geometry.x+=size;
  340. }
  341. }
  342. cell.parent.geometry.x-=size;
  343. cell.parent.geometry.width+=size;
  344. cell.geometry.x=0;
  345. }
  346. else if(cell.geometry.x+dx==0){
  347. cell.geometry.x=0;
  348. }
  349. else if(cell.geometry.x+dx>0 && cell.geometry.x+dx<cell.parent.geometry.width-70){
  350. cell.geometry.x+=dx;
  351. }
  352. else{
  353. cell.geometry.x=cell.parent.geometry.width-70;
  354. }
  355. }
  356. }
  357. }
  358. else if(cell.direction == 'left'){
  359. var py = 0;
  360. var myCell=cell;
  361. if(cell.parent.direction=='bottom') {
  362. cell.geometry.y += dy;
  363. for (var i = 0; i < cell.parent.children.length; i++) {
  364. if (cell.parent.children[i].geometry.y > py)
  365. py = cell.parent.children[i].geometry.y;
  366. }
  367. if(py>70) {
  368. cell.parent.geometry.height = py;
  369. }else{
  370. cell.parent.geometry.height = 70;
  371. }
  372. if(cell.geometry.y<70){
  373. cell.geometry.y=70;
  374. }
  375. }
  376. else{
  377. if(cell.geometry.y==0){
  378. if(dy<0) {
  379. for (var i = 0; i < cell.parent.children.length; i++) {
  380. if (cell.parent.children[i].id != cell.id) {
  381. cell.parent.children[i].geometry.y -= dy;
  382. }
  383. }
  384. cell.parent.geometry.y += dy;
  385. cell.parent.geometry.height -= dy;
  386. }
  387. else{
  388. //只有一个子集的时候
  389. if(cell.parent.children.length==1){
  390. var fishBody=_this.getCellById('fishboneBody');
  391. //拖动超过父级label的位置
  392. if(!(cell.geometry.y==0 && cell.parent.geometry.height==70)) {
  393. if (cell.parent.geometry.height - dy < 70) {
  394. if(cell.parent.parent.id==1){
  395. cell.parent.geometry.y = fishBody.geometry.y - 68;
  396. }else{
  397. cell.parent.geometry.y = cell.parent.parent.geometry.y - 70;
  398. }
  399. cell.parent.geometry.height = 70;
  400. } else {
  401. cell.parent.geometry.height -= dy;
  402. cell.parent.geometry.y += dy;
  403. }
  404. cell.geometry.y = 0;
  405. }
  406. }
  407. else {//超过一个子集
  408. var children=cell.parent.children;
  409. var temp1=children[0];//最小y坐标的cell
  410. var t=0;
  411. for (var i = 0; i < children.length; i++) {
  412. if (children[i].geometry.y < temp1.geometry.y) {
  413. temp1 = children[i];
  414. }
  415. if(children[i].geometry.y==0){
  416. t++;
  417. }
  418. }
  419. var temp2=null;//第二小y坐标的cell
  420. for (var i = 0; i < children.length; i++) {
  421. if (children[i].geometry.y > temp1.geometry.y && !temp2) {
  422. temp2=children[i];
  423. continue;
  424. }else if(temp2) {
  425. if (children[i].geometry.y < temp2.geometry.y && children[i].id != temp1.id) {
  426. temp2 = children[i];
  427. }
  428. }
  429. }
  430. //当没有超过第二小y坐标时。
  431. if(temp2 && t==1) {
  432. if (cell.geometry.y + dy < temp2.geometry.y) {
  433. for (var k = 0; k < cell.parent.children.length; k++) {
  434. if (cell.parent.children[k].id != cell.id) {
  435. cell.parent.children[k].geometry.y -= dy;
  436. }
  437. }
  438. cell.parent.geometry.height -= dy;
  439. cell.parent.geometry.y += dy;
  440. cell.geometry.y = 0;
  441. }
  442. //当超过第二小y坐标时候
  443. else {
  444. cell.geometry.y += dy;
  445. var py = cell.parent.geometry.height - (cell.parent.geometry.height - temp2.geometry.y);
  446. for (var k = 0; k < cell.parent.children.length; k++) {
  447. if (cell.parent.children[k].id != temp2.id) {
  448. cell.parent.children[k].geometry.y -= py;
  449. }
  450. }
  451. cell.parent.geometry.height -= temp2.geometry.y;
  452. cell.parent.geometry.y += temp2.geometry.y;
  453. temp2.geometry.y = 0;
  454.  
  455. for (var j = 0; j < cell.parent.children.length; j++) {
  456. if (cell.parent.geometry.height - cell.parent.children[j].geometry.y < 70) {
  457. cell.parent.children[j].geometry.y = cell.parent.geometry.height - 70;
  458. }
  459. }
  460. }
  461. }
  462. else if(t>1){
  463. if(cell.parent.geometry.height>70) {
  464. if (cell.parent.geometry.height - dy < 70) {
  465. cell.geometry.y = 70;
  466. } else {
  467. cell.geometry.y = dy;
  468. }
  469. }
  470. }
  471. }
  472. }
  473. }
  474. else{
  475. if(cell.geometry.y+dy>0){
  476. if(cell.parent.geometry.height-(cell.geometry.y+dy)<=70){
  477. cell.geometry.y=cell.parent.geometry.height-70;
  478. }else {
  479. cell.geometry.y += dy;
  480. }
  481. }else{
  482. for (var i = 0; i < cell.parent.children.length; i++) {
  483. if (cell.parent.children[i].id != cell.id) {
  484. cell.parent.children[i].geometry.y -= (cell.geometry.y+dy);
  485. }
  486. }
  487. cell.parent.geometry.y += (cell.geometry.y+dy);
  488. cell.parent.geometry.height -= cell.geometry.y+dy;
  489. cell.geometry.y=0;
  490. }
  491. }
  492. }
  493. }
  494. } finally {
  495. this.getModel().endUpdate();
  496. this.refresh();
  497. }
  498. }
  499. return cells;
  500. };
  501.  
  502. mxGraph.prototype.dblClick = function(evt, cell) {
  503. if(cell.id!='fishboneEnd' && cell.id!='fishboneBody' && !cell.isEdge()) {
  504. var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell', cell);
  505. this.fireEvent(mxe);
  506. if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed() && cell != null && this.isCellEditable(cell)) {
  507. this.startEditingAtCell(cell, evt);
  508. }
  509. }
  510. };
  511. // mxGraph.prototype.labelChanged = function(cell, value, evt) {
  512. // this.model.beginUpdate();
  513. // var geo=cell.geometry;
  514. // try {
  515. // this.cellLabelChanged(cell, value, true);
  516. // this.fireEvent(new mxEventObject(mxEvent.LABEL_CHANGED, 'cell', cell, 'value', value, 'event', evt));
  517. // geo.height =geo.height+((value.length-5>0)? (value.length-5)*18:0);
  518. // cell.geometry=geo;
  519. // if(cell.level>1){
  520. //// var myEdge = cell.edges[0];
  521. //// var point = myEdge.geometry.points[0];
  522. //// point.x=0;
  523. //// myEdge.geometry.points=[point];
  524. // }
  525. // } finally {
  526. // this.model.endUpdate();
  527. // }
  528. // return cell;
  529. // };
  530. mxCellEditor.prototype.init = function() {
  531. this.textarea = document.createElement('input');
  532. this.textarea.className = 'mxCellEditor';
  533. this.textarea.style.position = 'absolute';
  534. this.textarea.style.overflow = 'visible';
  535. this.textarea.setAttribute('type', 'textbox');
  536. if (false) {
  537. this.textarea.style.resize = 'none';
  538. }
  539. mxEvent.addListener(this.textarea, 'blur', mxUtils.bind(this,
  540. function(evt) {
  541. this.stopEditing(!this.graph.isInvokesStopCellEditing());
  542. }));
  543. mxEvent.addListener(this.textarea, 'keydown', mxUtils.bind(this,
  544. function(evt) {
  545. if (!mxEvent.isConsumed(evt)) {
  546. if (evt.keyCode == 113 || (this.graph.isEnterStopsCellEditing() && evt.keyCode == 13 && !mxEvent.isControlDown(evt) && !mxEvent.isShiftDown(evt))) {
  547. this.graph.stopEditing(false);
  548. mxEvent.consume(evt);
  549. } else if (evt.keyCode == 27) {
  550. this.graph.stopEditing(true);
  551. mxEvent.consume(evt);
  552. } else {
  553. if (this.clearOnChange) {
  554. this.clearOnChange = false;
  555. this.textarea.value = '';
  556. }
  557. this.setModified(true);
  558. }
  559. }
  560. }));
  561. };
  562. mxGraphHandler.prototype.selectEnabled=false;
  563. };
  564.  
  565. fishBone.prototype.buildCanvas=function(){
  566. var container = document.createElement('div');
  567. container.style.position = 'absolute';
  568. container.style.overflow = 'hidden';
  569. container.style.left = '0px';
  570. container.style.top = '0px';
  571. container.style.right = '0px';
  572. container.style.bottom = '0px';
  573. document.body.appendChild(container);
  574. var node = mxUtils.load('config/keyhandler-commons.xml').getDocumentElement();
  575. this.editor = new mxEditor(node);
  576. this.editor.graph=new mxGraph(container);
  577. this.graph = this.editor.graph;
  578. var outline = document.getElementById('outlineContainer');
  579. new mxOutline(this.graph, outline);
  580. if (mxClient.IS_IE) {
  581. new mxDivResizer(container);
  582. new mxDivResizer(outline);
  583. }
  584. this.graph.clearSelection();
  585. this.graph.setEnabled(true);
  586. this.graph.setTooltips(false);
  587. this.graph.setConnectable(false);
  588. this.graph.setPanning(true);
  589. this.graph.setCellsResizable(false);
  590. };
  591.  
  592. fishBone.prototype.setCanvasStyle=function(){
  593. var graph=this.graph;
  594. var style = graph.getStylesheet().getDefaultVertexStyle();
  595. style[mxConstants.STYLE_FONTCOLOR] = 'black';
  596. style[mxConstants.STYLE_STROKECOLOR] = '#8E8E8E';
  597. style[mxConstants.STYLE_FILLCOLOR] = 'white';
  598. style[mxConstants.STYLE_ROUNDED] = true;
  599. style[mxConstants.STYLE_FONTSTYLE] = 1;
  600. style[mxConstants.STYLE_FONTFAMILY] = '微软雅黑';
  601. // Sets the default style for edges
  602. style = graph.getStylesheet().getDefaultEdgeStyle();
  603. style[mxConstants.STYLE_ROUNDED] = true;
  604. style[mxConstants.STYLE_EDGE] = mxEdgeStyle.STRAIGHT;
  605.  
  606. // Enables rubberband selection and key handling
  607. var rubberband = new mxRubberband(graph);
  608. var keyHandler = new mxKeyHandler(graph);
  609. };
  610.  
  611. fishBone.prototype.buildContextMenu=function(){
  612. var fish=this;
  613. mxPopupMenu.prototype.submenuImage = 'src/images/submenu.gif';
  614. this.graph.panningHandler.factoryMethod = function (menu, cell, evt) {
  615. if (cell != null && !cell.isEdge() && cell.id!='fishboneEnd' && cell.id!='fishboneBody') {
  616. var item= menu.addItem('插入','src/images/maximize.gif');
  617. menu.addItem('插入主题','src/images/maximize.gif', function () {
  618. fish.createSubject(cell);
  619. fish.graph.clearSelection();
  620. },item);
  621. menu.addItem('插入子主题','src/images/normalize.gif', function () {
  622. fish.createSubject(cell,true);
  623. fish.graph.clearSelection();
  624. },item);
  625. menu.addItem('删除','images/delete2.png', function () {
  626. fish.graph.removeCells([cell]);
  627. });
  628. menu.addItem('编辑','images/icons48/tab.png', function () {
  629. fish.graph.startEditingAtCell(cell);
  630. });
  631. }else{
  632. this.graph.clearSelection();
  633. };
  634. };
  635. };
  636.  
  637. fishBone.prototype.createFishBone=function(){
  638. var xml = '\
  639. <mxGraphModel> \
  640. <root> \
  641. <mxCell id="0"/> \
  642. <mxCell id="1" parent="0"/> \
  643. <mxCell id="fishboneEnd" value="" parent="1" vertex="1" style="shape=triangle;direction=east1;fillColor=#CEFFCE;gradientColor=#93FF93;"> \
  644. <mxGeometry x="510" y="240" width="40" height="110" as="geometry"/> \
  645. </mxCell> \
  646. <mxCell id="fishboneBody" value="" vertex="1" parent="1" style="shape=triangle;direction=west;fillColor=#4F4F4F;"> \
  647. <mxGeometry x="550" y="290" width="800" height="10" as="geometry"/> \
  648. </mxCell> \
  649. <mxCell id="fishboneHead" level="0" value="需要解决的问题" vertex="1" parent="1" style="shape=triangle;direction=east;fillColor=#CEFFCE;gradientColor=#93FF93;fontSize=18;"> \
  650. <mxGeometry x="1351" y="252" width="150" height="80" as="geometry"/> \
  651. </mxCell>\
  652. </root> \
  653. </mxGraphModel> \
  654. ';
  655. doc = mxUtils.parseXml(xml);
  656. var codec = new mxCodec(doc);
  657. //mxShape.prototype.direction = mxConstants.DIRECTION_NORTH;
  658. codec.decode(doc.documentElement, this.graph.getModel());
  659. var items=['人员','管理','过程','环境','设备','材料'];
  660. var cell=this.getCellById('fishboneHead');
  661. for(var i=0;i<items.length;i++) {
  662. this.createSubject(cell, true,items[i]);
  663. };
  664. };
  665.  
  666. fishBone.prototype.createSubject=function(cell,isSub,text){
  667. var model=this.graph.getModel();
  668. var graph=this.graph;
  669. var fish=this;
  670. var x = 0, y = 0, py = 0, px = 0;
  671. var myChild = fish.getChildSubject(cell);
  672. if(!cell.children)cell.children=[];
  673.  
  674. //子主题在下方,插入子主题方向错误
  675. //子主题在右侧,插入子主题方向错误
  676. /*水平方向子主题*/
  677. var insertHorizontalSubLevel=function(){
  678. var selfTopParent = fish.getSelfTopParent(cell);
  679. if (!text)text = '分支主题' + (cell.children.length + 1);
  680. x = -70;
  681. if(selfTopParent.direction=='bottom') {
  682. if (cell.children.length == 0) {
  683. y = 70;
  684. } else {
  685. y += cell.geometry.height+70;
  686. }
  687. var myCell = graph.insertVertex(cell, null, text, x, y, 70, 1, 'shape=line;verticalAlign=top;align=right;strokeWidth=1;strokeColor=#BB3D00;');
  688. myCell.level = cell.level + 1;
  689. myCell.direction = 'left';
  690. myCell.geometry.x -= 70;
  691. if (myCell.parent.parent.direction == 'left') {
  692. myCell.parent.parent.geometry.height = 1;
  693. }
  694.  
  695. var resize = function (cell) {
  696. if (cell.parent.direction == 'bottom' && cell.parent.level > 1) {
  697. if (cell.parent.parent.children.length > 1 && cell.parent.children.length == 1 && cell.parent.geometry.x > 0) {
  698. for (var k = 0; k < cell.parent.parent.children.length; k++) {
  699. if (cell.parent.parent.children[k].geometry.x >= cell.parent.geometry.x && cell.parent.children.length == 1) {
  700. cell.parent.parent.children[k].geometry.x += 50;
  701. cell.parent.parent.geometry.x -= 50;
  702. cell.parent.parent.geometry.width += 50;
  703. }
  704. }
  705. }
  706. cell.parent.geometry.height = cell.parent.children[cell.parent.children.length - 1].geometry.y;
  707. } else {
  708. cell.parent.geometry.height = 1;
  709. }
  710. if (cell.parent.level != 1) {
  711. resize(cell.parent);
  712. } else {
  713. cell.parent.geometry.height = cell.parent.children[cell.parent.children.length - 1].geometry.y;
  714. }
  715. };
  716. resize(myCell);
  717. }
  718. else{
  719. y = 0;
  720. for(var i=0;i<cell.children.length;i++){
  721. cell.children[i].geometry.y+=70;
  722. }
  723. if(cell.children.length>0) {
  724. cell.geometry.height += 70;
  725. cell.geometry.y -= 70;
  726. }
  727. var myCell = graph.insertVertex(cell, null, text, x, y, 70, 1, 'shape=line;verticalAlign=top;align=right;strokeWidth=1;strokeColor=#BB3D00;');
  728. myCell.level = cell.level + 1;
  729. myCell.direction = 'left';
  730. myCell.geometry.x -= 70;
  731.  
  732. if (myCell.parent.parent.direction == 'left') {
  733. myCell.parent.parent.geometry.height = 1;
  734. }
  735.  
  736. var resize = function (cell) {
  737. if(cell.direction=='top' && cell.level!=1 && cell.children.length==1){
  738. for(var k=cell.parent.children.length-1;k>=0;k--){
  739. if(cell.parent.children[k].geometry.x>=cell.geometry.x){
  740. cell.parent.children[k].geometry.x+=cell.children[0].geometry.width;
  741. }
  742. }
  743. cell.geometry.x-=myCell.geometry.width;
  744. }
  745. };
  746. resize(myCell.parent);
  747. }
  748. };
  749. /*垂直方向子主题*/
  750. var insertVerticalSubLevel=function(){
  751. var selfTopParent = fish.getSelfTopParent(cell);
  752. var leftTopParents=fish.getLeftTopParent(selfTopParent,true);
  753. if (!text)text = '分支主题'+(cell.children.length+1);
  754. if(cell.children.length==0)
  755. x=0;
  756. else {
  757. var maxX=0;
  758. for(var i=0;i<cell.children.length;i++){
  759. if(cell.children[i].geometry.x>maxX){
  760. maxX=cell.children[i].geometry.x;
  761. }
  762. }
  763. x = 50 + maxX;
  764. }
  765. var align='right';
  766. var direction='bottom';
  767. if(selfTopParent.direction=='bottom') {
  768. y = 1;
  769. }else{
  770. y=0;
  771. align='left';
  772. direction='top';
  773. }
  774.  
  775. var myCell = graph.insertVertex(cell, null, text, x, y, 1, 70, 'shape=line;direction=north;verticalAlign=top;align='+align+';spacingBottom=10;strokeWidth=1;strokeColor=#BB3D00;');
  776. myCell.parentId = cell.id;
  777. myCell.level=cell.level+1;
  778. myCell.direction=direction;
  779. if(selfTopParent.direction=='bottom') {
  780. if (cell.children.length == 1) {
  781. var t = 0;
  782. for (var i = 0; i < cell.parent.children.length; i++) {
  783. if (cell.parent.children[i].id != cell.id && cell.parent.children[i].geometry.y > cell.geometry.y) {
  784. cell.parent.children[i].geometry.y += myCell.geometry.height;
  785. t++;
  786. }
  787. }
  788. if (t > 0) {
  789. selfTopParent.geometry.height += myCell.geometry.height;
  790. }
  791. };
  792. var resize = function (cell) {
  793. var parent = cell.parent;
  794. if(parent.id!=1) {
  795. if (cell.direction == 'left')cell.geometry.height = 1;
  796. if (parent.direction == 'left') {
  797. parent.geometry.height = 1;
  798. if (parent.children.length > 1 && cell.geometry.x > 0) {
  799. parent.geometry.width += 50;
  800. parent.geometry.x -= 50;
  801. }
  802. resize(parent);
  803. } else {
  804. parent.geometry.height = parent.children[parent.children.length - 1].geometry.y;
  805. if (parent.level != 1 && parent.parent.children.length > 1 && cell.parent.geometry.x > 0) {
  806. for (var k = 0; k < parent.parent.children.length; k++) {
  807. if (parent.parent.children[k].geometry.x >= parent.geometry.x)
  808. parent.parent.children[k].geometry.x += 50;
  809. }
  810. }
  811. resize(parent);
  812. }
  813. }
  814. };
  815. resize(myCell);
  816. }
  817. else{
  818. myCell.geometry.y-=70;
  819. var t=0;
  820. if (cell.children.length == 1&&cell.geometry.y>0) {
  821. for (var i = 0; i < cell.parent.children.length; i++) {
  822. if(cell.parent.children[i].geometry.y>0 && cell.parent.children[i].geometry.y>=cell.geometry.y){
  823. cell.parent.children[i].geometry.y+=cell.geometry.height;
  824. t++;
  825. }
  826. }
  827. cell.parent.geometry.height += myCell.geometry.height;
  828. cell.parent.geometry.y -= myCell.geometry.height;
  829. };
  830. var resize = function (cell) {
  831. if (cell.direction == 'left') {
  832. cell.geometry.height = 1;
  833. if(cell.children.length>1){
  834. for (var k = 0; k < cell.children.length; k++) {
  835. if (cell.children[k].geometry.x <= cell.geometry.x)
  836. cell.children[k].geometry.x += 50;
  837. }
  838. cell.geometry.x-=50;
  839. cell.geometry.width+=50;
  840. }
  841. }
  842. else {
  843. if(cell.children.length>1){
  844. for (var k = 0; k < cell.children.length; k++) {
  845. if (cell.children[k].geometry.x >= cell.geometry.x)
  846. cell.children[k].geometry.x += 50;
  847. }
  848. cell.geometry.x-=50;
  849. }
  850. }
  851. };
  852. resize(myCell.parent);
  853. }
  854. };
  855.  
  856. var insertTopLevel=function(){
  857. var direction='bottom';
  858. if(myChild.length%2==0){
  859. direction='top';
  860. }
  861. model.beginUpdate();
  862. x = cell.geometry.x - (myChild.length / 2 * 100) - (myChild.length + 1) * 30;
  863. if(myChild.length>0) {
  864. x = myChild[0].geometry.x;
  865. for (var t = 0; t < myChild.length; t++) {
  866. if (myChild[t].geometry.x < x) {
  867. x = myChild[t].geometry.x;
  868. }
  869. }
  870. }
  871. x-=100;
  872. if (myChild.length % 2 == 0) {
  873. y = cell.geometry.y - 30;
  874. } else {
  875. y = cell.geometry.y + 45;
  876. }
  877. if (!text)text = '分支主题';
  878. var vAlign='top';
  879. var dir='north';
  880. var align='right';
  881. if (myChild.length % 2 == 0) {
  882. vAlign='bottom';
  883. dir='south';
  884. align='left';
  885. }
  886. var myCell = graph.insertVertex(graph.getDefaultParent(), null, text, x, y, 1, 70, 'shape=line;align='+align+';verticalAlign='+vAlign+';direction='+dir+';verticalLabelPosition=middle;fontSize=18;strokeWidth=1;strokeColor=#8C8C00;');
  887. myCell.parentId = cell.id;
  888. myCell.level=cell.level+1;
  889. myCell.direction=direction;
  890.  
  891. model.endUpdate();
  892. graph.clearSelection();
  893. };
  894. var insert=function(){
  895. if(cell.level==0){
  896. insertTopLevel();
  897. }else{
  898. if(isSub){
  899. insertSubLevel();
  900. }else{
  901. insertSameLevel();
  902. }
  903. }
  904. };
  905. var insertSameLevel=function(){
  906. if(cell.level){
  907. if(cell.level==1){
  908. cell=fish.getCellById('fishboneHead');
  909. myChild = fish.getChildSubject(cell);
  910. insertTopLevel();
  911. }else{
  912. cell=cell.parent;
  913. myChild = cell.children;
  914. insertSubLevel();
  915. }
  916. }else{
  917. cell=cell.parent;
  918. myChild = cell.children;
  919. insertTopLevel();
  920. }
  921.  
  922. };
  923. var insertSubLevel=function(){
  924. switch (cell.direction){
  925. case 'left':
  926. insertVerticalSubLevel();
  927. break;
  928. case 'top':
  929. case 'bottom':
  930. insertHorizontalSubLevel();
  931. break;
  932. }
  933. graph.refresh();
  934. };
  935.  
  936. insert();
  937. };
  938.  
  939. fishBone.prototype.getChildSubject=function(cell){
  940. var allCells=this.graph.model.root.children[0].children;
  941. var children=[];
  942. for(var i=0;i<allCells.length;i++){
  943. if(allCells[i].parentId==cell.id){
  944. children.push(allCells[i]);
  945. }
  946. }
  947. return children;
  948. };
  949.  
  950. fishBone.prototype.getCellById=function(id){
  951. var allCells=this.graph.model.root.children[0].children;
  952. for(var i=0;i<allCells.length;i++){
  953. if(allCells[i].id==id){
  954. return allCells[i];
  955. }
  956. }
  957. return null;
  958. };
  959.  
  960. /*
  961. * 获取当前主题的1级主题
  962. * */
  963. fishBone.prototype.getSelfTopParent=function(cell){
  964. if(cell.level==1){
  965. return cell;
  966. }else{
  967. if(cell.parent.level==1){
  968. return cell.parent;
  969. }else{
  970. return this.getSelfTopParent(cell.parent);
  971. }
  972. }
  973. };
  974. /*
  975. * 获取左侧所有1级主题
  976. * */
  977. fishBone.prototype.getLeftTopParent=function(selfParentCell,isBottom){
  978. var allCells=this.graph.model.root.children[0].children;
  979. var arr=[];
  980. for(var i=0;i<allCells.length;i++){
  981. if(isBottom){
  982. if(allCells[i].level==1 && allCells[i].direction=='bottom' && allCells[i].geometry.x<selfParentCell.geometry.x){
  983. arr.push(allCells[i]);
  984. }
  985. }else{
  986.  
  987. }
  988. }
  989. return arr;
  990. };
  991. /*
  992. * 获取除自己以外的1级主题
  993. * */
  994. fishBone.prototype.getOtherTopParentCell=function(selfParentCell){
  995. var allCells=this.graph.model.root.children[0].children;
  996. var allTopParentCell=[];
  997. for(var i=0;i<allCells.length;i++){
  998. if(allCells[i].level==1 && allCells[i].id!=selfParentCell.id){
  999. allTopParentCell.push(allCells[i]);
  1000. }
  1001. }
  1002. return allTopParentCell;
  1003. }

  

mxGraph实现鱼骨图(因果图)(转自CSDN,链接附于文中)的更多相关文章

  1. jQuery横向上下排列鱼骨图形式信息展示代码时光轴样式(转自CSDN,原文链接附于文中)

    原文链接:http://www.jqueryfuns.com/resource/2173 $.fn.fishBone = function(data) { var colors = ['#F89782 ...

  2. 鱼骨时间轴案例(转自CSDN,原文链接附于文中)

    $.fn.fishBone = function(data) { var colors = ['#F89782','#1A84CE']; /**入口*/ //1.创建dom $(this).child ...

  3. 鱼骨图是什么?怎么用iMindMap画鱼骨图?

    鱼骨图是一种发现问题"根本原因"的方法,它也可以称之为"因果图".其特点是简捷实用,深入直观."鱼头"处标注的一般是问题或后果.按出现机会多 ...

  4. minitab的鱼骨图的制作

    Minitab的操作路径为:主菜单Stat > Quality Tools > Cause-and-Effect 先分别选择列"Man.Machine.Material.Meth ...

  5. 【数据结构与算法】自己动手实现图的BFS和DFS(附完整源码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/19617187 图的存储结构 本文的重点在于图的深度优先搜索(DFS)和广度优先搜索(BFS ...

  6. 图形化Cisco设备管理实践(附安装配置视频)

    图形化Cisco设备管理实践 Ciscoworks 2000是Cisco公司推出的基于SNMP协议的网络管理系统,通过它网络管理人员可以方便快捷地完成设备的配置.管理.监控和故障分析等任务, Cisc ...

  7. arcgis api 3.x for js 实现克里金插值渲染图不依赖 GP 服务(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  8. 一图解释PHPstorm代码片段设置---附官方文档(转)

    参考:https://blog.csdn.net/thinkthewill/article/details/81145106 资料 设置片段[官方设置教程] 设置变量对话框[官方设置教程] phpst ...

  9. 【VIP视频网站项目一】搭建视频网站的前台页面(导航栏+轮播图+电影列表+底部友情链接)

    首先来直接看一下最终的效果吧: 项目地址:https://github.com/xiugangzhang/vip.github.io 在线预览地址:https://xiugangzhang.githu ...

随机推荐

  1. android 位置记录软件

    行者 用的百度高德的方案,没有偏移问题endomondo,咕咚,行者.endomondo是国外软件,运行稳定,但GPS记录漂移比较严重:咕咚的GPS位置记录比较准确,缺点是容易崩溃,譬如记录过程中来个 ...

  2. 并发之痛 Thread,Goroutine,Actor

    转自:http://jolestar.com/parallel-programming-model-thread-goroutine-actor/ 先梳理下两个概念,几乎所有讲并发的文章都要先讲这两个 ...

  3. Ubuntu 16.04.3 LTS 安装 MongoDB

    1.安装Ubuntu16.04 运行sudo apt-get install mongodb安装Mongodb 如果没有MongoDB库,则运行sudo apt-get update更新库. 2.运行 ...

  4. udp重发

    最近在处理框架通讯方面的问题,通过积累的开发经验,其实在很多情况(尤其是实时大数据量),udp是占有很多优势的:不需要连接,只管发送,理论上要快很多; 另外在穿墙上占有很大优势: 但是最大的一个问题就 ...

  5. vs2017安装和使用教程(详细)

    借鉴:https://blog.csdn.net/qq_36556893/article/details/79430133#一.官网下载

  6. 值得推荐的五大敏捷PHP开发框架

    各位开发者,对于在HTML中混乱使用PHP的人来说,我们给大家推荐几款PHP敏捷开发的框架,以及它们为什么能够流行. 在我们开始之前,先了解敏捷开发是个什么东东. 敏捷是一种软件开发方法,每次开发计划 ...

  7. 【Python】Elasticsearch和elasticsearch_dsl

    官网:https://elasticsearch-py.readthedocs.io/en/master/api.html 官网:https://github.com/elastic/elastics ...

  8. 【MySQL】5.7 复制

    参考:http://www.cnblogs.com/zhoujinyi/p/5704567.html 参考:http://www.innomysql.com/article/25656.html 参考 ...

  9. 解决由于服务器调用删除或添加字段后CXF客户端未更新导致异常问题org.apache.cxf.interceptor.Fault: Unmarshalling Error: Unexpected element

    采用CXF客户端调用Webservice服务,由于服务端时不时会对Webservice服务删除或添加一些字段,而CXF未及时更新客户端代码导致再次调用服务时报异常错误: Interceptor for ...

  10. python 建立多维列表

    今天用到在网上没有找到合适的思路,于是自己动手写了一个,作为记录. dpa = [] dpb = [] dpc = [] for i in range(21): dpa.append(0) for i ...