Browse Source

Merge branch 'ETA_1.1.5'

hbchen 1 year ago
parent
commit
dff6972511
80 changed files with 5941 additions and 222 deletions
  1. 8 1
      build/webpack.base.conf.js
  2. 1 1
      config/index.js
  3. 3 1
      package.json
  4. 46 2
      src/api/modules/sandApi.js
  5. BIN
      src/assets/img/sand_new/add_ico.png
  6. BIN
      src/assets/img/sand_new/add_outline.png
  7. BIN
      src/assets/img/sand_new/arrow_black_down.png
  8. BIN
      src/assets/img/sand_new/copy.png
  9. BIN
      src/assets/img/sand_new/delete_outline.png
  10. BIN
      src/assets/img/sand_new/delete_outline_1.png
  11. BIN
      src/assets/img/sand_new/double-arrow-round.png
  12. BIN
      src/assets/img/sand_new/double-arrow-straight.png
  13. BIN
      src/assets/img/sand_new/double-arrow.png
  14. BIN
      src/assets/img/sand_new/edit_outline.png
  15. BIN
      src/assets/img/sand_new/eye-show-black.png
  16. BIN
      src/assets/img/sand_new/mindmap-double.png
  17. BIN
      src/assets/img/sand_new/mindmap-right.png
  18. BIN
      src/assets/img/sand_new/no-arrow-round.png
  19. BIN
      src/assets/img/sand_new/no-arrow-straight.png
  20. BIN
      src/assets/img/sand_new/no-arrow.png
  21. BIN
      src/assets/img/sand_new/remove.png
  22. BIN
      src/assets/img/sand_new/save-other.png
  23. BIN
      src/assets/img/sand_new/single-arrow-round.png
  24. BIN
      src/assets/img/sand_new/single-arrow-straight.png
  25. BIN
      src/assets/img/sand_new/single-arrow.png
  26. BIN
      src/assets/img/sand_new/style-black.png
  27. BIN
      src/assets/img/sand_new/style-blue-light.png
  28. BIN
      src/assets/img/sand_new/style-blue.png
  29. BIN
      src/assets/img/sand_new/style-red.png
  30. BIN
      src/assets/img/sand_new/tools/arrow-end-disabled.png
  31. BIN
      src/assets/img/sand_new/tools/arrow-end.png
  32. BIN
      src/assets/img/sand_new/tools/arrow-start-disabled.png
  33. BIN
      src/assets/img/sand_new/tools/arrow-start.png
  34. BIN
      src/assets/img/sand_new/tools/background-color-disabled.png
  35. BIN
      src/assets/img/sand_new/tools/background-color.png
  36. BIN
      src/assets/img/sand_new/tools/bold-disabled.png
  37. BIN
      src/assets/img/sand_new/tools/bold.png
  38. BIN
      src/assets/img/sand_new/tools/italic-disabled.png
  39. BIN
      src/assets/img/sand_new/tools/italic.png
  40. BIN
      src/assets/img/sand_new/tools/line-color-disabled.png
  41. BIN
      src/assets/img/sand_new/tools/line-color.png
  42. BIN
      src/assets/img/sand_new/tools/line-height-disabled.png
  43. BIN
      src/assets/img/sand_new/tools/line-height.png
  44. BIN
      src/assets/img/sand_new/tools/line-style-bend-round.png
  45. BIN
      src/assets/img/sand_new/tools/line-style-bend.png
  46. BIN
      src/assets/img/sand_new/tools/line-style-disabled.png
  47. BIN
      src/assets/img/sand_new/tools/line-style-straight.png
  48. BIN
      src/assets/img/sand_new/tools/line-style.png
  49. BIN
      src/assets/img/sand_new/tools/line-type-disabled.png
  50. BIN
      src/assets/img/sand_new/tools/line-type.png
  51. BIN
      src/assets/img/sand_new/tools/line-width-disabled.png
  52. BIN
      src/assets/img/sand_new/tools/line-width.png
  53. BIN
      src/assets/img/sand_new/tools/redo-disabled.png
  54. BIN
      src/assets/img/sand_new/tools/redo.png
  55. BIN
      src/assets/img/sand_new/tools/select-icon.png
  56. BIN
      src/assets/img/sand_new/tools/text-align-disabled.png
  57. BIN
      src/assets/img/sand_new/tools/text-align.png
  58. BIN
      src/assets/img/sand_new/tools/text-color-disabled.png
  59. BIN
      src/assets/img/sand_new/tools/text-color.png
  60. BIN
      src/assets/img/sand_new/tools/underline-disabled.png
  61. BIN
      src/assets/img/sand_new/tools/underline.png
  62. BIN
      src/assets/img/sand_new/tools/undo-disabled.png
  63. BIN
      src/assets/img/sand_new/tools/undo.png
  64. BIN
      src/assets/img/set_m/down_black.png
  65. BIN
      src/assets/img/set_m/slide_black.png
  66. 1 6
      src/main.js
  67. 17 3
      src/routes/modules/chartRoutes.js
  68. 5 1
      src/utils/buttonConfig.js
  69. 158 0
      src/views/sandbox_manage/common/edge.js
  70. 282 116
      src/views/sandbox_manage/common/events.js
  71. 46 17
      src/views/sandbox_manage/common/gragh.js
  72. 566 0
      src/views/sandbox_manage/common/mindmap.js
  73. 276 64
      src/views/sandbox_manage/common/node.js
  74. 16 2
      src/views/sandbox_manage/common/options.js
  75. 57 5
      src/views/sandbox_manage/common/toolConfig.js
  76. 2 0
      src/views/sandbox_manage/index.vue
  77. 1704 0
      src/views/sandbox_manage/index_new_version.vue
  78. 2505 0
      src/views/sandbox_manage/sandFlowNew/index.vue
  79. 84 0
      src/views/sandbox_manage/sandFlowNew/popover.vue
  80. 164 3
      src/vuex/modules/sand.js

+ 8 - 1
build/webpack.base.conf.js

@@ -43,7 +43,8 @@ module.exports = {
     }
   },
   externals:{
-	  "vue":"Vue"
+	  "vue":"Vue",
+    "jQuery": "jQuery"
   },
   plugins: [
     new webpack.DllReferencePlugin({
@@ -57,6 +58,12 @@ module.exports = {
       threshold: 10240, // 对超过10k的数据压缩
       deleteOriginalAssets: false, // 是否删除未压缩的源文件
     }),
+    // new webpack.ProvidePlugin({
+    //   $: "jquery",
+    //   jQuery: "jquery",
+    //   'window.jQuery': 'jquery',
+    //   jQuery: 'jquery'
+    // })
   ],
   module: {
     rules: [

+ 1 - 1
config/index.js

@@ -38,7 +38,7 @@ module.exports = {
     proxyTable:{
 		'/adminapi': {
       target: "http://8.136.199.33:7778",
-      // target: "http://192.168.20.49:8605",
+      // target: "http://192.168.77.7:8606",
 			// secure:false,  // 如果是https接口,需要配置这个参数
 			changeOrigin:true, // 如果接口跨域,需要进行这个参数配置
 			pathRewrite:{

+ 3 - 1
package.json

@@ -13,6 +13,7 @@
     "build.test": "node build/build.test.js"
   },
   "dependencies": {
+    "@antv/hierarchy": "^0.6.11",
     "@antv/x6": "^1.29.1",
     "@fullcalendar/interaction": "^5.10.1",
     "@fullcalendar/timegrid": "^5.10.1",
@@ -38,7 +39,7 @@
     "js-md5": "^0.7.3",
     "less-loader": "^4.1.0",
     "lodash": "^4.17.21",
-    "minio": "^7.0.18",
+    "minio": "7.0.18",
     "pptxgenjs": "^3.10.0",
     "qrcode": "^1.4.4",
     "sortablejs": "^1.15.0",
@@ -52,6 +53,7 @@
     "vue-codemirror": "^4.0.6",
     "vue-count-to": "^1.0.13",
     "vue-froala-wysiwyg": "^3.1.0",
+    "vue-giant-tree": "^1.0.0",
     "vue-masonry": "^0.16.0",
     "vue-pdf": "^4.2.0",
     "vue-qr": "^2.3.0",

+ 46 - 2
src/api/modules/sandApi.js

@@ -120,6 +120,50 @@ export default {
 	 */
 	sandDelVersion: params => {
 		return http.post('/sandbox/version/delete',params)
-	}
-
+	},
+	// 以下是新版本逻辑图的接口
+	// 获取沙盘图分类
+	getSandboxClassify: params => {
+		return http.get('/sandbox/classify/list',params)
+	},
+	// 获取沙盘图分类-仅有分类
+	getSandboxClassifyOnly: params => {
+		return http.get('/sandbox/classifyList',params)
+	},
+	//新增沙盘图分类
+	addSandboxClassify: params => {
+		return http.post('/sandbox/classify/add',params)
+	},
+	// 编辑沙盘图分类
+	editSandboxClassify: params => {
+		return http.post('/sandbox/classify/edit',params)
+	},
+	//沙盘图/分类移动
+	sandboxClassifyMove: params => {
+		return http.post('/sandbox/classify/move',params)
+	},
+	// 删除沙盘图分类检查
+	deleteSandboxClassifyCheck: params => {
+		return http.post('/sandbox/classify/delete/check',params)
+	},
+	// 删除沙盘图/沙盘图分类
+	deleteSandbox: params => {
+		return http.post('/sandbox/classify/delete',params)
+	},
+	// 沙盘图列表详情
+	getSandboxListV2: params => {
+		return http.get('/sandbox/listV2',params)
+	},
+	//沙盘图详情 SandboxId
+	getSandboxDetail: params => {
+		return http.get('/sandbox/detail',params)
+	},
+	//保存沙盘图V2
+	sandboxSaveV2: params => {
+		return http.post('/sandbox/saveV2',params)
+	},
+	//沙盘图链接检测 {EdbInfoIdList,ChartInfoIdList,ReportIdList}
+	sandboxLinkCheck: params => {
+		return http.post('/sandbox/link/check',params)
+	},
 }

BIN
src/assets/img/sand_new/add_ico.png


BIN
src/assets/img/sand_new/add_outline.png


BIN
src/assets/img/sand_new/arrow_black_down.png


BIN
src/assets/img/sand_new/copy.png


BIN
src/assets/img/sand_new/delete_outline.png


BIN
src/assets/img/sand_new/delete_outline_1.png


BIN
src/assets/img/sand_new/double-arrow-round.png


BIN
src/assets/img/sand_new/double-arrow-straight.png


BIN
src/assets/img/sand_new/double-arrow.png


BIN
src/assets/img/sand_new/edit_outline.png


BIN
src/assets/img/sand_new/eye-show-black.png


BIN
src/assets/img/sand_new/mindmap-double.png


BIN
src/assets/img/sand_new/mindmap-right.png


BIN
src/assets/img/sand_new/no-arrow-round.png


BIN
src/assets/img/sand_new/no-arrow-straight.png


BIN
src/assets/img/sand_new/no-arrow.png


BIN
src/assets/img/sand_new/remove.png


BIN
src/assets/img/sand_new/save-other.png


BIN
src/assets/img/sand_new/single-arrow-round.png


BIN
src/assets/img/sand_new/single-arrow-straight.png


BIN
src/assets/img/sand_new/single-arrow.png


BIN
src/assets/img/sand_new/style-black.png


BIN
src/assets/img/sand_new/style-blue-light.png


BIN
src/assets/img/sand_new/style-blue.png


BIN
src/assets/img/sand_new/style-red.png


BIN
src/assets/img/sand_new/tools/arrow-end-disabled.png


BIN
src/assets/img/sand_new/tools/arrow-end.png


BIN
src/assets/img/sand_new/tools/arrow-start-disabled.png


BIN
src/assets/img/sand_new/tools/arrow-start.png


BIN
src/assets/img/sand_new/tools/background-color-disabled.png


BIN
src/assets/img/sand_new/tools/background-color.png


BIN
src/assets/img/sand_new/tools/bold-disabled.png


BIN
src/assets/img/sand_new/tools/bold.png


BIN
src/assets/img/sand_new/tools/italic-disabled.png


BIN
src/assets/img/sand_new/tools/italic.png


BIN
src/assets/img/sand_new/tools/line-color-disabled.png


BIN
src/assets/img/sand_new/tools/line-color.png


BIN
src/assets/img/sand_new/tools/line-height-disabled.png


BIN
src/assets/img/sand_new/tools/line-height.png


BIN
src/assets/img/sand_new/tools/line-style-bend-round.png


BIN
src/assets/img/sand_new/tools/line-style-bend.png


BIN
src/assets/img/sand_new/tools/line-style-disabled.png


BIN
src/assets/img/sand_new/tools/line-style-straight.png


BIN
src/assets/img/sand_new/tools/line-style.png


BIN
src/assets/img/sand_new/tools/line-type-disabled.png


BIN
src/assets/img/sand_new/tools/line-type.png


BIN
src/assets/img/sand_new/tools/line-width-disabled.png


BIN
src/assets/img/sand_new/tools/line-width.png


BIN
src/assets/img/sand_new/tools/redo-disabled.png


BIN
src/assets/img/sand_new/tools/redo.png


BIN
src/assets/img/sand_new/tools/select-icon.png


BIN
src/assets/img/sand_new/tools/text-align-disabled.png


BIN
src/assets/img/sand_new/tools/text-align.png


BIN
src/assets/img/sand_new/tools/text-color-disabled.png


BIN
src/assets/img/sand_new/tools/text-color.png


BIN
src/assets/img/sand_new/tools/underline-disabled.png


BIN
src/assets/img/sand_new/tools/underline.png


BIN
src/assets/img/sand_new/tools/undo-disabled.png


BIN
src/assets/img/sand_new/tools/undo.png


BIN
src/assets/img/set_m/down_black.png


BIN
src/assets/img/set_m/slide_black.png


+ 1 - 6
src/main.js

@@ -151,12 +151,7 @@ router.beforeEach(async(to, from, next) => {
 
   /* 沙盘详情name添加 */
   if (to.path === "/sandflow") {
-    to.matched[1].name =
-      to.query.type === "view"
-        ? "查看沙盘"
-        : to.query.id
-        ? "编辑沙盘"
-        : "添加沙盘";
+    to.matched[1].name = to.query.SandboxId? "编辑逻辑": "添加逻辑";
   }
 
   if (to.path === "/analyseVariety") {

+ 17 - 3
src/routes/modules/chartRoutes.js

@@ -166,15 +166,29 @@ export default [
 		component: home,
 		name: 'ETA逻辑',
 		hidden: false,
-		children: [{
+		children: [
+			// {
+			// 	path: "sandlist", // 旧版本
+			// 	name: 'ETA逻辑',
+			// 	component: () => import('@/views/sandbox_manage/index.vue'),
+			// 	hidden: false,
+			// },
+			// 改版后的ETA逻辑图
+			{
 				path: "sandlist",
 				name: 'ETA逻辑',
-				component: () => import('@/views/sandbox_manage/index.vue'),
+				component: () => import('@/views/sandbox_manage/index_new_version.vue'),
 				hidden: false,
 			},
+			// {
+			// 	path: "sandflow", // 旧版本
+			// 	component: () => import('@/views/sandbox_manage/sandFlow/index.vue'),
+			// 	hidden: true,
+			// },
 			{
 				path: "sandflow",
-				component: () => import('@/views/sandbox_manage/sandFlow/index.vue'),
+				name: '添加逻辑',
+				component: () => import('@/views/sandbox_manage/sandFlowNew/index.vue'),
 				hidden: true,
 			}
 		]

+ 5 - 1
src/utils/buttonConfig.js

@@ -425,10 +425,14 @@ export const etaTablePermission = {
  * --------------------------------------------------------------------------ETA逻辑------------------------------------------------
 */
 export const sandboxPermission = {
-    sandbox_variety:'sandbox:variety',//沙盘品种选择,控制筛选项和列表项
+    sandbox_variety:'sandbox:variety',//沙盘品种选择,添加目录的时候控制显示
     sandbox_addMy:'sandbox:addMy',//复制图片
     sandbox_del:'sandbox:del',//删除
     sandbox_saveView:'sandbox:saveView',//添加/编辑/查看
+    sandbox_search:'sandbox:search',//搜索
+    sandbox_classify_move:'sandbox:classify:move',//分类操作:移动
+    sandbox_classify_del:'sandbox:classify:del',//分类操作:删除
+    sandbox_classify_addEdit:'sandbox:classify:addEdit',//分类操作:添加/编辑
 }
 /*
  * --------------------------------------------------------------------------语义分析------------------------------------------------

+ 158 - 0
src/views/sandbox_manage/common/edge.js

@@ -0,0 +1,158 @@
+import { configOpt } from './toolConfig';
+
+const { line} = configOpt;
+import store from "@/vuex/index"
+
+const styleConfig=store.state.sand.styleConfig
+// export const myEdges = [
+// 	{
+// 		type: 'noArrowStraight',
+//     ImgUrl:'~@/assets/icons/arrow.svg'
+// 	}
+// ]
+/**type--类型  xP--定位点的横坐标 yP--定位点的纵坐标
+ * 创建的线条
+ * 斜直线无箭头、斜直线单项箭头、斜直线双线箭头
+ * 弯折线无箭头、弯折线单项、弯折线双向箭头
+ * 圆角弯折线无箭头、圆角弯折线单项、圆角弯折线双向箭头
+ */
+export const myEdgeOption = (type,xP=0,yP=0) => {
+  // 斜直线
+  let skewLine={
+    source: { x: xP-40, y: yP-40 },
+    target: { x: xP+40 ,y: yP+40 },
+    router: {
+      name: 'normal',
+      args: {
+        padding: {
+          left: 10,
+        }
+      }
+    },
+    attrs:{
+      line:{
+        strokeDasharray:null,
+        stroke: styleConfig.lineColor,
+        strokeWidth: line.width,
+        sourceMarker: {},//起始箭头 
+        targetMarker: {},//终止箭头
+      }
+    }
+  }
+  //弯折线
+  let bendLine={
+    source: { x: xP-40, y: yP-40 },
+    target: { x: xP+40 ,y: yP+40 },
+    vertices:[{x:xP,y:yP-40},{x:xP,y:yP+40}],
+    attrs:{
+      line:{
+        strokeDasharray:null,
+        stroke: styleConfig.lineColor,
+        strokeWidth: line.width,
+        sourceMarker: {},//起始箭头 
+        targetMarker: {},//终止箭头
+      }
+    }
+  }
+	switch (type) {
+		case 'noArrowStraight': 
+			return skewLine
+		case 'singleArrowStraight': 
+			return {
+				...skewLine,
+        attrs:{
+          line:{
+            strokeDasharray:null,
+            stroke: styleConfig.lineColor,
+            strokeWidth: line.width,
+            sourceMarker: {},//起始箭头 
+            targetMarker: "classic",//终止箭头
+          }
+        },
+			}
+		case 'doubleArrowStraight': 
+			return {
+				...skewLine,
+        attrs:{
+          line:{
+            strokeDasharray:null,
+            stroke: styleConfig.lineColor,
+            strokeWidth: line.width,
+            sourceMarker: "classic",//起始箭头 
+            targetMarker: "classic",//终止箭头
+          }
+        },
+			}
+		case 'noArrowBend': 
+			return bendLine
+		case 'singleArrowBend': 
+			return {
+        ...bendLine,
+        attrs:{
+          line:{
+            strokeDasharray:null,
+            stroke: styleConfig.lineColor,
+            strokeWidth: line.width,
+            sourceMarker: {},//起始箭头 
+            targetMarker: "classic",//终止箭头
+          }
+        },
+			}
+    case 'doubleArrowBend':
+      return {
+        ...bendLine,
+        attrs:{
+          line:{
+            strokeDasharray:null,
+            stroke: styleConfig.lineColor,
+            strokeWidth: line.width,
+            sourceMarker: "classic",//起始箭头 
+            targetMarker: "classic",//终止箭头
+          }
+        },
+      }
+    case 'noArrowRoundBend': 
+			return {
+        ...bendLine,
+        connector: {
+          name: 'rounded',
+          args: { radius: 8 },
+        },
+      }
+    case 'singleArrowRoundBend': 
+			return {
+        ...bendLine,
+        connector: {
+          name: 'rounded',
+          args: { radius: 8 },
+        },
+        attrs:{
+          line:{
+            strokeDasharray:null,
+            stroke: styleConfig.lineColor,
+            strokeWidth: line.width,
+            sourceMarker: {},//起始箭头 
+            targetMarker: "classic",//终止箭头
+          }
+        },
+      }
+    case 'doubleArrowRoundBend': 
+			return {
+        ...bendLine,
+        connector: {
+          name: 'rounded',
+          args: { radius: 8 },
+        },
+        attrs:{
+          line:{
+            strokeDasharray:null,
+            stroke: styleConfig.lineColor,
+            strokeWidth: line.width,
+            sourceMarker: 'classic',//起始箭头 
+            targetMarker: "classic",//终止箭头
+          }
+        },
+      }
+  }
+}
+

+ 282 - 116
src/views/sandbox_manage/common/events.js

@@ -1,14 +1,17 @@
 import { store } from "../../../main";
+import { configOpt } from './toolConfig';
+import _ from "lodash"
+const { line} = configOpt;
 
 /* 节点操作监听事件 */
-export const myEvents = (graph) => {
-
+export const myEvents = (graph,mindmapDataUseFun) => {
+		
 		/* 节点双击编辑 */
 		graph.on('node:dblclick', ({ node, e }) => {
-
 			// 节点当前设置的样式同步到编辑区
-			const { text, rect} = node.attrs;
 
+			const { text, rect} = node.attrs;
+			// console.log(node,'nodenode');
 			const edit_area = document.createElement('div');
 			edit_area.contentEditable = "true";
 			edit_area.id = "editable-wrapper";
@@ -16,20 +19,29 @@ export const myEvents = (graph) => {
 			edit_area.style.outline = "none";
 			edit_area.style.padding = "4px";
 			edit_area.style.boxSizing = "border-box";
-			$('#flow-container')[0].appendChild(edit_area);
+			$('#sand-chart-container')[0].appendChild(edit_area);
 
 			const position = node.position();
       const size = node.size();
+			// console.log(position,size);
       // const pos = graph.localToClient(position);
       const pos = graph.localToGraph(position);
       const zoom = graph.zoom();
-      let width = size.width,
-      height = size.height;
+      let width = size.width,height = size.height;
 
 			// const edit_area =  $('#editable-wrapper')[0];
+			// if(node.shape.indexOf('mindmap')!==-1){
+
+			// 	edit_area.innerText = text.text
+			// 	node.attr('text/text', '');
+			// }else{
+				edit_area.innerText = text.text || text.textWrap.text || ' ';
+				// 开启editText的事务 
+				graph.startBatch('editText')
+				node.attr('text/textWrap/text', '');
+				
+			// }
 
-			edit_area.innerText = text.text || text.textWrap.text || ' ';
-			node.attr('text/textWrap/text', '');
 
 			edit_area.style.left = `${pos.x}px`;
 			edit_area.style.top = `${pos.y}px`;
@@ -63,29 +75,42 @@ export const myEvents = (graph) => {
 			//失焦后设置内容
 			edit_area.onblur = () => {
 				let newval = edit_area.innerText.replace(/(\n[\s\t]*\r*\n)/g,'\n');
+
+				// console.log(node.shape,'edit_area');
+
 				node.attr('text/textWrap/text', newval);
 				node.attr('text/text', newval);
-				
-				const domH = (edit_area.getBoundingClientRect().height) /zoom;
-				node.size(width,domH > 50 ? domH : 50);
-				
-				$('#flow-container')[0].removeChild(edit_area);
+				// 关闭editText的事务
+				graph.stopBatch('editText')
+
+				if(node.shape.indexOf('mindmap')!==-1){
+					const mindmapDataUse=mindmapDataUseFun()
+					let ids = node.id.split('-')
+					let mindmapDataIndex = mindmapDataUse.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
+					let mindMapDataCurrent = mindmapDataUse[mindmapDataIndex]?mindmapDataUse[mindmapDataIndex].mindmapData:{}
+
+					let findId = ids[0]
+					for (let i = 1; i < ids.length; i++) {
+						const element = ids[i];
+						findId = findId+'-'+element
+						mindMapDataCurrent=mindMapDataCurrent.children.find(it => it.id==findId)
+					}
+					mindMapDataCurrent.label = newval
+				}
+				if(node.shape.indexOf('mindmap')==-1){
+					const domH = (edit_area.getBoundingClientRect().height) /zoom;
+					node.size(width,domH > 50 ? domH : 50);
+					// node.size(width,domH);
+				}else{
+					const domH = (edit_area.getBoundingClientRect().height) /zoom;
+					// node.size(width,domH);
+					node.size(width,domH > 50 ? domH : 50);
+					// node.fit({ deep: true })
+				}
+
+				$('#sand-chart-container')[0].removeChild(edit_area);
 			}
-
-
-			// node.addTools({
-			// 	name: 'node-editor',
-			// 	args: {
-			// 		event: e,
-			// 		attrs: {
-			// 			fontSize: text.fontSize,
-			// 			color: text.fill,
-			// 			backgroundColor: rect.fill === 'transparent' ? '#fff' : rect.fill,
-			// 		},
-			// 	},
-			// })
 		})
-
 		/* 鼠标移入移出控制连接桩 */
 		graph.on('node:mouseenter', ({ node, e }) => {
 			// console.log(node)
@@ -102,79 +127,127 @@ export const myEvents = (graph) => {
 
 		/* 节点右键 */
 		graph.on('node:contextmenu',({node,e}) => {
-			graph.select(node);
+			graph.resetSelection(node);
 			const dom = $('#contextMenu-wrapper')[0];
 			dom.style.left = e.clientX-3 + 'px';
 			dom.style.top = e.clientY-3 + 'px';
 		})
 
 		/* 选中事件 */
-		graph.on('cell:selected',({cell,options}) => {
-			// console.log(cell)
-			//节点
-			if(cell.shape === 'rect') {
-				const { key } = cell.data;
-
-				const options = setSelectedOptions(cell.attrs,key);
-				//设置toolbar状态
-				store.commit('sand/SET_SELECT_STATUS',{
-					key,
-					options
-				});
-			}else if(cell.isEdge()) {
-
-				const { line } = cell.attrs;
-				//高亮选中
-				// cell.attr('line', { stroke: '#f00', strokeWidth: 3 });
-				//设置toolbar状态
-				store.commit('sand/SET_SELECT_STATUS',{
-					key: 'line',
-					options: {
-						line: {
-							width: line.strokeWidth,
-							color: line.stroke,
-						}
-					}
-				});
-			}
-		})
+		// graph.on('cell:selected',({cell,options}) => {
+		// 	// console.log(cell)
+		// 	//节点
+		// 	if(cell.shape === 'rect') {
+		// 		const { key } = cell.data;
+
+		// 		const options = setSelectedOptions(cell.attrs,key);
+		// 		//设置toolbar状态
+		// 		store.commit('sand/SET_SELECT_STATUS',{
+		// 			key,
+		// 			options
+		// 		});
+		// 	}else if(cell.isEdge()) {
+
+		// 		const { line } = cell.attrs;
+		// 		//高亮选中
+		// 		// cell.attr('line', { stroke: '#f00', strokeWidth: 3 });
+		// 		//设置toolbar状态
+		// 		store.commit('sand/SET_SELECT_STATUS',{
+		// 			key: 'line',
+		// 			options: {
+		// 				line: {
+		// 					width: line.strokeWidth,
+		// 					color: line.stroke,
+		// 				}
+		// 			}
+		// 		});
+		// 	}
+		// })
 		
 		/* 点击空白区域清空选区 屏蔽工具栏 */
-		graph.on('blank:click',() => {
-			graph.cleanSelection();
-			store.commit('sand/SET_SELECT_STATUS',{key:'default'});
-			store.commit('sand/SET_SELECT_CELL',null);
+		// graph.on('blank:click',() => {
+		// 	graph.cleanSelection();
+		// 	// store.commit('sand/SET_SELECT_STATUS',{key:'default'});
+		// 	store.commit('sand/SET_SELECT_CELLS',[]);
 
-			if($('#editable-wrapper')[0]) $('#editable-wrapper')[0].blur();
-		})
+		// 	if($('#editable-wrapper')[0]) $('#editable-wrapper')[0].blur();
+		// })
+
+		const changeSelection=_.debounce((selected)=> {
+			// console.log(selected,'selected');
+			store.commit('sand/SET_SELECT_CELLS',selected)
+		},50)
 
 		/* 监听选中事件 */
 		graph.on('selection:changed',({selected}) => {
-			
-			selected.length ? store.commit('sand/SET_SELECT_CELL',selected[0]) : store.commit('sand/SET_SELECT_CELL',null);
+			// console.log(selected,'选中修改');
+			// selected.length ? store.commit('sand/SET_SELECT_CELL',selected[0]) : store.commit('sand/SET_SELECT_CELL',null);
+			changeSelection(selected)
+		})
+
+		/* 监听选中事件 */
+		graph.on('edge:mouseenter', ({ cell }) => {
+			// console.log(cell,'myEdgeOption');
+			// console.log(cell.store.data);
+			if(cell.store.data.shape=="mindmap-edge"){
+				// 思维导图的边
+			}else{
+				cell.addTools([
+					{
+						name: 'source-arrowhead',
+						args: {
+							attrs:{
+								d: 'M 8 -6 -8 0 8 6 Z',
+								fill: line.color
+							}
+						}
+					},
+					{
+						name: 'target-arrowhead',
+						args: {
+							attrs:{
+								d: 'M -8 -6 8 0 -8 6 Z',
+								fill: line.color
+							}
+						}
+					},
+					{
+						name:"vertices",
+						args:{
+							addable:false,
+							removable:false
+						}
+					},
+					{
+						name: 'segments'
+					}
+				])
+			}
+
+		})
+		
+		graph.on('edge:mouseleave', ({ cell }) => {
+			if(cell.store.data.shape=="mindmap-edge"){
+				// 思维导图的边
+			}else{
+				cell.removeTools()
+			}
 		})
 }
 
 /* 绑定键盘事件 */
-export const bindKey = (graph) => {
+export const bindKey = (graph,mindmapDataUseFun,mindmapAssistData) => {
 	 // 删除
 	graph.bindKey(['delete', 'backspace'], () => {
-		const select_cell = graph.getSelectedCells();
-			if (select_cell.length) {
-				// 移除工具
-				select_cell.forEach(item => item.removeTools());
-				graph.removeCells(select_cell)
-
-				//重置工具栏
-				store.commit('sand/SET_SELECT_STATUS',{key:'default'});
-				store.commit('sand/SET_SELECT_CELL',null);
-			}
-			return false
+		const mindmapDataUse=mindmapDataUseFun()
+		deleteNodes(graph,mindmapDataUse,mindmapAssistData)
+		return false
 	}, 'keydown');
 
 	/* cv */
 	graph.bindKey('ctrl+c', () => {
-		const select_cell = graph.getSelectedCells();
+		// 去除思维导图的节点
+		const select_cell = graph.getSelectedCells().filter(it => it.shape.indexOf('mindmap')==-1);
 		if (select_cell.length) {
 			graph.copy(select_cell)
 		}
@@ -189,35 +262,37 @@ export const bindKey = (graph) => {
 		}
 		return false
 	});
+
+	graph.bindKey('ctrl+z', () => {
+		if(graph.canUndo()){
+			graph.undo()
+		}
+		return false
+	});
+	graph.bindKey('ctrl+y', () => {
+		if(graph.canRedo()){
+			graph.redo()
+		}
+		return false
+	});
 }
 
 /* 右键事件 */
-export const contextEvent = (graph,key) => {
-	console.log(key)
+export const contextEvent = (graph,key,mindmapDataUse,mindmapAssistData) => {
+	// console.log(key)
 	switch (key) {
 		case 'copy':
 			nodeCopyAndPaste(graph);
 			break;
 		case 'del': 
-			nodeDelete(graph);
+			nodeDelete(graph,mindmapDataUse,mindmapAssistData);
 			break;
 	}
 }
 
 /* 删除节点 清空选区*/
-const nodeDelete = (graph) => {
-	const select_cell = graph.getSelectedCells();
-	console.log(select_cell)
-	if (select_cell.length) {
-
-		// 移除工具
-		select_cell.forEach(item => item.removeTools());
-		graph.removeCells(select_cell);
-
-		//重置工具栏
-		store.commit('sand/SET_SELECT_STATUS',{key:'default'});
-		store.commit('sand/SET_SELECT_CELL',null);
-	}
+const nodeDelete = (graph,mindmapDataUse,mindmapAssistData) => {
+	deleteNodes(graph,mindmapDataUse,mindmapAssistData)
 }
 /* 复制粘贴节点 */
 const nodeCopyAndPaste = (graph) => {
@@ -233,25 +308,116 @@ const nodeCopyAndPaste = (graph) => {
 }
 
 /* 关联三种基础图形选中样式 */
-const setSelectedOptions = ({ rect, text }, key) => {
-	return ['rect','date'].includes(key) ? {
-		text: { //文本设置
-			size: text.fontSize,
-			fontWeight: text.fontWeight, //
-			color: text.fill
-		},
-		border: { //线框设置
-			// isDash: 0, // 0实 1虚线
-			width: rect.strokeWidth,
-			fill: rect.fill,
-			borderColor: rect.stroke,
-		},
-
-	} : key === 'text' ? {
-		text: { //文本设置
-			size: text.fontSize,
-			fontWeight: text.fontWeight,
-			color: text.fill
-		},
-	}: {}
+// const setSelectedOptions = ({ rect, text }, key) => {
+// 	console.log({ rect, text },'{ rect, text }',key);
+// 	return ['rect','date'].includes(key) ? {
+// 		text: { //文本设置
+// 			size: text.fontSize,
+// 			fontWeight: text.fontWeight, //
+// 			color: text.fill
+// 		},
+// 		border: { //线框设置
+// 			// isDash: 0, // 0实 1虚线
+// 			width: rect.strokeWidth,
+// 			fill: rect.fill,
+// 			borderColor: rect.stroke,
+// 		},
+
+// 	} : key === 'text' ? {
+// 		text: { //文本设置
+// 			size: text.fontSize,
+// 			fontWeight: text.fontWeight,
+// 			color: text.fill
+// 		},
+// 	}: {}
+// }
+
+const deleteNodes=(graph,mindmapDataUse,mindmapAssistData)=>{
+	console.log(mindmapDataUse,mindmapAssistData,'最源数据');
+	const select_cell = graph.getSelectedCells();
+	let delete_cells=[]
+	let mindmapCell=[]
+	// return 
+	// console.log(select_cell.length,'select_cell.length');
+	for (let i = 0; i < select_cell.length; i++) {
+		const cell = select_cell[i];
+		if(cell.shape == "mindmap-edge"){
+			continue
+		}else if(cell.shape.indexOf('mindmap')!=-1){
+			mindmapCell.push(cell)
+			// 拿到该节点的所有子节点
+			let Successors = graph.getSuccessors(cell) 
+			// console.log(Successors,'Successors');
+			mindmapCell = [...mindmapCell,...Successors]
+		}
+		delete_cells.push(cell)
+	}
+	// 去重
+	const uniqueArr = mindmapCell.filter((item, index) => mindmapCell.findIndex(i => i.id === item.id) === index);
+	// console.log(uniqueArr,'uniqueArr');
+
+	delete_cells = [...delete_cells,...uniqueArr]
+	// return 
+	if (delete_cells.length) {
+		// 移除工具
+		delete_cells.forEach(item => item.removeTools());
+		graph.removeCells(delete_cells)
+	}
+	let shouldOperations=[]
+	mindmapDataUse.map((item,index)=>{
+		let levelIds = uniqueArr.filter(mindMap => mindMap.id.startsWith(item.mindmapData.id)).map(mindMap => mindMap.id)
+		// console.log(levelIds,'levelIds');
+		if(!(levelIds && levelIds.length>0)) return 
+		// console.log(levelIds,'levelIds');
+		let mindMapIds=[...levelIds]
+		for (let i = 0; i < levelIds.length; i++) {
+			const element = levelIds[i]
+			mindMapIds=mindMapIds.filter( id => id.indexOf(element) !=0 || id==element)
+		}
+		// console.log(mindMapIds,'mindMapIds');
+		shouldOperations.push(mindMapIds)
+	})
+	// 删除前备份
+	console.log(mindmapDataUse,'元数据');
+	mindmapAssistData.mindmapDataRecoverUse=[JSON.stringify(mindmapDataUse)]
+
+	console.log('删除备份',JSON.parse(mindmapAssistData.mindmapDataRecoverUse[0]),mindmapAssistData.mindmapDataRecoverUse.length,mindmapAssistData.mindmapDataRecoverUse);
+	
+	// console.log(shouldOperations,'shouldOperations');
+	shouldOperations.map(it =>{
+		it.map(it1 =>{
+			deleteMindmapData(it1,mindmapDataUse)
+		})
+	})
+	// console.log(mindmapDataUse,'mindmapDataUse',mindmapAssistData,'mindmapAssistData');
+
+}
+
+const deleteMindmapData=(id,data)=>{
+	// console.log(id,data,'id,data');
+	// return 
+	let ids = id.split('-')
+	let mindmapDataIndex = data.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
+	if(ids.length==1){
+		data.splice(mindmapDataIndex,1)
+		return 
+	}
+
+	let mindmapData = data[mindmapDataIndex].mindmapData
+	let findId = ids[0]
+	for (let i = 1; i < ids.length-1; i++) {
+		const element = ids[i];
+		findId = findId+'-'+element
+		if(!(mindmapData && mindmapData.children)){
+			return 
+		}
+		mindmapData=mindmapData.children.find(it => it.id==findId)
+	}
+	let endId = ids[ids.length-1]
+	if(!(mindmapData && mindmapData.children)){
+		return 
+	}
+	let endIndex = mindmapData.children.findIndex(it => it.id == findId+'-'+endId)
+	mindmapData.children.splice(endIndex,1)
+	// console.log(data);
 }

+ 46 - 17
src/views/sandbox_manage/common/gragh.js

@@ -1,15 +1,28 @@
 import { Graph,Shape } from '@antv/x6';
 import { bindKey,myEvents } from './events';
 import { configOpt } from './toolConfig';
+import store from '@/vuex/index'
+console.log(store,'store');
+const styleConfig=store.state.sand.styleConfig
 
 const { line } = configOpt;
-
-export function myGraph (wrapper) {
+// wrapper DOM的Id mindmapDataUseFun 返回思维导图数组函数,传递给事件 type 模式,编辑和查看
+export function myGraph (wrapper,mindmapDataUseFun,mindmapAssistData,type='edit') {
 	const graph = new Graph({
 		container: document.getElementById(wrapper),
 		// width: $(window).width(),
 		// height: $(window).height(),
-		autoResize: true, 
+		history:{
+			enabled:true,
+			beforeAddCommand(event, args){
+				console.log(event, args,'event, args');
+				if(args.key=='tools'){
+					// 工具的改变不加入撤销和重做的队列
+					return false
+				}
+			}
+		},
+		autoResize: false, 
 		background: {
 			color: '#fff',
 		},
@@ -22,7 +35,12 @@ export function myGraph (wrapper) {
 		selecting: {
 			enabled: true,
 			showNodeSelectionBox: false,
-			multiple: false
+			multiple: true,
+			multipleSelectionModifiers:['shift'],
+			rubberband:true,
+			// rubberNode:true,
+			rubberEdge:true,//加上这个才能框选边 官方配置也不写……
+			modifiers:['ctrl']
 		},
 		snapline: true, //对齐线
 		// panning: { //画布拖动
@@ -40,7 +58,7 @@ export function myGraph (wrapper) {
 		}, //滚轮缩放
 		grid: {
 			size: 10,      // 网格大小
-			visible: true,
+			visible: false,
 		},
 		highlighting: {
 			// 当链接桩可以被链接时,在链接桩外围渲染一个 2px 宽的红色矩形框
@@ -66,10 +84,20 @@ export function myGraph (wrapper) {
 					},
 			},
 		},
+		interacting:type=='view'?{
+			nodeMovable:false,
+			magnetConnectable:false,
+			edgeMovable:false,
+			edgeLabelMovable:false,
+			arrowheadMovable:false,
+			vertexMovable:false,
+			vertexMovable:false,
+			vertexDeletable:false
+		}:{},
 		connecting: {
 			snap: true,
-			// 不允许连接到空白位置
-			allowBlank: false,
+			// 允许连接到空白位置
+			allowBlank: true,
 			// 不允许创建循环连线
 			allowLoop: false,
 			// 不允许在相同节点创建多条边
@@ -91,7 +119,7 @@ export function myGraph (wrapper) {
 				return new Shape.Edge({
 					attrs: {
 						line: {
-							stroke: line.color,
+							stroke: styleConfig.lineColor,
 							strokeWidth: line.width,
 							strokeDasharray: "",//虚线间隔
 							sourceMarker: false,//起始箭头 
@@ -103,25 +131,26 @@ export function myGraph (wrapper) {
 			},
 		},//连线
 		resizing: {
-			enabled: true,
+			enabled: type=='view'?false:true,
 			orthogonal: false,
 		},
 		scaling: {
 			min: 0.5,
 			max: 2
 		},
-        //小地图
-        minimap: {
-            enabled: true,
-            container: document.getElementById("minimap"),
-        }
+		//小地图
+		minimap: {
+				enabled: true,
+				container: document.getElementById("minimap"),
+		}
 	})
 
 	/* 节点操作事件 */
-	myEvents(graph);
+	if(type!='view') myEvents(graph,mindmapDataUseFun);
+	
 
 	/* 键盘事件 */
-	bindKey(graph);
+	if(type!='view') bindKey(graph,mindmapDataUseFun,mindmapAssistData);
 
 	return graph;
-}
+}

+ 566 - 0
src/views/sandbox_manage/common/mindmap.js

@@ -0,0 +1,566 @@
+import { Graph, Cell, Node, Path } from '@antv/x6'
+import Hierarchy from '@antv/hierarchy'
+
+export default {
+  data() {
+    return {
+      mindMapDataCurrent:{},
+      positionCurrent:{},
+      addTypeCurrent:'',
+      styleConfig:{}
+    }
+  },
+  mounted() {
+    
+  },
+  created() {
+    this.styleConfig=this.$store.state.sand.styleConfig
+    // console.log(this.styleConfig,'this.styleConfig');
+    // 中心主题
+    Graph.registerNode(
+      'mindmap-topic',          
+      {
+        inherit: 'rect',
+        markup: [
+          {
+            tagName: 'rect',
+            selector: 'body',
+          },
+          {
+            tagName: 'image',
+            selector: 'leftImg',
+          },
+          {
+            tagName: 'image',
+            selector: 'rightImg',
+          },
+          {
+            tagName: 'text',
+            selector: 'text',
+          },
+        ],
+        attrs: {
+          body: {
+            rx: 6,
+            ry: 6,
+            // stroke: this.$store.state.sand.styleConfig.borderColor,
+            // fill: this.$store.state.sand.styleConfig.backgroundColor,
+            strokeWidth: 1,
+            width:100,
+            height:50
+          },
+          leftImg: {
+            ref: 'body',
+            refX: -16,
+            refY: '50%',
+            refY2: -8,
+            width: 16,
+            height: 16,
+            'xlink:href':
+              'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SYCuQ6HHs5cAAAAAAAAAAAAAARQnAQ',
+            event: 'add:topic:left',
+            class: 'left-topic-image',
+          },
+          rightImg: {
+            ref: 'body',
+            refX: '100%',
+            refY: '50%',
+            refY2: -8,
+            width: 16,
+            height: 16,
+            'xlink:href':
+              'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SYCuQ6HHs5cAAAAAAAAAAAAAARQnAQ',
+            event: 'add:topic:right',
+            class: 'right-topic-image',
+          },
+          text: {
+            fontSize: 14,
+            fill: this.$store.state.sand.styleConfig.color,
+            textWrap:{
+              width:-10
+            }
+          }
+        },
+      },
+      true,
+    )
+    // 左分支主题
+    Graph.registerNode(
+      'mindmap-topic-left',          
+      {
+        inherit: 'rect',
+        markup: [
+          {
+            tagName: 'rect',
+            selector: 'body',
+          },
+          {
+            tagName: 'image',
+            selector: 'leftImg',
+          },
+          {
+            tagName: 'image',
+            selector: 'rightImg',
+          },
+          {
+            tagName: 'text',
+            selector: 'text',
+          },
+        ],
+        attrs: {
+          body: {
+            rx: 6,
+            ry: 6,
+            // stroke: this.$store.state.sand.styleConfig.borderColor,
+            // fill: this.$store.state.sand.styleConfig.backgroundColor,
+            strokeWidth: 1,
+            width:100,
+            height:50
+          },
+          leftImg: {
+            ref: 'body',
+            refX: -16,
+            refY: '50%',
+            refY2: -8,
+            width: 16,
+            height: 16,
+            'xlink:href':
+              'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SYCuQ6HHs5cAAAAAAAAAAAAAARQnAQ',
+            event: 'add:topic:left',
+            class: 'left-topic-image',
+          },
+          text: {
+            fontSize: 14,
+            fill: this.$store.state.sand.styleConfig.color,
+            textWrap:{
+              width:-10
+            }
+          }
+        },
+      },
+      true,
+    )    
+    // 右分支主题
+    Graph.registerNode(
+      'mindmap-topic-right',          
+      {
+        inherit: 'rect',
+        markup: [
+          {
+            tagName: 'rect',
+            selector: 'body',
+          },
+          {
+            tagName: 'image',
+            selector: 'leftImg',
+          },
+          {
+            tagName: 'image',
+            selector: 'rightImg',
+          },
+          {
+            tagName: 'text',
+            selector: 'text',
+          },
+        ],
+        attrs: {
+          body: {
+            rx: 6,
+            ry: 6,
+            // stroke: this.$store.state.sand.styleConfig.borderColor,
+            // fill: this.$store.state.sand.styleConfig.backgroundColor,
+            strokeWidth: 1,
+            width:100,
+            height:50
+          },
+          rightImg: {
+            ref: 'body',
+            refX: '100%',
+            refY: '50%',
+            refY2: -8,
+            width: 16,
+            height: 16,
+            'xlink:href':
+              'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SYCuQ6HHs5cAAAAAAAAAAAAAARQnAQ',
+            event: 'add:topic:right',
+            class: 'right-topic-image',
+          },
+          text: {
+            fontSize: 14,
+            fill:this.$store.state.sand.styleConfig.color,
+            textWrap:{
+              width:-10
+            }
+          }
+        },
+      },
+      true,
+    )
+    // 分支主题
+    // Graph.registerNode(
+    //   'mindmap-topic-child',
+    //   {
+    //     inherit: 'rect',
+    //     markup: [
+    //       {
+    //         tagName: 'rect',
+    //         selector: 'body',
+    //       },
+    //       {
+    //         tagName: 'text',
+    //         selector: 'text',
+    //       },
+    //       {
+    //         tagName: 'path',
+    //         selector: 'line',
+    //       },
+    //     ],
+    //     attrs: {
+    //       body: {
+    //         fill: '#ffffff',
+    //         strokeWidth: 0,
+    //         stroke: '#5F95FF',
+    //       },
+    //       text: {
+    //         fontSize: 14,
+    //         fill: '#262626',
+    //         textVerticalAnchor: 'bottom',
+    //       },
+    //       line: {
+    //         stroke: '#5F95FF',
+    //         strokeWidth: 2,
+    //         d: 'M 0 15 L 60 15',
+    //       },
+    //     },
+    //   },
+    //   true,
+    // )
+
+    // 连接器
+    Graph.registerConnector(
+      'mindmap',
+      (sourcePoint, targetPoint, routerPoints, options) => {
+        // console.log(sourcePoint, targetPoint, routerPoints, options,'sourcePoint, targetPoint, routerPoints, options');
+        const midX = sourcePoint.x + 10
+        const midY = sourcePoint.y
+        const ctrX = (targetPoint.x - midX) / 5 + midX
+        const ctrY = targetPoint.y
+        const pathData = `
+        M ${sourcePoint.x} ${sourcePoint.y}
+        Q ${ctrX} ${ctrY} ${targetPoint.x} ${targetPoint.y}
+        `
+        return options.raw ? Path.parse(pathData) : pathData
+      },
+      true,
+    )
+
+    // 边
+    Graph.registerEdge(
+      'mindmap-edge',
+      {
+        inherit: 'edge',
+        connector: {
+          name: 'mindmap',
+        },
+        attrs: {
+          line: {
+            targetMarker: false,
+            // stroke: this.$store.state.sand.styleConfig.lineColor,
+            strokeWidth: 2,
+          },
+        },
+        zIndex: 0,
+      },
+      true,
+    )
+  },
+  methods: {
+    setGraph(){
+      this.graph.on('add:topic:left', ({ node }) => {
+        if(this.operationType=='view') return 
+        const { id } = node
+        this.setCurrent(id)
+        const type = node.prop('type')
+        if (this.addChildNode(id, type,'left')) {
+          this.mindMapRender()      
+        }
+      })
+
+      this.graph.on('add:topic:right', ({ node }) => {
+        if(this.operationType=='view') return 
+        const { id } = node
+        this.setCurrent(id)
+        const type = node.prop('type')
+        if (this.addChildNode(id, type,'right')) {
+          this.mindMapRender()      
+        }
+      })
+      this.graph.on('node:change:size', (args) => { 
+        if(this.operationType=='view') return 
+        if(args.node.shape.indexOf('mindmap')!==-1){
+          let ids = args.node.id.split('-')
+					let mindmapDataIndex = this.mindmapDataUse.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
+					let mindMapDataCurrent = this.mindmapDataUse[mindmapDataIndex]?this.mindmapDataUse[mindmapDataIndex].mindmapData:{}
+
+					let findId = ids[0]
+
+					for (let i = 1; i < ids.length; i++) {
+						const element = ids[i];
+						findId = findId+'-'+element
+						mindMapDataCurrent=mindMapDataCurrent.children.find(it => it.id==findId)
+					}
+
+					mindMapDataCurrent.width = args.node.size().width
+					mindMapDataCurrent.height = args.node.size().height
+        }
+      })
+      this.graph.on('node:change:position', (args) => { 
+        if(this.operationType=='view') return 
+        if(args.node.shape.indexOf('mindmap')!==-1 && Number(args.node.id)){
+
+          let index = this.mindmapDataUse.findIndex(it =>it.mindmapData.id == args.node.id)
+          // let index = Number(args.node.id)
+          if(index!=-1){
+            this.mindmapDataUse[index].position = args.current
+          // this.setCurrent(args.node.id)
+          }
+        }
+      })
+      // this.graph.on('node:added', ({node}) => { 
+      //   if(node.shape.indexOf('mindmap')!=-1){
+      //     // 思维导图需要处理相对应的数据
+      //     console.log('mindmap',node.id);
+      //   }
+      //   console.log(node,'node:added');
+      // })
+      // this.graph.on('node:removed', (args) => { 
+      //   console.log(args,'node:removed');
+      // })
+      // this.graph.bindKey(['backspace', 'delete'], () => {
+      //   const selectedNodes = this.graph.getSelectedCells().filter((item) => item.isNode())
+      //   if (selectedNodes.length) {
+      //     const { id } = selectedNodes[0]
+      //     if (this.removeNode(id)) {
+      //       this.mindMapRender()
+      //     }
+      //   }
+      // })
+      
+      this.graph.bindKey('tab', (e) => {  
+        if(this.operationType=='view') return 
+        e.preventDefault()
+        const selectedNodes = this.graph.getSelectedCells().filter((item) => {
+          return item.shape.indexOf('mindmap')!=-1 && item.isNode()
+        })
+        if (selectedNodes.length) {
+          const node = selectedNodes[0]
+          this.setCurrent(node.id)
+          let type = node.prop('type')
+          let direction = node.shape.indexOf('left')!=-1?'left':'right'
+          if (this.addChildNode(node.id, type,direction)) {
+            this.mindMapRender()
+          }
+        }
+      })
+    },
+    // 通过id设置当前操作的思维导图
+    setCurrent(id){
+      let rootId = id.split('-')[0]
+      let index = this.mindmapDataUse.findIndex(it =>it.mindmapData.id == rootId)
+      this.mindMapDataCurrent = this.mindmapDataUse[index]?this.mindmapDataUse[index].mindmapData:{}
+      this.positionCurrent = this.mindmapDataUse[index]?this.mindmapDataUse[index].position:{x:0,y:0}
+      this.addTypeCurrent = this.mindmapDataUse[index]?this.mindmapDataUse[index].addType:'singleMindmap'
+    },
+    mindMapRender(i){
+      this.graph.startBatch('renderMindmap')
+      let mindMapType = i || i==0?this.mindmapDataUse[i].addType:this.addTypeCurrent
+      this.mindMapDataCurrent = i || i==0?this.mindmapDataUse[i].mindmapData:this.mindMapDataCurrent
+      this.positionCurrent = i || i==0?this.mindmapDataUse[i].position:this.positionCurrent
+      const result = Hierarchy.mindmap(this.mindMapDataCurrent, {
+        direction: 'H',
+        getHeight(d) {
+          return d.height
+        },
+        getWidth(d) {
+          return d.width
+        },
+        getHGap() {
+          return 40
+        },
+        getVGap() {
+          return 20
+        },
+        getSide: (d) => {
+          return mindMapType.indexOf('double') != -1?d.data.direction || 'left':'right'
+        }
+      })
+      const cells = []
+      let xGap = this.positionCurrent?this.positionCurrent.x-result.x:0
+      let yGap = this.positionCurrent?this.positionCurrent.y-result.y:0
+      // console.log(result,'result');
+      // return 
+      const traverse = (hierarchyItem) => {
+        if (hierarchyItem) {
+          const { data, children } = hierarchyItem
+          // console.log(hierarchyItem,'hierarchyItemhierarchyItemhierarchyItem');
+          let mindmapDirection = mindMapType.indexOf('double') != -1?data.direction:'right'
+          // console.log(mindmapDirection,data.label,data.direction);
+          let currentCell=this.graph.getCellById(data.id)
+
+          if(!currentCell){
+            // 没有 新增
+            cells.push(
+              this.graph.createNode({
+                id: data.id,
+                shape:mindmapDirection=='right'?'mindmap-topic-right':mindmapDirection=='left'?'mindmap-topic-left':'mindmap-topic',
+                x: xGap+hierarchyItem.x,
+                y: yGap+hierarchyItem.y,
+                width: data.width,
+                height: data.height,
+                label: data.label,
+                type: data.type,
+                attrs:{
+                  body: {
+                    stroke: this.$store.state.sand.styleConfig.borderColor,
+                    fill: this.$store.state.sand.styleConfig.backgroundColor
+                  },
+                  text:{
+                    fill:this.$store.state.sand.styleConfig.color
+                  }
+                },
+              }),
+            )
+          }else{
+            // 有,更新下位置信息
+            currentCell.position(xGap+hierarchyItem.x,yGap+hierarchyItem.y)
+          }
+          // return 
+   
+          if (children) {
+            children.forEach((item) => {
+              const { id, data } = item
+              let mindmapChildDirection = mindMapType.indexOf('double') != -1?data.direction:'right'
+              let currentEdge=this.graph.getCellById(data.id)
+              if(!currentEdge){
+                cells.push(
+                  this.graph.createEdge({
+                    shape: 'mindmap-edge',
+                    attrs:{
+                      line:{
+                        stroke:this.$store.state.sand.styleConfig.lineColor
+                      }
+                    },
+                    source: {
+                      cell: hierarchyItem.id,
+                      anchor: {
+                        name: 'center',
+                        args: {
+                          dx: mindmapChildDirection=='right'?'25%':mindmapChildDirection=='left'?'-25%':0,
+                        },
+                      },
+                    },
+                    target: {
+                      cell: id,
+                      anchor: {
+                        name: mindmapChildDirection =='left'?'right':'left',
+                      },
+                    },
+                  }),
+                )
+              }
+
+              traverse(item)
+            })
+          }
+        }
+      }
+      traverse(result)
+      // 排下序,把边放最后面 不然 边 会找不到 节点
+      let sortCells = cells.sort(cell => cell.shape.indexOf('edge'))
+      // console.log(sortCells,'result');
+      // return 
+      // this.graph.removeCells(sortCells)
+      this.graph.addCell(sortCells)
+      this.graph.stopBatch('renderMindmap')
+      // this.graph.resetCells(cells)
+      // this.graph.centerContent()
+    },
+    findItem(obj,id){
+      if (obj.id === id) {
+        return {
+          parent: null,
+          node: obj,
+        }
+      }
+      const { children } = obj
+      if (children) {
+        for (let i = 0, len = children.length; i < len; i++) {
+          const res = this.findItem(children[i], id)
+          if (res) {
+            return {
+              parent: res.parent || obj,
+              node: res.node,
+            }
+          }
+        }
+      }
+      return null
+    },
+    addChildNode (id, type,direction='left'){
+      console.log('添加');
+      // 重做不了 清空重做栈
+      this.mindmapAssistData.mindmapDataRecoverUse = []
+      // console.log(this.mindmapAssistData);
+
+      const res = this.findItem(this.mindMapDataCurrent, id)
+      const dataItem = res && res.node
+      if (dataItem) {
+        let item = null
+        // console.log(dataItem.children);
+        let addId ='1'
+        if(dataItem.children && dataItem.children.length>0){
+          let ids = dataItem.children[dataItem.children.length-1].id.split('-')
+          addId = parseInt(ids[ids.length-1])+1+''
+        }
+        if (type === 'topic') {
+          item = {
+            id: `${id}-${addId}`,
+            type: 'topic-branch',
+            label: `分支主题${addId}`,
+            width: 100,
+            height: 40,
+            direction
+          }
+        } else if (type === 'topic-branch' || type=='topic-child') {
+          item = {
+            id: `${id}-${addId}`,
+            type: 'topic-child',
+            label: `子主题${addId}`,
+            width: 60,
+            height: 30,
+            direction
+          }
+        }
+        if (item) {
+          if (dataItem.children) {
+            dataItem.children.push(item)
+          } else {
+            dataItem.children = [item]
+          }
+          return item
+        }
+      }
+      return null
+    },
+    removeNode (id) {
+      const res = this.findItem(this.mindMapDataCurrent, id)
+      const dataItem = res && res.parent
+      if (dataItem && dataItem.children) {
+        const { children } = dataItem
+        const index = children.findIndex((item) => item.id === id)
+        return children.splice(index, 1)
+      }
+      return null
+    }
+  },
+}

+ 276 - 64
src/views/sandbox_manage/common/node.js

@@ -1,73 +1,93 @@
 import { configOpt } from './toolConfig';
+import store from "@/vuex/index"
 
 const { line,border,text } = configOpt;
 
+console.log(store,'storestorestore');
+const styleConfig=store.state.sand.styleConfig
+
 //定义图形
 const configStyles = {
 	rect: {
-		width: '120px',
-		height: '50px',
-		textAlign: 'center',
-		lineHeight: '50px',
-		border: '1px solid #5b8ffa',
-		backgroundColor: border.fill,
-		borderColor: border.borderColor,
-		borderRadius: '4px',
-		color: '#7D7671',
-		margin: '0 10px 20px 0',
+		width: '30px',
+		height: '18px',
+		border: `2px solid #27292A`
+	},
+	roundRect: {
+		width: '30px',
+		height: '18px',
+		border: `2px solid #27292A`,
+		borderRadius:"8px"
+	},
+	ellipse:{
+		width: '30px',
+		height: '18px',
+		border: `2px solid #27292A`,
+		borderRadius:"100%"
+	},
+	rhomboid:{
+		width: '14px',
+		height: '14px',
+		border: `2px solid #27292A`,
+		transform: 'rotate(-45deg) skew(10deg, 10deg)',
+		position:'relative',
+		left:'4px',
+		marginRight:'6px'
 	},
 	text: {
-		width: '110px',
-		height: '50px',
+		width: '30px',
+		height: '18px',
 		textAlign: 'center',
-		lineHeight: '50px',
-		background: '#fff',
-		color: text.color,
+		lineHeight: '18px',
+		border: `2px solid #27292A`,
+		color: '#333333',
 		fontSize: '14px',
-		fontWeight: 'normal',
-		
+		fontWeight: 'bold',
 	},
-	date: {
-		width: '120px',
-		height: '50px',
-		textAlign: 'center',
-		lineHeight: '50px',
-		backgroundColor: '#5B9BD5',
-		borderRadius: '4px',
-		color: '#fff',
-		fontSize: '14px',
-		fontWeight: 'bold'
-	}
-
 }
 
-/* 图形种类 */
 export const myNodes = [
 	{
 		shape: 'rect',
 		key: 'rect',
-		label: '双击输入文本',
+		label:'',
+		img:'~@/assets/img/chart_m/User_act.png',
 		styles: {
 			...configStyles.rect,
 		}
 	},
 	{
 		shape: 'rect',
-		key: 'text',
-		label: '双击替换文本',
+		key: 'roundRect',
+		label:'',
 		styles: {
-			...configStyles.text,
+			...configStyles.roundRect,
+		}
+	},
+	{
+		shape: 'ellipse',
+		key: 'ellipse',
+		label:'',
+		styles: {
+			...configStyles.ellipse,
+		}
+	},
+	{
+		shape: 'polygon',
+		key: 'rhomboid',
+		label:'',
+		styles: {
+			...configStyles.rhomboid,
 		}
 	},
 	{
 		shape: 'rect',
-		key: 'date',
-		label:'Date:',
+		key: 'text',
+		label: 'T',
 		styles: {
-			...configStyles.date,
+			...configStyles.text,
 		}
 	},
-
 ]
 
 
@@ -84,28 +104,31 @@ export const portStyle = {
 		}
 	}
 }
-// 创建的节点配置 框 文本 日期
+// 创建的节点配置 矩形、圆角矩形、椭圆形、菱形、文本
 export const myNodeOption = (key) => {
 	switch (key) {
 		case 'rect': 
 			return {
-				width: 120,
-				height: 50,
+				width: 60,
+				height: 40,
 				data: {
 					key
 				},
 				attrs: {
-					rect: {
-						stroke: border.borderColor,
+					body: {
+						stroke: styleConfig.borderColor,
 						strokeWidth: border.width,
-						fill: border.fill,
+						fill:styleConfig.backgroundColor,
 						strokeDasharray: null,
 					},
 					text: {
-						fill: text.color,
+						fill: styleConfig.color,
 						fontSize: text.size,
 						lineHeight: text.lineHeight,
 						fontWeight: 'normal',
+						fontStyle:"normal",
+						textDecoration:'none',
+						relativeLineHeight:1.3,
 						textWrap: {
 							width: -10,
 						},
@@ -118,6 +141,66 @@ export const myNodeOption = (key) => {
 							{ group: 'port-left', id: 'p_left' },
 							{ group: 'port-right', id: 'p_right' },
 					],
+					groups: {
+							"port-top": {
+									position: 'top',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-bottom": {
+									position: 'bottom',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-left": {
+									position: 'left',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-right": {
+									position: 'right',
+									zIndex: 20,
+									...portStyle
+							}
+					}
+				},
+			}
+		case 'roundRect': 
+			return {
+				width: 60,
+				height: 40,
+				data: {
+					key
+				},
+				attrs: {
+					body: {
+						stroke: styleConfig.borderColor,
+						strokeWidth: border.width,
+						fill: styleConfig.backgroundColor,
+						strokeDasharray: null,
+						rx:8,
+						ry:8
+					},
+					text: {
+						fill: styleConfig.color,
+						fontSize: text.size,
+						lineHeight: text.lineHeight,
+						fontWeight: 'normal',
+						fontStyle:"normal",
+						textDecoration:'none',
+						relativeLineHeight:1.3,
+						textWrap: {
+							width: -10,
+						},
+					}
+				},
+				ports: {
+					items: [
+						{ group: 'port-top', id: 'p_top' },
+						{ group: 'port-bottom', id: 'p_bottom' },
+						{ group: 'port-left', id: 'p_left' },
+						{ group: 'port-right', id: 'p_right' },
+					],
 					groups: {
 							"port-top": {
 									position: 'top',
@@ -142,57 +225,186 @@ export const myNodeOption = (key) => {
 					}
 				},
 			}
-		case 'date': 
+		case 'ellipse': 
 			return {
-				width: 120,
-				height: 50,
+				width: 60,
+				height: 40,
 				data: {
-					key,
+					key
 				},
 				attrs: {
-					rect: {
-						stroke: '#5B9BD5',
-						strokeWidth: 1,
-						fill: '#5B9BD5'
+					body: {
+						stroke: styleConfig.borderColor,
+						strokeWidth: border.width,
+						fill: styleConfig.backgroundColor,
+						strokeDasharray: null,
 					},
 					text: {
-						text: 'Date:',
-						fill: '#fff',
-						fontSize: 18,
+						fill: styleConfig.color,
+						fontSize: text.size,
+						lineHeight: text.lineHeight,
 						fontWeight: 'normal',
+						fontStyle:"normal",
+						textDecoration:'none',
+						relativeLineHeight:1.3,
+						textWrap: {
+							width: -10,
+						},
+					}
+				},
+				ports: {
+					items: [
+							{ group: 'port-ellipse', id: 'p_top' },
+							{ group: 'port-ellipse', id: 'p_bottom' },
+							{ group: 'port-ellipse', id: 'p_left' },
+							{ group: 'port-ellipse', id: 'p_right' },
+							{ group: 'port-ellipse', id: 'p_top-1' },
+							{ group: 'port-ellipse', id: 'p_bottom-1' },
+							{ group: 'port-ellipse', id: 'p_left-1' },
+							{ group: 'port-ellipse', id: 'p_right-1' },
+					],
+					groups: {
+							"port-ellipse": {
+									position: 'ellipseSpread',
+									zIndex: 20,
+									...portStyle
+							},
+							// "port-bottom": {
+							// 		position: 'bottom',
+							// 		zIndex: 20,
+							// 		...portStyle
+							// },
+							// "port-left": {
+							// 		position: 'left',
+							// 		zIndex: 20,
+							// 		...portStyle
+							// },
+							// "port-right": {
+							// 		position: 'right',
+							// 		zIndex: 20,
+							// 		...portStyle
+							// },
+					}
+				},
+			}
+		case 'rhomboid': 
+			return {
+				width: 60,
+				height: 60,
+				// angle:-45,
+				data: {
+					key
+				},
+				attrs: {
+					body: {
+						stroke: styleConfig.borderColor,
+						strokeWidth: border.width,
+						fill: styleConfig.backgroundColor,
+						strokeDasharray: null,
+						refPoints: '0,10 10,0 20,10 10,20',
+					},
+					text: {
+						fill: styleConfig.color,
+						fontSize: text.size,
 						lineHeight: text.lineHeight,
+						fontWeight: 'normal',
+						fontStyle:"normal",
+						textDecoration:'none',
+						relativeLineHeight:1.3,
 						textWrap: {
 							width: -10,
 						},
 					}
 				},
-				ports: false
+				ports: {
+					items: [
+							{ group: 'port-top', id: 'p_top' },
+							{ group: 'port-bottom', id: 'p_bottom' },
+							{ group: 'port-left', id: 'p_left' },
+							{ group: 'port-right', id: 'p_right' },
+					],
+					groups: {
+							"port-top": {
+									position: 'top',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-bottom": {
+									position: 'bottom',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-left": {
+									position: 'left',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-right": {
+									position: 'right',
+									zIndex: 20,
+									...portStyle
+							},
+					}
+				},
 			}
 		case 'text': 
 			return {
-				width: 120,
-				height: 50,
+				width: 60,
+				height: 40,
 				data: {
 					key,
 				},
 				attrs: {
-					rect: {
+					body: {
 						stroke: '',
 						strokeWidth: 0,
 						fill: 'transparent',
 					},
 					text: {
-						text: '双击替换文本',
+						text: '文本',
 						fontSize: text.size,
 						lineHeight: text.lineHeight,
 						fontWeight: 'normal',
-						fill: text.color,
+						fontStyle:"normal",
+						textDecoration:'none',
+						relativeLineHeight:1.3,
+						fill: styleConfig.textColor,
 						textWrap: {
 							width: -10,
 						},
 					}
 				},
+				ports: {
+					items: [
+							{ group: 'port-top', id: 'p_top' },
+							{ group: 'port-bottom', id: 'p_bottom' },
+							{ group: 'port-left', id: 'p_left' },
+							{ group: 'port-right', id: 'p_right' },
+					],
+					groups: {
+							"port-top": {
+									position: 'top',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-bottom": {
+									position: 'bottom',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-left": {
+									position: 'left',
+									zIndex: 20,
+									...portStyle
+							},
+							"port-right": {
+									position: 'right',
+									zIndex: 20,
+									...portStyle
+							}
+					}
+				},
 			}
 	}
-	
-}
+}
+

+ 16 - 2
src/views/sandbox_manage/common/options.js

@@ -4,11 +4,25 @@ export const contextMenuOption = [
 	{
 		label: '复制',
 		key: 'copy',
-		icon: 'el-icon-document-copy'
+		icon: 'el-icon-document-copy',
+		show:true
 	},
 	{
 		label: '删除',
 		key: 'del',
-		icon: 'el-icon-delete'
+		icon: 'el-icon-delete',
+		show:true
+	},
+	{
+		label: '添加链接',
+		key: 'addLink',
+		icon: 'el-icon-link',
+		show:true
+	},
+	{
+		label: '清除链接',
+		key: 'deleteLink',
+		icon: 'el-icon-delete',
+		show:true
 	},
 ]

+ 57 - 5
src/views/sandbox_manage/common/toolConfig.js

@@ -8,20 +8,37 @@ export let configOpt = {
 		lineHeight: TEXT_SIZE * 1.3,
 	},
 	line: { //线条设置
-		width: 2,
-		color: '#5B9BD5',
+		width: 1,
+		color: '#0052D9',
 	},
 	border: { //线框设置
-		isDash: 0, // 0实 1虚线
+		isDash: 0, // 0实 1虚线border: 1px solid #0052D9
 		width: 2,
-		fill: '#fff',
-		borderColor: '#5B9BD5',
+		fill: '#DAE8FF',
+		borderColor: '#0052D9',
 	}
 }
 
 /* 定义默认颜色 */
 export const colorsOptions = ['#333','#5B9BD5','#f00','#fff','#00f','#000','#00FFFF','#70DB93','#9F5F9F','#A67D3D','#5F9F9F']
 
+export const familyOptions = [
+	{name:'微软雅黑',value:'微软雅黑'},
+	{name:'宋体',value:'宋体'},
+	{name:'黑体',value:'黑体'}
+]
+export const fontSizeOptions = [
+	{name:'12px',value:12},
+	{name:'14px',value:14},
+	{name:'16px',value:16},
+	{name:'18px',value:18}
+]
+export const lineHeightOptions = [
+	1,
+	1.15,
+	1.5,
+	2
+]
 export const sizeOptions = [
 	30,
 	28,
@@ -34,4 +51,39 @@ export const sizeOptions = [
 	14,
 	12,
 	10,
+]
+
+export const styleSettings=[
+	{
+		id:1,
+		backgroundColor:'#BBCEFF',
+		color:'#1841AA',
+		textColor:'#1841AA',
+		borderColor:'#1841AA',
+		lineColor:'#1841AA'
+	},
+	{
+		id:2,
+		backgroundColor:'#1841AA',
+		color:'#FFFFFF',
+		textColor:'#1841AA',
+		borderColor:'#1841AA',
+		lineColor:'#1841AA'
+	},
+	{
+		id:3,
+		backgroundColor:'#FFFFFF',
+		color:'#333333',
+		textColor:'#333333',
+		borderColor:'#333333',
+		lineColor:'#333333'
+	},
+	{
+		id:4,
+		backgroundColor:'#FFF6F4',
+		color:'#AA3218',
+		textColor:'#AA3218',
+		borderColor:'#AA3218',
+		lineColor:'#AA3218'
+	}
 ]

+ 2 - 0
src/views/sandbox_manage/index.vue

@@ -1,3 +1,5 @@
+<!-- 此沙盘图为老版本,防止后面需求回溯 暂作保留,后续新版沙盘图使用时间增长,使用情况稳定,还请给予删除,防止项目越来越大 开始保留时间 2023-10-9 -->
+<!-- 同目录的sanFlow目录下也一样的情况,属于老版本 -->
 <template>
   <div class="sandList-container">
     <div class="main-top">

+ 1704 - 0
src/views/sandbox_manage/index_new_version.vue

@@ -0,0 +1,1704 @@
+<template>
+  <div id="sandbox-index-container">
+    <span class="slide-icon slide-right" @click="slideHandle" v-show="isSlideLeft">
+      <i class="el-icon-d-arrow-right"></i>
+    </span>
+    <div class="sandbox-content-tree-box" v-show="!isSlideLeft">
+      <div class="sandbox-content-tree-header">
+        <el-button type="primary" style="width: 170px;" @click="addSand" v-permission="permissionBtn.sandboxPermission.sandbox_saveView">添加逻辑</el-button>
+        <el-checkbox v-model="searchParams.IsShowMe" @change="onlyMeHandler">只看我的</el-checkbox>
+      </div>
+      <div class="sandbox-content-tree-body">
+        <div style="padding: 0 20px;" v-permission="permissionBtn.sandboxPermission.sandbox_search">
+          <el-select v-model="searchSandboxId" v-loadMore="searchLoad" :filterable="!searchSandboxId" remote @change="sandboxChange"
+          clearable placeholder="请输入逻辑图名称" style="width: 100%; margin-bottom: 20px;" :remote-method="searchHandle"
+          @click.native="inputFocusHandle">
+            <i slot="prefix" class="el-input__icon el-icon-search"></i>
+            <el-option
+              v-for="item in searchOptions"
+              :key="item.SandboxId"
+              :label="item.Name"
+              :value="item.SandboxId"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="sandbox-content-tree">
+          <tree :nodes="treeData" :setting="setting" @onCreated="getZTree" @onDrop="zTreeDrop" key="classify" 
+          @onExpand="zTreeExpand"/>
+          <div class="add-classify" v-permission="permissionBtn.sandboxPermission.sandbox_classify_addEdit">
+            <img src="~@/assets/img/sand_new/add_ico.png"/>
+            <span @click="addLevelOneHandle">添加分类</span>
+          </div>
+          <span class="slide-icon slide-left" @click="slideHandle">
+            <i class="el-icon-d-arrow-left"></i>
+          </span>
+        </div>
+      </div>
+    </div>
+    <div class="sandbox-pictures-box" v-show="rightType=='list'" v-permission="permissionBtn.sandboxPermission.sandbox_saveView">
+      <div class="pictures-count">
+        共{{total}}个逻辑图
+      </div>
+      <div class="pictures-box" ref="pictureListRef" @scroll="loadMoreSandbox">
+        <el-col :span="6" style="margin-bottom:20px;padding-right: 20px;min-width: 255px;"
+        v-for="picture in pictureList" :key="picture.SandboxId">
+          <div class="pictures-item">
+            <div class="pictures-item-header">
+              <span class="text_oneLine">{{ picture.Name }}</span>
+            </div>
+            <img :src="picture.PicUrl" class="picture-img" @click="detailShowHandle(picture)"/>
+            <div class="item-bottom">
+              <span>{{ picture.CreateTime.slice(0,10) }}</span>
+              <div class="item-bottom-buttons">
+                <span class="join_txt" @click="copyHandle(picture)" v-permission="permissionBtn.sandboxPermission.sandbox_addMy">复制</span>
+                <span class="join_txt" @click="deleteHandle(picture,'inList')" style="color: #C54322;"
+                v-permission="permissionBtn.sandboxPermission.sandbox_del">删除</span>
+              </div>
+            </div>
+          </div>
+        </el-col>
+      </div>
+    </div>
+    <div class="sandbox-chart-box" 
+    :style="{visibility:rightType=='list'?'hidden':'visible',
+            width:rightType=='list'?'1px':'unset',
+            flexGrow:rightType=='list'?'unset':1}">
+      <div class="sandbox-chart-head">
+        <div class="sandbox-chartHead-author">作者:<span>{{ this.viewSandbox.SysUserName }}</span></div>
+        <div class="sandbox-chartHead-title">{{ this.viewSandbox.Name }}</div>
+        <div class="sandbox-chartHead-options">
+          <div class="chartHead-options-button" @click="editSand" v-permission="permissionBtn.sandboxPermission.sandbox_saveView">
+            <img src="~@/assets/img/sand_new/edit_outline.png" />
+            <span>编辑</span> 
+          </div>
+          <div class="chartHead-options-button" @click="saveOther">
+            <img src="~@/assets/img/sand_new/save-other.png" />
+            <span>另存为</span> 
+          </div>
+          <div class="chartHead-options-button" @click="copySandHandle" v-permission="permissionBtn.sandboxPermission.sandbox_addMy">
+            <img src="~@/assets/img/sand_new/copy.png" />
+            <span>复制</span> 
+          </div>
+          <div class="chartHead-options-button" @click="deleteHandle(viewSandbox,'inchart')">
+            <img src="~@/assets/img/sand_new/remove.png" />
+            <span style="color: red;" v-permission="permissionBtn.sandboxPermission.sandbox_del">删除</span> 
+          </div>
+        </div>
+      </div>
+      <div class="sandbox-body">
+        <div class="sand-chart-body" id="sand-chart-body"></div>
+        <!-- 缩略图 -->
+        <div id="minimap" class="minimap"></div>
+        <el-popover
+        placement="top"
+        trigger="manual"
+        v-model="popoverVisible">
+        <div id="link-popover" :style="{height:popoverFlod?'20px':'unset'}">
+          <div class="link-box">
+            <div v-for="item in checkedLinkList" :key="item.RId" class="link-item" @click="navigateTo(item)">
+              {{ item.Name }}
+            </div>
+          </div>
+          <img src="~@/assets/img/sand_new/arrow_black_down.png" class="link-fold" 
+          :style="{transform:popoverFlod?'':'rotate(180deg)'}" v-show="checkedLinkList.length>1"
+          @click="foldLink"/>
+        </div>
+        <div id="link-reference" slot="reference"></div>
+      </el-popover>
+      </div>
+    </div>
+    <!-- 目录自定义按钮区域 -->
+    <div id="custom-button-zone" class="custom-button-zone">
+      <img src="~@/assets/img/sand_new/add_outline.png" class="add-classify-img" v-permission="permissionBtn.sandboxPermission.sandbox_classify_addEdit"/>
+      <img src="~@/assets/img/sand_new/edit_outline.png" class="edit-classify-img" v-permission="permissionBtn.sandboxPermission.sandbox_classify_addEdit"/>
+      <img src="~@/assets/img/sand_new/delete_outline.png" class="delete-classify-img" v-permission="permissionBtn.sandboxPermission.sandbox_classify_del"/>
+    </div>
+    <!-- 添加分类 -->
+    <el-dialog
+      :title="classifyAddTitle"
+      :visible.sync="classifyAddShow"
+      :append-to-body="true"
+      :close-on-click-modal="false"
+      @closed="classifyAddClosed"
+      width="560px">
+      <div style="padding: 10px 40px 0;">
+        <el-form :model="classifyForm" ref="classifyFormRef" :rules="classifyFormRules" 
+        label-width="80px">
+          <el-form-item label="上级目录" v-if="lastLevelClassifyName">
+            {{ lastLevelClassifyName }}
+          </el-form-item>
+          <el-form-item label="目录名称" prop="SandboxClassifyName">
+            <el-input v-model="classifyForm.SandboxClassifyName" style="width: 317px;" placeholder="请输入目录名称"></el-input>
+          </el-form-item>
+          <el-form-item label="关联品种" prop="ChartPermissionId" v-if="hasVariety && (classifyAddTitle.indexOf('添加')!==-1)">
+            <el-cascader 
+							:options="classifyArr"
+							:props="classifyProps"
+							v-model="classifyForm.ChartPermissionId" 
+							placeholder="请选择对应品种" 
+              id="classifyAddCascader"
+						/>
+          </el-form-item>
+        </el-form>
+        <div style="text-align: center;padding:40px 0 ;">
+          <el-button @click="classifyAddSubmit" type="primary" style="width: 120px;" size="large">保存</el-button>
+          <el-button @click="classifyAddShow=false" style="width: 120px;margin-left: 28px;" size="large">取消</el-button>
+        </div>
+      </div>
+    </el-dialog>
+    <!-- 另存为 -->
+    <el-dialog
+      title="另存为"
+      :visible.sync="saveOtherShow"
+      :append-to-body="true"
+      :close-on-click-modal="false"
+      @closed="saveOtherClosed"
+      width="600px">
+      <div style="padding: 10px 40px 0;" class="save-as-dialog">
+        <el-form :model="saveOtherForm" ref="saveOtherFormRef"
+        label-width="96px">
+          <el-form-item label="逻辑图名称" prop="chartName" :rules="{required:true,message:'请输入逻辑图名称',trigger:'blur'}">
+            <el-input v-model="saveOtherForm.chartName" style="width: 317px;" placeholder="请输入逻辑图名称"></el-input>
+          </el-form-item>
+          <el-form-item label="分类" prop="classifyId" :rules="{required:true,message:'请选择分类',trigger:'change'}" >
+            <el-popover
+              placement="bottom"
+              width="400"
+              popper-class="classify-popper"
+              trigger="click"
+              v-model="selectClassifyShow">
+                <tree :nodes="onlyClassifyTreeData" :setting="selectSetting" key="saveOther" @onCreated="getSelectZTree" @onClick="selectClassify" />
+                <el-cascader 
+                slot="reference"
+                :options="onlyClassifyTreeData"
+                :props="{children: 'Children',
+                  label: 'SandboxClassifyName',
+                   value: 'SandboxClassifyId',emitPath:false,checkStrictly:true}"
+                v-model="saveOtherForm.classifyId" 
+                popper-class="classify-cascader-popper"
+                placeholder="请选择分类">
+                </el-cascader>
+            </el-popover>
+          </el-form-item>
+        </el-form>
+        <div style="text-align: center;padding:40px 0 ;">
+          <el-button @click="saveOtherSubmit" type="primary" style="width: 120px;" size="large">保存</el-button>
+          <el-button @click="saveOtherShow=false" style="width: 120px;margin-left: 28px;" size="large">取消</el-button>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import tree from "vue-giant-tree";
+import { dataBaseInterface,sandInterface,customInterence} from '@/api/api.js';
+import { svgToBase64 } from '@/utils/svgToblob'
+import mindmap from "./common/mindmap"
+
+import { myGraph } from './common/gragh';
+
+  export default {
+    name:"sandbox_manage_index",
+    components:{tree},
+    computed:{
+      hasVariety(){
+        return this.permissionBtn.isShowBtn('sandboxPermission','sandbox_variety')
+      }
+    },
+    mixins:[mindmap],
+    watch: {
+      initData(newval) {
+        console.log(newval);
+        if(!this.graph){
+          this.$nextTick(()=>{
+            this.init()
+            this.graph.fromJSON(newval);
+            this.graph.zoomToFit()
+          })
+        }else{
+          this.graph.fromJSON(newval);
+          this.graph.zoomToFit()
+        }
+      }
+	  },
+    data() {
+      return {
+        SandboxClassifyId:0,
+        searchOptions:[],
+        searchHaveMore:false,
+        rightType:"list",
+        pictureHaveMore:false,
+        searchParams:{
+          SandboxClassifyId:'',
+          PageSize:12,
+          CurrentIndex:1,
+          IsShowMe:false
+        },
+        searchCurrentIndex:1,
+        KeyWord:'',
+        searchSandboxId:'',
+        total:0,
+        pictureList:[],
+        treeData:[],
+        onlyClassifyTreeData:[],
+        setting:{
+          data:{
+            keep:{
+              leaf:true,
+              parent:true
+            },
+            key:{
+              name:"SandboxClassifyName",
+              children:"Children",
+              isParent:"isCatalogue"
+            },
+          },
+          view:{
+            showLine:false,
+            showIcon:false,
+            selectedMulti:false,
+            addDiyDom: this.addDiyDom,
+            // addHoverDom: this.addHoverDom,
+		        // removeHoverDom: this.removeHoverDom
+          },
+          edit:{
+            enable:true,
+            showRemoveBtn:false,
+            showRenameBtn:false,
+            drag:{
+              isCopy:false,
+              isMove:true,
+            }
+          },
+          callback:{
+            beforeDrag:this.zTreeDragBefore,
+            beforeDrop:this.zTreeDropBefore,
+            onDrop:this.zTreeDrop,
+            beforeExpand:this.zTreeExpandBefore,
+            onClick:this.zTreeClick
+          }
+        },
+        zTreeObj:{},
+        selectSetting:{
+          data:{
+            key:{
+              name:"SandboxClassifyName",
+              children:"Children"
+            }
+          },
+          view:{
+            showLine:false,
+            showIcon:false,
+            selectedMulti:false
+          }
+        },
+        selectClassifyShow:false,
+        selectZTreeObj:{},
+        lockLoding:null,
+        viewSandbox:{},
+        activeNode:{},
+        addClassifyNodeTid:'',
+        movingRecoveData:{
+          targetNode:null,
+          treeNode:null,
+          moveType:''
+        },
+        graph:null,
+        initData:{},
+        // ------- 添加分类弹窗
+        classifyAddTitle:"添加分类",
+        classifyAddShow:false,
+        lastLevelClassifyName:'',
+        classifyForm:{
+          SandboxClassifyId:0,
+          SandboxClassifyName:'',
+          ChartPermissionId:null,
+          ParentId:0,
+          Level:0
+        },
+        classifyFormRules:{
+          SandboxClassifyName:{required: true, message:'请输入目录名称', trigger: 'blur'},
+          ChartPermissionId:{required: true, message:'请选择对应品种', trigger: 'change'}
+        },
+        classifyProps: {
+          children: 'Items',
+          label: 'ClassifyName',
+          value: 'ChartPermissionId',
+          emitPath:false
+			  },
+        classifyArr:[],
+        // 另存为弹窗
+        saveOtherShow:false,
+        saveOtherForm:{
+          chartName:"",
+          classifyId:""
+        },
+        saveOtherClassifys:'',
+        locationActiveNode:'',
+        operationType:'view',
+        popoverVisible:false,
+        popoverFlod:true,
+        popoverDom:null,
+        popoverTriggerDom:null,
+        checkedLinkList:[],
+        linkNode:null,
+        popoverTimeout:null,
+        isSlideLeft:false
+      }
+    },
+    created(){
+      this.getClassify()
+      this.getSandboxClassify()
+      this.getSandboxClassifyOnly()
+      this.getSandboxList()
+      // this.setting.async={
+        // enable:false,
+        // type:'head'
+      //   url:(()=>{
+      //     console.log(process.env);
+      //     // console.log(process.env.VUE_APP_API_ROOT,'process.env.VUE_APP_API_ROOT');
+      //     // if(process.env.NODE_ENV === "development"){
+      //     //   return "http://8.136.199.33:7777/adminapi/sandbox/classify/list"
+      //     // }else{
+      //       return process.env.VUE_APP_API_ROOT+'/sandbox/classify/list'
+      //     // }
+      //   })(),
+      //   type:'get',
+      //   autoParam: ["SandboxClassifyId"],
+      //   otherParam:{ "IsShowMe":this.isOnlyMe},
+      //   dataFilter: (treeId,parentNode,responseData)=>{
+      //     console.log(treeId,parentNode,responseData,'responseData');
+      //     return responseData.Data.AllNodes
+      //   },
+      //   headers:{
+      //     Authorization:localStorage.getItem("auth"),
+      //     Uuid:localStorage.getItem("uuid") || "",
+      //     AccessToken:localStorage.getItem("uuid")+"--zheshiyigename",
+      //   }
+      // }
+    },
+    mounted(){
+      // this.init()
+      this.popoverDom = $('#link-popover')[0];
+      this.popoverTriggerDom = $('#link-reference')[0];
+      
+      this.popoverDom.addEventListener('mouseenter',this.clearPopoverTimeout)
+      this.popoverDom.addEventListener('mouseleave',this.closePopover)
+
+    },
+    beforeDestroy(){
+      this.popoverDom.removeEventListener('mouseenter',this.clearPopoverTimeout)
+      this.popoverDom.removeEventListener('mouseleave',this.closePopover)
+
+    },
+    methods:{
+      /* 获取品种 */
+      getClassify() {
+        customInterence.getvariety({
+          CompanyType: 'ficc'
+        }).then(res => {
+          console.log(res);
+          if(res.Ret !== 200)  return
+            this.classifyArr = res.Data.List||[ ]
+        })
+      },
+      // 获取沙盘图分类
+      getSandboxClassify(parentNode,locationNode) {
+        console.log(parentNode,locationNode,'parentNode,locationNode');
+        const sandboxClassifyId = parentNode ? parentNode.SandboxClassifyId:0
+        sandInterface.getSandboxClassify({SandboxClassifyId:sandboxClassifyId,IsShowMe:this.searchParams.IsShowMe}).then(res=>{
+          console.log(res);
+          if (res.Ret === 200) {
+            let nodesData=res.Data.AllNodes || []
+            nodesData.map(item =>{
+              item.isCatalogue = item.SandboxId?false:true
+            })
+            if(parentNode){
+              this.zTreeObj.addNodes(parentNode,nodesData)
+            }else{
+              this.treeData = nodesData
+            }
+            if(locationNode){
+              let searchNode
+              let locationId = locationNode.isCatalogue?locationNode.SandboxClassifyId:locationNode.SandboxId
+              if(parentNode){
+                if(locationNode.isCatalogue){
+                  searchNode=this.zTreeObj.getNodesByParam('SandboxClassifyId',+locationId,parentNode)
+                }else{
+                  searchNode=this.zTreeObj.getNodesByParam('SandboxId',+locationId,parentNode)
+                }
+                console.log(searchNode,'searchNode');
+                if(!(searchNode && searchNode.length>0)) return 
+                this.zTreeClick(null,'',searchNode[0],1)
+                this.zTreeObj.selectNode(searchNode[0])
+              }else{
+                requestAnimationFrame(()=>{
+                  searchNode=this.zTreeObj.getNodesByParam('SandboxClassifyId',+locationId)
+                  console.log(searchNode,'searchNode');
+                  if(!(searchNode && searchNode.length>0)) return 
+                  this.zTreeClick(null,'',searchNode[0],1)
+                  this.zTreeObj.selectNode(searchNode[0])
+                })
+              }
+
+            }
+          }
+        })
+      },
+      getSandboxClassifyOnly(){
+        sandInterface.getSandboxClassifyOnly().then(res=>{
+          if (res.Ret === 200) {
+            this.onlyClassifyTreeData=res.Data.AllNodes || []
+          }
+        })
+      },
+      getSandboxList(type){
+        if(type == 'setCurrentIndex'){
+          this.searchParams.CurrentIndex=1
+        }
+        sandInterface.getSandboxListV2(this.searchParams).then(res=>{
+          console.log(res,'res');
+          if(res.Ret == 200){
+            if(!res.Data){
+              this.pictureList=[]
+              this.total = 0
+              this.pictureHaveMore=false
+            }else{
+              let arr = res.Data.List || []
+              this.pictureList=this.searchParams.CurrentIndex == 1 ?arr:[...this.pictureList,...arr]
+              this.total = res.Data.Paging.Totals || 0
+              this.pictureHaveMore= this.searchParams.CurrentIndex < res.Data.Paging.Pages
+            }
+            this.rightType='list'
+          }
+        })
+      },
+      getZTree(zTree){
+        this.zTreeObj=zTree
+      },
+      getSelectZTree(zTree){
+        this.selectZTreeObj=zTree
+      },
+      selectClassify(event,treeId,treeNode,clickFlag){
+        this.sandSaveParams.SandboxClassifyId = treeNode.SandboxClassifyId
+        this.selectClassifyShow = false
+      },
+      zTreeDragBefore(treeId,treeNodes){
+        return this.permissionBtn.isShowBtn('sandboxPermission','sandbox_classify_move')
+      },
+      zTreeDropBefore(treeId,treeNodes,targetNode,moveType,isCopy){
+        console.log(treeId,treeNodes,targetNode,moveType,isCopy,'zTreeDropBefore');
+        if((!targetNode) && (!moveType)) return false
+        if(((!treeNodes[0].isCatalogue) && targetNode.Level==1 && moveType!='inner')||
+        ((!targetNode.isCatalogue) && moveType=='inner')||
+        (treeNodes[0].isCatalogue && targetNode.Level==5 && moveType=='inner')){
+          return false
+        }
+
+        // 接口失败后的还原
+        // this.movingRecoveData.treeNode=treeNodes[0]
+        // if(treeNodes[0].getPreNode()){
+        //   this.movingRecoveData.targetNode = treeNodes[0].getPreNode()
+        //   this.movingRecoveData.moveType = "next"
+        // }else if(treeNodes[0].getNextNode()){
+        //   this.movingRecoveData.targetNode = treeNodes[0].getNextNode()
+        //   this.movingRecoveData.moveType = "prev"
+        // }else if(treeNodes[0].getParentNode()){
+        //   this.movingRecoveData.targetNode = treeNodes[0].getParentNode()
+        //   this.movingRecoveData.moveType = "inner"
+        // }
+      },
+      zTreeDrop(e,treeId,treeNodes,targetNode,moveType,isCopy){
+        console.log(treeId,treeNodes[0],targetNode,moveType,isCopy,'zTreeDrop');
+        if((!targetNode) && (!moveType)) return
+        console.log(treeNodes[0].getParentNode(),targetNode.getParentNode());
+        let parentNode = treeNodes[0].getParentNode()
+        let prevNode = treeNodes[0].getPreNode()
+        let nextNode = treeNodes[0].getNextNode()
+        //移动分类
+        let params={
+          ClassifyId:treeNodes[0].SandboxClassifyId,
+          SandboxId:treeNodes[0].isCatalogue?0:treeNodes[0].SandboxId,
+          ParentClassifyId:parentNode?parentNode.SandboxClassifyId:0,
+          PrevId:prevNode? (prevNode.isCatalogue?prevNode.SandboxClassifyId:prevNode.SandboxId) :0,
+          NextId:nextNode? (nextNode.isCatalogue?nextNode.SandboxClassifyId:nextNode.SandboxId) :0,
+          PrevType:prevNode?(prevNode.isCatalogue?1:2):0,
+          NextType:nextNode?(nextNode.isCatalogue?1:2):0
+        }
+        sandInterface.sandboxClassifyMove(params).then(res=>{
+          if(res.Ret == 200){
+            this.$message.success('移动分类成功')
+            if(parentNode){
+              this.zTreeObj.removeChildNodes(parentNode)
+            }
+            this.getSandboxClassify(parentNode,treeNodes[0])
+          }
+        })
+        // if((!targetNode) && (!moveType)) return
+        // let parentNode = treeNodes[0].getParentNode()
+        // let prevNode = treeNodes[0].getPreNode()
+        // let nextNode = treeNodes[0].getNextNode()
+        // //移动分类
+        // let params={
+        //   ClassifyId:treeNodes[0].SandboxClassifyId,
+        //   SandboxId:treeNodes[0].isCatalogue?0:treeNodes[0].SandboxId,
+        //   ParentClassifyId:parentNode?parentNode.SandboxClassifyId:0,
+        //   PrevId:prevNode? (prevNode.isCatalogue?prevNode.SandboxClassifyId:prevNode.SandboxId) :0,
+        //   NextId:nextNode? (nextNode.isCatalogue?nextNode.SandboxClassifyId:nextNode.SandboxId) :0,
+        //   PrevType:prevNode?(prevNode.isCatalogue?1:2):0,
+        //   NextType:nextNode?(nextNode.isCatalogue?1:2):0
+        // }
+        // console.log(params);
+        // sandInterface.sandboxClassifyMove(params).then(res=>{
+        //   if(res.Ret == 200){
+        //     this.$message.success('移动分类成功')
+        //   }else{
+        //     // let result=this.zTreeObj.moveNode(this.movingRecoveData.targetNode,this.movingRecoveData.treeNode,this.movingRecoveData.moveType)
+        //     // if(!result){
+        //     //   window.location.reload()
+        //     // }
+        //   }
+        // })
+      },
+      zTreeExpandBefore(treeId, treeNode){
+        // console.log( treeId, treeNode);
+        if(treeNode.Children && treeNode.Children.length>0){
+          return true
+        }else{
+          this.getSandboxClassify(treeNode)
+        }
+      },
+      zTreeExpand(event, treeId, treeNode){
+        // console.log(event, treeId, treeNode);
+      },
+      zTreeClick(event, treeId, treeNode,clickFlag){
+        console.log(event, treeId, treeNode,clickFlag);
+
+        if(clickFlag==1){
+          if(this.activeNode.SandboxClassifyId == treeNode.SandboxClassifyId &&
+          this.activeNode.SandboxId == treeNode.SandboxId){
+            return 
+          }
+          this.activeNode = treeNode
+          if(treeNode.isCatalogue){
+            // 目录
+            this.searchParams.CurrentIndex=1
+            this.searchParams.SandboxClassifyId = treeNode.SandboxClassifyId
+            this.getSandboxList('setCurrentIndex')
+            // this.rightType='list'
+          }else{
+            // 沙盘图
+            sandInterface.getSandboxDetail({SandboxId:this.activeNode.SandboxId}).then(res=>{
+              console.log(res);
+              if(res.Ret == 200){
+                this.viewSandbox=res.Data
+                this.rightType='chart'
+                this.initData = JSON.parse(this.viewSandbox.Content)
+              }
+            })
+          }
+        }
+
+        if(clickFlag==0 && treeNode.isCatalogue){
+          this.activeNode={}
+          this.searchParams.CurrentIndex=1
+          this.searchParams.SandboxClassifyId = ''
+          this.getSandboxList('setCurrentIndex')
+          // this.rightType='list'
+        }
+      },
+      onlyMeHandler(){
+        this.activeNode={}
+        this.getSandboxClassify()
+        this.searchParams.SandboxClassifyId=''
+        this.getSandboxList('setCurrentIndex')
+      },
+      searchHandle(query) {
+        // console.log(query,"搜索");
+        this.searchCurrentIndex = 1;
+        this.KeyWord = query;
+        this.searchSandbox()
+		  },
+      searchSandbox(){
+        sandInterface.getSandboxListV2({...this.searchParams,
+          CurrentIndex:this.searchCurrentIndex,SandboxClassifyId:'',
+          KeyWord:this.KeyWord}).then(res=>{
+            if(res.Ret == 200){
+              if(!res.Data){
+                this.searchOptions=[]
+                this.searchHaveMore=false
+              }else{
+                let arr = res.Data.List || []
+                this.searchOptions=this.searchCurrentIndex==1?arr:[...this.searchOptions,...arr]
+                this.searchHaveMore = this.searchCurrentIndex < res.Data.Paging.Pages
+              }
+            }
+          })
+      },
+      searchLoad() {
+        // 加载更多
+        console.log("加载更多");
+        if(!this.searchHaveMore) return;
+        this.searchCurrentIndex++
+        this.searchSandbox()
+		  },
+      /* 聚焦获取当前检索 */
+      inputFocusHandle(e) {
+        // 选取
+        console.log('选取',e.target.value);
+        this.searchCurrentIndex = 1;
+        this.KeyWord = e.target.value;
+        if(this.KeyWord) {
+          this.searchSandbox()
+        }else {
+          this.searchOptions = [];
+        }
+      },
+      sandboxChange(sandboxId){
+        let sandbox=this.searchOptions.find(it => it.SandboxId == sandboxId)
+        if(sandbox){
+          this.rightType='chart'
+          this.viewSandbox=sandbox
+          this.initData = sandbox.Content?JSON.parse(sandbox.Content):sandbox.Content
+          let ParentClassifys = sandbox.ParentIds.split(',').filter(Boolean)
+          ParentClassifys.push(sandbox.SandboxClassifyId)
+          ParentClassifys.push(sandbox.SandboxId)
+          this.sandboxLocation(ParentClassifys)
+        }
+      },
+      // 添加一级分类
+      addLevelOneHandle(){
+        this.classifyAddTitle="添加分类"
+        this.classifyAddShow=true
+        this.addClassifyNodeTid=''
+      },
+      addClassify(e,node){
+        e.stopPropagation()
+        console.log(node);
+        this.addClassifyNodeTid = node.tId
+        this.lastLevelClassifyName = this.getParentNodeName(node)
+        this.classifyAddTitle="添加分类"
+        this.classifyForm.Level = node.Level
+        this.classifyForm.ParentId = node.SandboxClassifyId
+        this.classifyAddShow=true
+      },
+      getParentNodeName(node){
+        let parentNode = node.getParentNode()
+        if(parentNode){
+          return this.getParentNodeName(parentNode)+'/'+node.SandboxClassifyName
+        }else{
+          return node.SandboxClassifyName
+        }
+      },
+      editClassify(e,node){
+        console.log(node);
+        e.stopPropagation()
+        let pNode = node.getParentNode()
+        this.lastLevelClassifyName = pNode ? pNode.SandboxClassifyName:''
+        this.classifyForm.SandboxClassifyId = node.SandboxClassifyId
+        this.classifyForm.SandboxClassifyName = node.SandboxClassifyName
+        this.classifyAddTitle="重命名"
+        this.classifyAddShow=true
+      },
+      deleteClassify(e,node){
+        e.stopPropagation()
+        sandInterface.deleteSandboxClassifyCheck({SandboxClassifyId:node.SandboxClassifyId}).then(res=>{
+          if (res.Ret === 200) {
+            /**
+             * 0 可删除
+             * 1 关联沙盘图
+             * 2 有子目录无沙盘图
+             */
+            const deleteLabelMap = {
+              1: '该分类下存在沙盘图,不可删除',
+              2: '确认删除当前分类及包含的子分类吗?',
+              4: res.Data.TipsMsg
+            }
+
+            if([1,4].includes(res.Data.DeleteStatus)) this.$confirm(
+                deleteLabelMap[res.Data.DeleteStatus],
+                '删除失败',
+                {
+                confirmButtonText: '知道了',
+                showCancelButton:false,
+                type: 'error'
+              })
+            else if([0,2].includes(res.Data.DeleteStatus)) this.$confirm(
+                res.Data.DeleteStatus === 2 
+                ? deleteLabelMap[res.Data.DeleteStatus]
+                : node.SandboxId?'确认删除该沙盘图吗?':'确定删除当前分类吗?', 
+                '提示',
+                {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+              }).then(() => {
+                res.Data.DeleteStatus === 0 && node.SandboxId 
+                ? this.delHandle(node.SandboxClassifyId, node.SandboxId, 1)
+                : this.delHandle(node.SandboxClassifyId, node.SandboxId);
+              }).catch(() => {         
+              });
+          }
+        })
+      },
+      delHandle(SandboxClassifyId,SandboxId,type){
+        let currentNode = this.zTreeObj.getSelectedNodes()[0]
+        sandInterface.deleteSandbox({
+          SandboxClassifyId,
+          SandboxId,
+        }).then((res) => {
+          if (res.Ret === 200) {
+            this.zTreeObj.removeNode(currentNode)
+            this.$message.success(res.Msg);
+            // if(type && res.Data.SandboxId){
+              
+            // }else{
+
+            // }
+          }
+        });
+      },
+      classifyAddSubmit(){
+        console.log(this.classifyForm);
+        //提交
+        this.$refs.classifyFormRef.validate(valid=>{
+          if(valid){
+            if(this.classifyForm.SandboxClassifyId){
+              //编辑
+              let params={
+                SandboxClassifyId:this.classifyForm.SandboxClassifyId,
+                SandboxClassifyName:this.classifyForm.SandboxClassifyName
+              }
+              sandInterface.editSandboxClassify(params).then(res=>{
+                if(res.Ret == 200){
+                  this.classifyAddShow=false
+                  this.$message.success(this.classifyAddTitle+"成功")
+                  let currentNode = this.zTreeObj.getSelectedNodes()[0]
+
+                  currentNode.SandboxClassifyName = this.classifyForm.SandboxClassifyName
+                  this.zTreeObj.updateNode(currentNode)
+                  // this.getSandboxClassify()
+                }
+              })
+            }else{
+              //新增
+              let params={
+                SandboxClassifyName:this.classifyForm.SandboxClassifyName,
+                ParentId:this.classifyForm.ParentId,
+                Level:this.classifyForm.Level,
+                ChartPermissionId:this.classifyForm.ChartPermissionId
+              }
+              sandInterface.addSandboxClassify(params).then(res=>{
+                if(res.Ret == 200){
+                  this.classifyAddShow=false
+                  this.$message.success(this.classifyAddTitle+"成功")
+                  if(!this.addClassifyNodeTid){
+                    this.getSandboxClassify()
+                  }else{
+                    let curNode = this.zTreeObj.getNodeByTId(this.addClassifyNodeTid)
+                    // console.log(curNode,'');
+                    this.zTreeObj.removeChildNodes(curNode)
+                    // this.$nextTick(()=>{
+                      this.zTreeObj.expandNode(curNode,true,false,false,true)
+                    // })
+                  }
+                }
+              })
+            }
+
+          }
+        })
+      },
+      classifyAddClosed(){
+        this.classifyForm={
+          SandboxClassifyId:0,
+          SandboxClassifyName:'',
+          ChartPermissionId:null,
+          ParentId:0,
+          Level:0
+        }
+        this.lastLevelClassifyName=""
+        this.$refs.classifyFormRef.clearValidate()
+      },
+      /* 加载更多 */
+      loadMoreSandbox:_.throttle(function(e) {
+        let scrollTop = this.$refs.pictureListRef.scrollTop;
+        let clientHeight = this.$refs.pictureListRef.clientHeight;
+        let scrollHeight = this.$refs.pictureListRef.scrollHeight;
+        // console.log('scrollTop:',scrollTop)
+        // console.log('clientHeight:',clientHeight)
+        // console.log('scrollHeight:',scrollHeight)
+        if(scrollTop + clientHeight >= scrollHeight-10 && this.pictureHaveMore){
+          this.searchParams.CurrentIndex++
+          this.getSandboxList();
+          console.log("加载更多");
+        }
+      },300),
+      /* 展示详情 */
+      detailShowHandle(item) {
+        this.viewSandbox=item
+        this.rightType='chart' 
+        this.initData = JSON.parse(item.Content)
+        let ParentClassifys = item.ParentIds.split(',').filter(Boolean)
+        ParentClassifys.push(item.SandboxClassifyId)
+        ParentClassifys.push(item.SandboxId)
+        this.sandboxLocation(ParentClassifys)
+      },
+      async sandboxLocation(ParentClassifys){
+        console.log(ParentClassifys,'ParentClassifys');
+        let beActiveNode=''
+        for (let i = 0; i < ParentClassifys.length; i++) {
+          const element = ParentClassifys[i];
+          // if(i == (ParentClassifys.length-1)){
+          //   console.log(+element,beActiveNode,'element');
+          //   let searchNode=this.zTreeObj.getNodesByParam('SandboxId',+element,beActiveNode)
+          //   console.log(searchNode[0]);
+          //   this.zTreeObj.selectNode(searchNode[0],false,false)
+          //   return 
+          // }
+          let searchNode=this.zTreeObj.getNodesByParam('SandboxClassifyId',+element,beActiveNode)
+          console.log(searchNode,'searchNode',i);
+          if(!(searchNode&&searchNode.length>0)){
+            if(!beActiveNode){
+              break
+            }
+            let flag =false
+            if(i == ParentClassifys.length-1){
+              // 最后一层找沙盘的id SandboxId
+              flag = await this.expandNodeAsync(beActiveNode,+element,true)
+            }else{
+              flag = await this.expandNodeAsync(beActiveNode,+element)
+            }
+
+            if(!flag){
+              return 
+            }else{
+              beActiveNode=flag[0]
+            }
+          }else{
+            beActiveNode = searchNode[0]
+          }
+        }
+        this.locationActiveNode=beActiveNode
+        // 选中
+        requestAnimationFrame(()=>{
+          this.zTreeObj.selectNode(beActiveNode)
+          this.activeNode = beActiveNode
+        })
+      },
+      expandNodeAsync(parentNode,activeId,isLast=false){
+        return new Promise((resolve,reject)=>{
+          let resultNode ;
+          if(isLast){
+            resultNode = this.zTreeObj.getNodesByParam('SandboxId',activeId,parentNode)
+          }else{
+            resultNode = this.zTreeObj.getNodesByParam('SandboxClassifyId',activeId,parentNode)
+          }
+          console.log(resultNode,'resolve(resultNode)');
+          if(resultNode.length>0){
+            resolve(resultNode)
+            return 
+          }
+          sandInterface.getSandboxClassify({SandboxClassifyId:parentNode.SandboxClassifyId,IsShowMe:this.searchParams.IsShowMe}).then(res=>{
+            if (res.Ret === 200) {
+              let nodesData=res.Data.AllNodes || []
+              nodesData.map(item =>{
+                item.isCatalogue = item.SandboxId?false:true
+              })
+              this.zTreeObj.addNodes(parentNode,nodesData)
+              if(isLast){
+                resolve(this.zTreeObj.getNodesByParam('SandboxId',activeId,parentNode))
+              }else{
+                resolve(this.zTreeObj.getNodesByParam('SandboxClassifyId',activeId,parentNode))
+              }
+            }else{
+              resolve(null)
+            }
+          })
+        })
+      },
+      copyHandle:_.debounce(function ({ PicUrl }){
+        if(!PicUrl) return this.$message('暂无内容可复制')
+        console.log("复制");
+        this.lockLoding = this.$loading({
+          lock: true,
+          text: '复制图片中...',
+          target: '.sandbox-pictures-box',
+          spinner: 'el-icon-loading',
+          background: 'rgba(255, 255, 255, 0.8)'
+        });
+        const canvas = document.createElement("canvas");
+        const ctx = canvas.getContext("2d");
+        const img = new Image();
+        img.crossOrigin = "Anonymous";
+        img.src = PicUrl;
+        img.onload = ()=>{
+          canvas.width = img.width;
+          canvas.height = img.height;
+          ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+          ctx.fillStyle="#fff";
+          ctx.fillRect(0, 0, img.width, img.height);
+          ctx.drawImage(img, 0, 0);
+          if(window.ClipboardItem) {
+            canvas.toBlob(async (blob) => {
+              const data = [new ClipboardItem({ [blob.type]: blob })]; 
+              await navigator.clipboard.write(data).then(
+                () => {
+                this.$message.success('复制成功!')
+                },
+                () => {
+                  this.$message.warning('浏览器不支持')
+                }
+              ).finally(()=>{
+                this.lockLoding && this.lockLoding.close();
+              });
+            });
+          }else {
+            this.lockLoding && this.lockLoding.close();
+            this.$message.warning('当前协议暂不支持,仅支持https协议')
+          }
+        }
+      },500),
+      deleteHandle(item,type){
+        console.log(item,type);
+        this.$confirm("确定删除该沙盘图吗?", "提示", {
+          type: "warning",
+        })
+        .then(() => {
+          sandInterface.deleteSandbox({
+          SandboxClassifyId:item.SandboxClassifyId,
+          SandboxId:item.SandboxId,
+          }).then((res) => {
+            if (res.Ret === 200) {
+              this.$message.success(res.Msg);
+              let result = this.zTreeObj.getNodesByParam('SandboxId',item.SandboxId)[0]
+              console.log(result);
+              if(type=='inList'){
+                // 在沙盘图列表中删除
+                if(result){
+                  this.zTreeObj.removeNode(result)
+                }
+              }else{
+                // 在沙盘图详情删除
+                this.sandboxLocation([result.SandboxClassifyId])
+                this.zTreeObj.removeNode(result)
+              }
+              this.getSandboxList('setCurrentIndex')
+              
+            }
+          });
+        })
+        .catch(() => {});
+      },
+      addDiyDom(treeId, treeNode) {
+        // console.log(treeNode,'treeId, treeNode');
+        var aObj = $("#" + treeNode.tId + "_a");
+        if ($("#diyBtn_"+treeNode.SandboxClassifyId).length>0) return;
+        if(treeNode.isCatalogue){
+          //目录添加自定义按钮
+          let dom = $('#custom-button-zone')[0].cloneNode(true)
+          let addClassifyDom = $(dom).find('.add-classify-img')[0]
+          let editClassifyDom = $(dom).find('.edit-classify-img')[0]
+          let delClassifyDom = $(dom).find('.delete-classify-img')[0]
+          if(treeNode.Level>4){
+            addClassifyDom && (addClassifyDom.style.display='none')
+          }else{
+            addClassifyDom && addClassifyDom.addEventListener("click",(e)=>this.addClassify(e,treeNode))
+          }
+          editClassifyDom && editClassifyDom.addEventListener("click",(e)=>this.editClassify(e,treeNode))
+          delClassifyDom && delClassifyDom.addEventListener("click",(e)=>this.deleteClassify(e,treeNode))
+
+          aObj.append(dom);
+        }
+
+      },
+      // removeHoverDom(treeId, treeNode) {
+      //   console.log(treeId, treeNode,'treeId, treeNode');
+      //   $("#diyBtn_"+treeNode.SandboxClassifyId).unbind().remove();
+	    //   $("#diyBtn_space_" +treeNode.SandboxClassifyId).unbind().remove();
+      // }
+      addSand(){
+        const { href } = this.$router.resolve({ path: '/sandflow' });
+        window.open(href, '_blank');
+      },
+      editSand(){
+        const { href } = this.$router.resolve({ path: '/sandflow' ,query:{SandboxId:this.viewSandbox.SandboxId}});
+        window.open(href, '_blank');
+      },
+      saveOther(){
+        this.saveOtherForm.chartName = this.viewSandbox.Name+"(1)"
+        this.saveOtherShow=true
+      },
+      saveOtherSubmit: _.debounce( function() {
+        // console.log(this.viewSandbox);
+        // return 
+        if(!this.graph.toJSON().cells.length) return this.$message.warning('画布无内容');
+        this.$refs.saveOtherFormRef.validate(valid=>{
+          if(valid){
+            const { Content,MindmapData} = this.viewSandbox;
+            this.lockLoding = this.$loading({
+              lock: true,
+              text: '保存中...',
+              target: '.save-as-dialog',
+              spinner: 'el-icon-loading',
+              background: 'rgba(255, 255, 255, 0.8)'
+            });
+            const { cells } = this.graph.toJSON();
+            this.graph.toSVG(async (dataUri) => {
+              const params = new FormData();
+              params.append('Img',dataUri)
+              const { Data } = await dataBaseInterface.uploadImgSvg(params);
+
+              // return 
+              const { Ret , Data : sandData} = await sandInterface.sandboxSaveV2({
+                Name:this.saveOtherForm.chartName,
+                SandboxClassifyId: Number(this.saveOtherForm.classifyId),
+                Content,
+                PicUrl: Data?Data.ResourceUrl:'',
+                SvgData: dataUri,
+                MindmapData
+              })
+
+              if(Ret !== 200){
+                this.lockLoding.close();
+                return;
+              }
+              console.log(sandData,'sandData');
+              this.lockLoding.close();
+              this.$message.success("另存为成功")
+              let saveOtherIds = this.saveOtherClassifys.split(',')
+              saveOtherIds.push(sandData.SandboxId)
+              this.sandboxLocation(saveOtherIds)
+              this.saveOtherShow=false
+            },{
+              preserveDimensions:true,//让svg为实际图片大小
+              beforeSerialize:(svg)=>{
+                const {x,y,width,height} = this.graph.getContentBBox(cells)
+                let {tx,ty} = this.graph.translate() // 画布偏移量
+                //给导出的svg增加一点宽高
+                svg.setAttribute('width',width+50)
+                svg.setAttribute('height',height+50) 
+                //设置viewBox使图像居中
+                svg.setAttribute('viewBox',`${x-25} ${y-25} ${width+50} ${height+50}`)
+                // 在图表右下方 加上"来源:弘则研究"字样
+                let gNode = svg.getElementsByClassName('x6-graph-svg-viewport')[0]
+                // 去掉不该截图的添加图标
+                let leftImg = svg.getElementsByClassName('left-topic-image')
+                for (let i = 0; i < leftImg.length; i++) {
+                  const element = leftImg[i];
+                  element.parentElement.removeChild(element)
+                  i--
+                }
+                let rightImg = svg.getElementsByClassName('right-topic-image')
+                for (let i = 0; i < rightImg.length; i++) {
+                  const element = rightImg[i];
+                  element.parentElement.removeChild(element)
+                  i--
+                }
+                let textNode = document.createElement('text')
+                textNode.setAttribute('x',x-tx+width-90)
+                textNode.setAttribute('y',y-ty+height+22)
+                textNode.setAttribute('font-size','16px')
+                textNode.setAttribute('font-style','italic')
+                textNode.innerText = '来源:弘则研究'
+                gNode.appendChild(textNode)
+                  },
+              copyStyles:false,
+              stylesheet: `
+                  svg{
+                      background-color:white;
+                  }
+                .x6-port {
+                    visibility: hidden;
+                }
+                ` 
+            })
+          }
+        })
+
+          
+      },500),
+      saveOtherClosed(){
+        this.saveOtherForm={
+          chartName:"逻辑图名称",
+          classifyId:""
+        }
+        this.selectZTreeObj.cancelSelectedNode()
+        this.$refs.saveOtherFormRef.clearValidate()
+      },
+      copySandHandle: _.debounce(function() {
+        const { cells } = this.graph.toJSON();
+        if(!cells.length) return this.$message.warning('当前画布无可复制内容');
+
+        this.lockLoding = this.$loading({
+          lock: true,
+          text: '复制图片中...',
+          target: '.sandbox-chart-box',
+          spinner: 'el-icon-loading',
+          background: 'rgba(255, 255, 255, 0.8)'
+        });
+        this.graph.toSVG(async(dataUri) => {
+
+        const canvas = document.createElement("canvas");
+        const ctx = canvas.getContext("2d");
+        const img = new Image();
+        img.crossOrigin = "Anonymous";
+        img.src = svgToBase64(dataUri);
+        img.onload = ()=>{
+          canvas.width = img.width;
+          canvas.height = img.height;
+          // console.log('width',img.width)
+          // console.log('height',img.height)
+          ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+          ctx.fillStyle="#fff";
+          ctx.fillRect(0, 0, img.width, img.height);
+          ctx.drawImage(img, 0, 0);
+          if(window.ClipboardItem) {
+            canvas.toBlob(async (blob) => {
+                const data = [new ClipboardItem({ [blob.type]: blob })]; 
+                await navigator.clipboard.write(data).then(
+                () => {
+                    this.$message.success('复制成功!')
+                },
+                () => {
+                    this.$message.warning('浏览器不支持')
+                }
+                ).finally(()=>{
+                    this.lockLoding && this.lockLoding.close();
+                });
+            });
+          }else {
+            this.lockLoding && this.lockLoding.close();
+            this.$message.warning('当前协议暂不支持,仅支持https协议')
+          }	
+        }
+      },{
+        preserveDimensions:true,//让svg为实际图片大小
+        beforeSerialize:(svg)=>{
+          const {x,y,width,height} = this.graph.getContentBBox(cells)
+          let {tx,ty} = this.graph.translate()
+          //给导出的svg增加一点宽高
+          svg.setAttribute('width',width+50)
+          svg.setAttribute('height',height+50) 
+          //设置viewBox使图像居中
+          svg.setAttribute('viewBox',`${x-25} ${y-25} ${width+50} ${height+50}`)
+          let gNode = svg.getElementsByClassName('x6-graph-svg-viewport')[0]
+          // 去掉不该截图的添加图标
+          let leftImg = svg.getElementsByClassName('left-topic-image')
+          for (let i = 0; i < leftImg.length; i++) {
+            const element = leftImg[i];
+            element.parentElement.removeChild(element)
+            i--
+          }
+          let rightImg = svg.getElementsByClassName('right-topic-image')
+          for (let i = 0; i < rightImg.length; i++) {
+            const element = rightImg[i];
+            element.parentElement.removeChild(element)
+            i--
+          }
+          let textNode = document.createElement('text')
+          textNode.setAttribute('x',x-tx+width-90)
+          textNode.setAttribute('y',y-ty+height+22)
+          textNode.setAttribute('font-size','16px')
+          textNode.setAttribute('font-style','italic')
+          textNode.innerText = '来源:弘则研究'
+          gNode.appendChild(textNode)
+            },
+        copyStyles:false,
+        stylesheet: `
+            svg{
+                background-color:white;
+            }
+          .x6-port {
+              visibility: hidden;
+          }` 
+        })
+      },500),
+      selectClassify(event,treeId,treeNode,clickFlag){
+        // console.log(treeNode);
+        this.saveOtherForm.classifyId = treeNode.SandboxClassifyId
+        this.saveOtherClassifys=this.getParentClassifyIds(treeNode)
+        this.selectClassifyShow = false
+      },
+      getParentClassifyIds(treeNode){
+        let parentNode = treeNode.getParentNode()
+        if(parentNode){
+          return this.getParentClassifyIds(parentNode)+','+treeNode.SandboxClassifyId
+        }else{
+          return treeNode.SandboxClassifyId+''
+        }
+      },
+      //==============================画布
+      // 初始化画布
+      init() {
+        const graph = new myGraph('sand-chart-body',null,'','view');
+        console.log(graph,'graph');
+        this.graph = graph;
+        graph.on('node:mouseenter', ({ node, e }) => {
+          console.log(node);
+          let data = node.data
+          this.linkNode = node
+          if(data && data.linkData && data.linkData.length>0){
+            this.popoverFlod = data.linkFold
+            this.popoverVisible=false
+            clearTimeout(this.popoverTimeout)
+            this.popoverTimeout=null
+            let currentLinks = data.linkData
+            //指标id
+            let edbInfoIdList = data.linkData.filter(it => it.Type==1).map(it => it.Id)
+            // 图库id
+            let chartInfoIdList = data.linkData.filter(it => it.Type==2).map(it => it.Id)
+            // 报告id
+            let reportIdList = data.linkData.filter(it => it.Type==3).map(it => it.Id)
+            sandInterface.sandboxLinkCheck({EdbInfoIdList:edbInfoIdList,ChartInfoIdList:chartInfoIdList,ReportIdList:reportIdList}).then(res=>{
+              if(res.Ret == 200){
+                let EdbInfoIdList = res.Data.EdbInfoIdList || []
+                let ChartInfoIdList = res.Data.ChartInfoIdList || []
+                let ReportIdList = res.Data.ReportIdList || []
+                this.checkedLinkList = currentLinks.filter(link =>{
+                  return EdbInfoIdList.includes(link.Id) || ChartInfoIdList.includes(link.Id) || ReportIdList.includes(link.Id)
+                })
+                let clinetPositon=graph.localToClient(node.position())
+                let size=node.size()
+                console.log(clinetPositon,'clinetPositon',node.position());
+                // const dom = $('#link-reference')[0];
+                // console.log(this.popoverTriggerDom,'domdomdom');
+                console.log(clinetPositon.x+size.width/2,clinetPositon.y);
+                this.popoverTriggerDom.style.left = clinetPositon.x+size.width/2 + 'px';
+                this.popoverTriggerDom.style.top = clinetPositon.y + 'px';
+                this.popoverVisible=true
+              }
+            })
+          }
+        })
+      
+        graph.on('node:mouseleave', ({ node, e }) => {
+          if(!this.popoverTimeout){
+            this.popoverTimeout= setTimeout(()=>{
+              this.popoverVisible=false
+              this.popoverTriggerDom.style.left = '-99999px';
+              this.popoverTriggerDom.style.top = '-99999px';
+              this.popoverTimeout=null
+            },500)
+          }
+        })
+		  },
+      foldLink(){
+        this.popoverFlod=!this.popoverFlod 
+        if(this.linkNode){
+          this.linkNode.data.linkFold = this.popoverFlod
+        }
+        this.popoverVisible=false
+        this.$nextTick(()=>{
+          this.popoverVisible=true
+        })
+      },
+      navigateTo(item){
+        console.log(item,'item');
+        if(item.Type == 1){
+          if(item.databaseType==0){
+            // 普通指标
+            const { href } = this.$router.resolve({ path: '/database',query:item.detailParams});
+            window.open(href, '_blank');
+          }else{
+            // 预测指标
+            const { href } = this.$router.resolve({ path: '/predictEdb',query:item.detailParams});
+            window.open(href, '_blank');
+          }
+        }else if(item.Type == 2){
+          // 跳转到图库详情
+          const { href } = this.$router.resolve({ path: '/chartsetting',query:item.detailParams});
+          window.open(href, '_blank');
+        }else if(item.Type == 3){
+          // 跳转到研报
+          const { href } = this.$router.resolve({ path: '/reportdtl',query:item.detailParams});
+          window.open(href, '_blank');
+        }
+      },
+      clearPopoverTimeout(){
+        clearTimeout(this.popoverTimeout)
+        this.popoverTimeout=null 
+      },
+      closePopover(){
+        this.popoverVisible=false
+      },
+      /* 向左收起 展开 */
+      slideHandle() {
+        this.isSlideLeft = !this.isSlideLeft;
+      },
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+#sandbox-index-container{
+  height: calc(100vh - 120px);
+  min-height: 600px;
+  display: flex;
+  align-items: flex-start;
+  flex-wrap: nowrap;
+  .sandbox-content-tree-box{
+    background-color: white;
+    min-width: 20vw;
+    width: 20vw;
+    height: 100%;
+    border: solid 1px #DCDFE6;
+    border-radius: 4px;
+    box-sizing: border-box;
+    margin-right: 16px;
+    position: relative;
+    .sandbox-content-tree-header{
+      box-sizing: border-box;
+      padding: 20px;
+      border-bottom: solid 1px #DCDFE6;
+      box-shadow: 0px 2px 12px 0px rgba($color: #000000, $alpha: 0.08);
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+    }
+    .sandbox-content-tree-body{
+      padding: 20px 0;
+      box-sizing: border-box;
+      height: calc(100% - 80px);
+      .sandbox-content-tree{
+        height: calc(100% - 60px);
+        padding: 0 20px;
+        overflow: auto;
+        scroll-behavior: smooth;
+        .add-classify{
+          margin: 30px 0 50px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          img{
+            width: 12px; 
+            height: 12px; 
+            margin-right: 6px;
+            cursor: pointer;
+          }
+          span{
+            font-size: 14px;
+            color: #0052D9;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+  }
+  .sandbox-pictures-box{
+    flex-grow: 1;
+    height: 100%;
+    .pictures-count{
+      font-size: 14px;
+      color: #333333; 
+      margin-bottom: 14px;
+    }
+    .pictures-box{
+      display: flex;
+      flex-wrap: wrap;
+      max-height: calc(100% - 34px);
+      overflow: hidden;
+      overflow-y: auto;
+      .pictures-item{
+        background-color: white;
+        border: 1px solid #DCDFE6;
+        padding: 0 10px 10px;
+        .pictures-item-header{
+          padding: 10px 0;
+          border-bottom: 1px solid #DCDFE6;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          .text_oneLine{
+            font-size: 16px;
+            color: #333333;
+            font-weight: 500;
+            
+          } 
+        }
+        .picture-img{
+          width: 100%;
+          height: 230px;
+          object-fit: fill !important;
+          cursor: pointer;
+          padding: 10px 0;
+        }
+        .item-bottom{
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          span{
+            font-size: 14px;
+            color: #333333;
+          }
+          .item-bottom-buttons{
+            width: 75px;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            span{
+              color: #0052D9;
+              cursor: pointer;
+            }
+          }
+        }
+      }
+    }
+  }
+  .sandbox-chart-box{
+    background-color: white;
+    // flex-grow: 1;
+    height: 100%;
+    border: 1px solid #DCDFE6;
+    border-radius: 4px;
+    overflow: hidden;
+    .sandbox-chart-head{
+      padding: 20px;
+      border-bottom: 1px solid #DCDFE6;
+      box-shadow: 0px 2px 12px 0px rgba($color: #000000, $alpha: 0.08);
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      flex-wrap: nowrap;
+      white-space: nowrap;
+      .sandbox-chartHead-author{
+        font-size: 16px;
+        color: #666666;
+      }
+      .sandbox-chartHead-title{
+        font-size: 16px;
+        color: #333333;
+        padding: 0 12px;
+      }
+      .sandbox-chartHead-options{
+        display: flex;
+        align-items: center;
+        margin-right: -20px;
+        .chartHead-options-button{
+          margin-right: 20px;
+          display: flex;
+          align-items: center;
+          flex-wrap: nowrap;
+          cursor: pointer;
+          img{
+            height: 16px;
+            margin-right: 3px;
+          }
+          span{
+            color: #0052D9;
+          }
+        }
+      }
+    }
+  }
+
+  .slide-icon {
+    padding: 20px 0;
+    /* display: block; */
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.3);
+    border-radius: 5px;
+    cursor: pointer;
+    position: absolute;
+    top: 50%;
+    transform: translateY(-50%);
+    z-index: 99;
+    &:hover {
+      background-color: rgba(0, 0, 0, 0.05);
+    }
+    &.slide-left {
+      right: 0;
+    }
+    &.slide-right {
+      left: 0;
+    }
+  }
+  @media screen and (max-width:1680px) {
+    .sandbox-content-tree-box{
+      min-width: 330px;
+      width: 330px;
+    }
+
+  }
+  .custom-button-zone{
+    // display: flex;
+    flex-wrap: nowrap;
+    align-items: center;
+    justify-content: flex-start;
+    position: relative;
+    display: none;
+    right: 12px;
+    height: 100%;
+    img{
+      cursor: pointer;
+      height: 16px;
+      margin-right: 8px;
+    }
+  }
+}
+#link-popover{
+  display: flex;
+  justify-content: space-between;
+  transition: all 0.3s ease;
+  overflow: hidden;
+  .link-box{
+    .link-item{
+      &:hover{
+        text-decoration: underline;
+        color: #0052D9;
+        cursor: pointer;
+      }
+    }
+  }
+  .link-fold{
+    transition: all 0.3s ease;
+    height: 16px;
+    width: 16px;
+    cursor: pointer;
+  }
+}
+</style>
+<style lang="scss">
+.vue-giant-tree{
+  li{
+    ul{
+      padding: 0 0 0 10px!important;
+    }
+    .button{
+      z-index: 1;
+      height: 30px;
+      width: 20px;
+      &::before{
+        border:none!important;
+        top: 50%!important;
+        left: 50%!important;
+        transform: translate(-50%,-50%)!important;
+        height: 16px!important;
+        width: 16px!important;
+      }
+    }
+    .button.noline_close{
+      &::before{
+        content: url('../../assets/img/set_m/slide_black.png')!important;
+      }
+    }
+    .button.noline_open{
+      &::before{
+        content: url('../../assets/img/set_m/down_black.png')!important;
+      }
+    }
+    a{
+      width: calc(100% - 22px);
+      height: 30px!important;
+      line-height: 30px!important;
+      .node_name{
+        width: calc(100% - 6px);
+        color: #333333!important;
+        border-radius: 0!important;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
+    .curSelectedNode{
+      position: relative;
+      z-index: 0;
+      display: inline-flex;
+      flex-wrap: nowrap;
+      align-items: center;
+      .node_name{
+        flex: 1;
+        color: unset;
+        background-color: transparent;
+        &::after{
+          content: '';
+          position: absolute;
+          top: 0;
+          left: -22px;
+          height: 30px;
+          width: calc(100% + 22px);
+          background-color: #ECF2FE;
+          z-index: -1;
+        }
+      }
+      .custom-button-zone{
+        display: flex!important;
+      }
+    }
+  }
+
+}
+.ztree.zTreeDragUL{
+  width: 300px;
+	z-index: 10000!important;
+}
+.tmpzTreeMove_arrow{
+  position: absolute;
+	z-index: 10000!important;
+  &::after{
+    content: url('../../assets/img/sand_new/tools/arrow-end.png');
+  }
+}
+.el-cascader{
+  .el-input{
+    width: 317px;
+  }
+}
+.classify-popper{
+  height: 400px;
+  overflow: auto;
+}
+.classify-cascader-popper{
+  display: none;
+}
+.sandbox-body{
+  height: calc(100% - 62px);
+  display: flex;
+  position: relative;
+  #link-reference{
+    position: fixed;
+    z-index: -1;
+    top: -99999px;
+    left: -99999px;
+    background-color: transparent;
+  }
+  .minimap{
+    position:absolute;
+    right:6px;
+    bottom:6px;
+    box-sizing: border-box;
+  }
+  #sand-chart-body{
+    flex: 1;
+  }
+  .x6-graph-scroller {
+    flex: 1;
+  }
+
+  .x6-port-body {
+    display: none;
+  }
+
+  /* reseize 框样式 */
+  .x6-widget-transform {
+    .x6-widget-transform-resize {
+      border-radius: 0;
+    }
+  }
+  .x6-widget-minimap-viewport{
+    border-color: red;
+    .x6-widget-minimap-viewport-zoom{
+      border-color: red;
+    }
+  }
+  .x6-widget-minimap{
+    width: auto !important;
+    height: auto !important;
+  }
+  .left-topic-image,.right-topic-image {
+    display: none;
+    pointer-events:none;
+  }
+}
+</style>

+ 2505 - 0
src/views/sandbox_manage/sandFlowNew/index.vue

@@ -0,0 +1,2505 @@
+<template>
+  <div id="sand-edit-container" class="sand-edit-container">
+    <span class="slide-icon slide-right" @click="slideHandle" v-show="isSlideLeft">
+      <i class="el-icon-d-arrow-right"></i>
+    </span>
+    <div class="sand-toolbar" v-show="!isSlideLeft">
+      <span class="slide-icon slide-left" @click="slideHandle">
+        <i class="el-icon-d-arrow-left"></i>
+      </span>
+      <el-tabs v-model="activeToolTabName" stretch class="sand-toolbar-tabs">
+        <el-tab-pane label="元素库" name="元素库" id="element">
+          <div class="sand-elements-tab">
+            <div class="sand-elements sand-elements-line">
+              <span>线条</span>
+              <div class="elements-row" >
+                <img src="~@/assets/img/sand_new/no-arrow-straight.png" :draggable="true" @dragstart="edgeDragStart('noArrowStraight',$event)" />
+                <img src="~@/assets/img/sand_new/single-arrow-straight.png" :draggable="true" @dragstart="edgeDragStart('singleArrowStraight',$event)" />
+                <img src="~@/assets/img/sand_new/double-arrow-straight.png" :draggable="true" @dragstart="edgeDragStart('doubleArrowStraight',$event)"/>
+                <img src="~@/assets/img/sand_new/no-arrow.png" :draggable="true" @dragstart="edgeDragStart('noArrowBend',$event)"/>
+                <img src="~@/assets/img/sand_new/single-arrow.png" :draggable="true" @dragstart="edgeDragStart('singleArrowBend',$event)"/>
+                <img src="~@/assets/img/sand_new/double-arrow.png" :draggable="true" @dragstart="edgeDragStart('doubleArrowBend',$event)"/>
+                <img src="~@/assets/img/sand_new/no-arrow-round.png" :draggable="true" @dragstart="edgeDragStart('noArrowRoundBend',$event)"/>
+                <img src="~@/assets/img/sand_new/single-arrow-round.png" :draggable="true" @dragstart="edgeDragStart('singleArrowRoundBend',$event)"/>
+                <img src="~@/assets/img/sand_new/double-arrow-round.png" :draggable="true" @dragstart="edgeDragStart('doubleArrowRoundBend',$event)"/>
+              </div>
+            </div>
+            <div class="sand-elements sand-elements-shape">
+              <span>基本形状</span>
+              <div class="elements-row">
+                <div class="elements-shape-item" v-for="shape in myNodes" :key="shape.key">
+                  <div 
+                    :style="shape.styles" 
+                    @mousedown="dragStart(shape,$event)">
+                    {{shape.label}}
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div class="sand-elements sand-elements-mind">
+              <span>思维导图</span>
+              <div class="elements-row-mind">
+                <img src="~@/assets/img/sand_new/mindmap-right.png" :draggable="true" @dragstart="edgeDragStart('singleMindmap',$event)"/>
+                <img src="~@/assets/img/sand_new/mindmap-double.png" :draggable="true" @dragstart="edgeDragStart('doubleMindmap',$event)"/>
+              </div>
+            </div>
+          </div>
+
+        </el-tab-pane>
+        <el-tab-pane label="风格" name="风格" id="style">
+          <div class="sand-style-tab">
+            <div class="sand-style-tab-item" :class="styleActive==1?'active':''" @click="changeStyle(1)" :draggable="false">
+              <img src="~@/assets/img/sand_new/style-blue-light.png" />
+            </div>
+            <div class="sand-style-tab-item" :class="styleActive==2?'active':''" @click="changeStyle(2)" :draggable="false">
+              <img src="~@/assets/img/sand_new/style-blue.png" />
+            </div>
+            <div class="sand-style-tab-item" :class="styleActive==3?'active':''" @click="changeStyle(3)" :draggable="false">
+              <img src="~@/assets/img/sand_new/style-black.png" />
+            </div>
+            <div class="sand-style-tab-item" :class="styleActive==4?'active':''" @click="changeStyle(4)" :draggable="false">
+              <img src="~@/assets/img/sand_new/style-red.png" />
+            </div>
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+    <div class="sand-main">
+      <div class="sand-main-top">
+        <div class="sand-mainTop-form">
+          <el-input
+            v-model="sandSaveParams.Name"
+            style="width: 240px; margin-right: 20px"
+            placeholder="请输入逻辑图名称">
+            <i slot="prefix" class="el-input__icon el-icon-search"></i>
+          </el-input>
+          <el-popover
+            placement="bottom"
+            width="400"
+            popper-class="classify-popper"
+            trigger="click"
+            v-model="selectClassifyShow">
+              <tree :nodes="treeData" :setting="selectSetting" @onCreated="getSelectZTree" @onClick="selectClassify" />
+              <el-cascader 
+                slot="reference"
+                :options="treeData"
+                :props="{children: 'Children',
+                  label: 'SandboxClassifyName',
+                    value: 'SandboxClassifyId',emitPath:false,checkStrictly:true}"
+                v-model="sandSaveParams.SandboxClassifyId" 
+                popper-class="classify-cascader-popper"
+                placeholder="请选择分类">
+              </el-cascader>
+          </el-popover>
+        </div>
+        <div class="sand-mainTop-option">
+          <!-- <div class="sand-option-linkShow">
+            <img src="~@/assets/img/sand_new/eye-show-black.png" />
+            <span>链接展示</span>
+          </div> -->
+          <!-- <el-button size="large" type="primary" @click="backList" style="margin-right: 20px;min-width: 120px;"
+          v-if="$route.query.SandboxId">返回</el-button> -->
+          <el-button size="large" type="primary" plain @click="copySandHandle" style="margin-right: 20px;min-width: 120px;"
+          v-permission="permissionBtn.sandboxPermission.sandbox_addMy">复制图片</el-button>
+          <el-button type="primary" size="large" @click="saveChart(null)" style="min-width: 120px;" v-permission="permissionBtn.sandboxPermission.sandbox_saveView">保存</el-button>
+        </div>
+      </div>
+      <!-- <div class="sand-main-body"> -->
+        <div class="sand-mainBody-chart" id="sand-mainBody-chart">
+          <div class="sand-mainBody-tool" id="sand-mainBody-tool">
+            <el-tooltip content="撤销(Ctrl+Z)" placement="top" :open-delay="250">
+              <img :src="canUndo?require('@/assets/img/sand_new/tools/undo.png'):require('@/assets/img/sand_new/tools/undo-disabled.png')" 
+              class="sand-tool-item"  @click="toolClickOptions('undo',!canUndo)" :class="canUndo?'':'tool-disabled'"/>
+            </el-tooltip>
+            <el-tooltip content="恢复(Ctrl+Y)" placement="top" :open-delay="250">
+              <img :src="canRedo?require('@/assets/img/sand_new/tools/redo.png'):require('@/assets/img/sand_new/tools/redo-disabled.png')" 
+              class="sand-tool-item"  @click="toolClickOptions('redo',!canRedo)" :class="canRedo?'':'tool-disabled'"/>
+            </el-tooltip>
+            <div class="sand-tool-item">
+                <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeFamily',nodeTextDisable,e)"
+                  placement="bottom">
+                  <div :class="nodeTextDisable?'tool-disabled':''" class="dropdown-box">
+                    <el-tooltip content="字体" placement="top" :open-delay="250">
+                      <div class="dropdown-content"  >
+                        <div class="dropdown-content-text" style="width:28px">{{ styleOptions.fontF }}</div>
+                        <img src="~@/assets/img/sand_new/tools/select-icon.png" style="width: 8px;height: 5px;"/>
+                      </div>
+                    </el-tooltip>
+                    <el-tooltip content="字体" placement="top" :open-delay="250">
+                      <span v-show="nodeTextDisable" class="disabled-item" @click.stop="()=>{}"></span>
+                    </el-tooltip>
+                  </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item v-for="f in familyOptions" :command="f.value" :key="f.value"
+                  :class="styleOptions.fontF==f.value?'style-acitve':''">
+                      {{ f.name }}
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            <div class="sand-tool-item">
+              <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeSize',nodeTextDisable,e)"
+                placement="bottom">
+                <div :class="nodeTextDisable?'tool-disabled':''" class="dropdown-box">
+                  <el-tooltip content="字号" placement="top" :open-delay="250">
+                    <div class="dropdown-content"  >
+                      <div class="dropdown-content-text" >{{ styleOptions.fontS }}px</div>
+                      <img src="~@/assets/img/sand_new/tools/select-icon.png" style="width: 8px;height: 5px;"/>
+                    </div>
+                  </el-tooltip>
+                  <el-tooltip content="字号" placement="top" :open-delay="250">
+                    <span v-show="nodeTextDisable" class="disabled-item" @click.stop="()=>{}"></span>
+                  </el-tooltip>
+                </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item v-for="s in fontSizeOptions" :command="s.value" :key="s.value"
+                  :class="styleOptions.fontS==s.value?'style-acitve':''">
+                      {{ s.name }}
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            <el-tooltip content="加粗(Ctrl+B)" placement="top" :open-delay="250">
+              <div class="sand-tool-item img-box" :class="styleOptions.fontW=='bold'?'style-acitve-back':''">
+                <img :class="nodeTextDisable?'tool-disabled':''" :src="nodeTextDisable?
+                  require('@/assets/img/sand_new/tools/bold-disabled.png'):
+                  require('@/assets/img/sand_new/tools/bold.png')"  @click="toolClickOptions('changeWeight',nodeTextDisable)"/>
+              </div>
+
+            </el-tooltip>      
+            <el-tooltip content="斜体" placement="top" :open-delay="250">
+              <div class="sand-tool-item img-box" :class="styleOptions.fontStyle=='italic'?'style-acitve-back':''">
+                <img :class="nodeTextDisable?'tool-disabled':''" :src="nodeTextDisable?
+                  require('@/assets/img/sand_new/tools/italic-disabled.png'):
+                  require('@/assets/img/sand_new/tools/italic.png')"  @click="toolClickOptions('changeFontStyle',nodeTextDisable)"/>
+              </div>
+            </el-tooltip>      
+            <el-tooltip content="下划线" placement="top" :open-delay="250">
+              <div class="sand-tool-item img-box" :class="styleOptions.textDecoration=='underline'?'style-acitve-back':''">
+                <img :class="nodeTextDisable?'tool-disabled':''" :src="nodeTextDisable?
+                  require('@/assets/img/sand_new/tools/underline-disabled.png'):
+                  require('@/assets/img/sand_new/tools/underline.png')" @click="toolClickOptions('changeDecoration',nodeTextDisable)"/>
+              </div>
+            </el-tooltip>      
+            <div class="sand-tool-item sand-tool-img">
+                <img :src="nodeTextDisable?
+                    require('@/assets/img/sand_new/tools/text-color-disabled.png'):
+                    require('@/assets/img/sand_new/tools/text-color.png')" />
+              <el-tooltip content="字体颜色" placement="top" :open-delay="250">
+                <el-color-picker
+                  key="textColor"
+                  v-model="styleOptions.color"
+                  size="mini"
+                  :predefine="colorsOptions"
+                  @change="(e)=>toolClickOptions('changeColor',nodeTextDisable,e)"
+                  style="position: absolute;top: 0;left: 0;width: 16px;height: 16px;opacity: 0;"
+                  :disabled="nodeTextDisable"
+                />
+              </el-tooltip>
+            </div>
+            <div class="sand-tool-item">
+              <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeLineHeight',nodeTextDisable,e)" 
+                placement="bottom">
+                <div :class="nodeTextDisable?'tool-disabled':''" class="dropdown-box">
+                  <el-tooltip content="文本行高" placement="top" :open-delay="250">
+                    <div class="dropdown-content"  >
+                      <img :src="nodeTextDisable? 
+                        require('@/assets/img/sand_new/tools/line-height-disabled.png'):
+                        require('@/assets/img/sand_new/tools/line-height.png')" 
+                        style="vertical-align: middle;"/>
+                    </div>
+                  </el-tooltip>
+                  <el-tooltip content="文本行高" placement="top" :open-delay="250">
+                    <span v-show="nodeTextDisable" class="disabled-item" @click.stop="()=>{}"></span>
+                  </el-tooltip>
+                </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item v-for="s in lineHeightOptions" :command="s" :key="s"
+                  :class="styleOptions.lineHeight==s?'style-acitve':''">
+                      {{ s }}
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            <div class="sand-tool-item">
+              <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeTextAlign',nodeTextDisable,e)" 
+                placement="bottom">
+                <div :class="nodeTextDisable?'tool-disabled':''" class="dropdown-box">
+                  <el-tooltip content="文本对齐" placement="top" :open-delay="250">
+                    <div class="dropdown-content"  >
+                      <img :src="nodeTextDisable? 
+                        require('@/assets/img/sand_new/tools/text-align-disabled.png'):
+                        require('@/assets/img/sand_new/tools/text-align.png')" 
+                        style="vertical-align: middle;"/>
+                    </div>
+                  </el-tooltip>
+                  <el-tooltip content="文本对齐" placement="top" :open-delay="250">
+                    <span v-show="nodeTextDisable" class="disabled-item" @click.stop="()=>{}"></span>
+                  </el-tooltip>
+                </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item command="middle" :class="styleOptions.textAlign=='middle'?'style-acitve':''">
+                    居中
+                  </el-dropdown-item>
+                  <el-dropdown-item command="start" :class="styleOptions.textAlign=='start'?'style-acitve':''">
+                    居左
+                  </el-dropdown-item>
+                  <el-dropdown-item command="end" :class="styleOptions.textAlign=='end'?'style-acitve':''">
+                    居右
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            <div class="sand-tool-item sand-tool-img">
+              <img :src="toolStatus.nodeDisabled?
+                    require('@/assets/img/sand_new/tools/background-color-disabled.png'):
+                    require('@/assets/img/sand_new/tools/background-color.png')" />
+              <el-tooltip content="颜色填充" placement="top" :open-delay="250">
+                <el-color-picker
+                  key="backgroundColor"
+                  v-model="styleOptions.backgroundColor"
+                  size="mini"
+                  :predefine="colorsOptions"
+                  @change="(e)=>toolClickOptions('changeBackgroundColor',toolStatus.nodeDisabled,e)"
+                  style="position: absolute;top: 0;left: 0;width: 16px;height: 16px;opacity: 0;"
+                  :disabled="toolStatus.nodeDisabled"
+                />
+              </el-tooltip>
+            </div>
+            <div class="sand-tool-item sand-tool-img">
+              <img :src="nodeEdgeDisable?
+                    require('@/assets/img/sand_new/tools/line-color-disabled.png'):
+                    require('@/assets/img/sand_new/tools/line-color.png')" />
+              <el-tooltip content="线条颜色" placement="top" :open-delay="250">
+                <el-color-picker
+                  key="lineColor"
+                  v-model="styleOptions.lineColor"
+                  size="mini"
+                  :predefine="colorsOptions"
+                  @change="(e)=>toolClickOptions('changeLineColor',nodeEdgeDisable,e)"
+                  style="position: absolute;top: 0;left: 0;width: 16px;height: 16px;opacity: 0;"
+                  :disabled="nodeEdgeDisable"
+                />
+              </el-tooltip>
+            </div>
+            <div class="sand-tool-item">
+              <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeLineWidth',nodeEdgeDisable,e)"
+                placement="bottom">
+                <div :class="nodeEdgeDisable?'tool-disabled':''" class="dropdown-box">
+                  <el-tooltip content="线条宽度" placement="top" :open-delay="250">
+                    <div class="dropdown-content"  >
+                      <img :src="nodeEdgeDisable? 
+                        require('@/assets/img/sand_new/tools/line-width-disabled.png'):
+                        require('@/assets/img/sand_new/tools/line-width.png')" 
+                        style="vertical-align: middle;"/>
+                    </div>
+                  </el-tooltip>
+                  <el-tooltip content="线条宽度" placement="top" :open-delay="250">
+                    <span v-show="nodeEdgeDisable" class="disabled-item" @click.stop="()=>{}"></span>
+                  </el-tooltip>
+                </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item :command="1" :class="styleOptions.lineWidth==1?'style-acitve':''">
+                    1
+                  </el-dropdown-item>
+                  <el-dropdown-item :command="2" :class="styleOptions.lineWidth==2?'style-acitve':''">
+                    2
+                  </el-dropdown-item>
+                  <el-dropdown-item :command="3" :class="styleOptions.lineWidth==3?'style-acitve':''">
+                    3
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            <div class="sand-tool-item">
+              <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeStrokeDasharray',nodeEdgeDisable,e)"
+                placement="bottom">
+                <div :class="nodeEdgeDisable?'tool-disabled':''" class="dropdown-box">
+                  <el-tooltip content="线条样式" placement="top" :open-delay="250">
+                    <div class="dropdown-content"  >
+                      <img :src="nodeEdgeDisable? 
+                        require('@/assets/img/sand_new/tools/line-style-disabled.png'):
+                        require('@/assets/img/sand_new/tools/line-style.png')" 
+                        style="vertical-align: middle;"/>
+                    </div>
+                  </el-tooltip>
+                  <el-tooltip content="线条样式" placement="top" :open-delay="250">
+                    <span v-show="nodeEdgeDisable" class="disabled-item" @click.stop="()=>{}"></span>
+                  </el-tooltip>
+                </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item command="4 4" :class="styleOptions.lineStyle=='4 4'?'style-acitve':''">
+                    虚线
+                  </el-dropdown-item>
+                  <el-dropdown-item :command="null" :class="styleOptions.lineStyle==null?'style-acitve':''">
+                    实线
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            <div class="sand-tool-item">
+              <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeRouter',toolStatus.edgeDisabled,e)"
+                placement="bottom">
+                <div :class="toolStatus.edgeDisabled?'tool-disabled':''" class="dropdown-box">
+                  <el-tooltip content="连线类型" placement="top" :open-delay="250">
+                    <div class="dropdown-content"  >
+                      <img :src="toolStatus.edgeDisabled? 
+                        require('@/assets/img/sand_new/tools/line-type-disabled.png'):
+                        require('@/assets/img/sand_new/tools/line-type.png')" 
+                        style="vertical-align: middle;"/>
+                    </div>
+                  </el-tooltip>
+                  <el-tooltip content="连线类型" placement="top" :open-delay="250">
+                    <span v-show="toolStatus.edgeDisabled" class="disabled-item" @click.stop="()=>{}"></span>
+                  </el-tooltip>
+                </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item :command="1" :class="styleOptions.connectStyle==1?'style-acitve-back':''">
+                    <img src="~@/assets/img/sand_new/tools/line-style-straight.png" style="width: 17px;height: 16px;"/>
+                  </el-dropdown-item>
+                  <el-dropdown-item :command="2" :class="styleOptions.connectStyle==2?'style-acitve-back':''">
+                    <img src="~@/assets/img/sand_new/tools/line-style-bend.png" style="width: 17px;height: 16px;"/>
+                  </el-dropdown-item>
+                  <el-dropdown-item :command="3" :class="styleOptions.connectStyle==3?'style-acitve-back':''">
+                    <img src="~@/assets/img/sand_new/tools/line-style-bend-round.png" style="width: 17px;height: 16px;"/>
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            <div class="sand-tool-item">
+              <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeSourceMarker',toolStatus.edgeDisabled,e)"
+                placement="bottom">
+                <div :class="toolStatus.edgeDisabled?'tool-disabled':''" class="dropdown-box">
+                  <el-tooltip content="开始箭头" placement="top" :open-delay="250">
+                    <div class="dropdown-content"  >
+                      <img :src="toolStatus.edgeDisabled? 
+                        require('@/assets/img/sand_new/tools/arrow-start-disabled.png'):
+                        require('@/assets/img/sand_new/tools/arrow-start.png')" 
+                        style="vertical-align: middle;"/>
+                    </div>
+                  </el-tooltip>
+                  <el-tooltip content="开始箭头" placement="top" :open-delay="250">
+                    <span v-show="toolStatus.edgeDisabled" class="disabled-item" @click.stop="()=>{}"></span>
+                  </el-tooltip>
+                </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item command="classic" :class="styleOptions.startArrow=='classic'?'style-acitve':''">
+                    有
+                  </el-dropdown-item>
+                  <el-dropdown-item :command="{}" :class="typeof(styleOptions.startArrow)=='string' || styleOptions.startArrow.name?'':'style-acitve'">
+                    无
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            <div class="sand-tool-item">
+              <el-dropdown trigger="click" @command="(e)=>toolClickOptions('changeTargetMarker',toolStatus.edgeDisabled,e)"
+                placement="bottom">
+                <div :class="toolStatus.edgeDisabled?'tool-disabled':''" class="dropdown-box">
+                  <el-tooltip content="结束箭头" placement="top" :open-delay="250">
+                    <div class="dropdown-content"  >
+                      <img :src="toolStatus.edgeDisabled? 
+                        require('@/assets/img/sand_new/tools/arrow-end-disabled.png'):
+                        require('@/assets/img/sand_new/tools/arrow-end.png')" 
+                        style="vertical-align: middle;"/>
+                    </div>
+                  </el-tooltip>
+                  <el-tooltip content="结束箭头" placement="top" :open-delay="250">
+                    <span v-show="toolStatus.edgeDisabled" class="disabled-item" @click.stop="()=>{}"></span>
+                  </el-tooltip>
+                </div>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item command="classic" :class="styleOptions.endArrow=='classic'?'style-acitve':''">
+                    有
+                  </el-dropdown-item>
+                  <el-dropdown-item :command="{}" :class="typeof(styleOptions.endArrow)=='string' || styleOptions.endArrow.name?'':'style-acitve'">
+                    无
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </div>
+            
+          </div>
+          <div id="sand-chart-container" ></div>
+
+          <!-- 缩略图 -->
+          <div id="minimap" class="minimap"></div>
+
+          <div class="contextMenu-wrapper" id="contextMenu-wrapper" @mouseleave="hideContextMenu">
+            <dropdown-menu size="medium">
+              <el-dropdown-item v-for="menu in contextMenuOption.filter(it => it.show)" :key="menu.key" @click.native="handleContext(menu.key)">
+                <i :class="menu.icon" v-if="menu.icon" /> 
+                {{menu.label}}
+              </el-dropdown-item>
+            </dropdown-menu>
+				  </div>
+          <el-popover
+            placement="top"
+            trigger="manual"
+            v-model="popoverVisible">
+            <div id="link-popover" :style="{height:popoverFlod?'20px':'unset'}">
+              <div class="link-box">
+                <div v-for="item in checkedLinkList" :key="item.RId" class="link-item" @click="navigateTo(item)">
+                  {{ item.Name }}
+                </div>
+              </div>
+              <img src="~@/assets/img/sand_new/arrow_black_down.png" class="link-fold" 
+              :style="{transform:popoverFlod?'':'rotate(180deg)'}" v-show="checkedLinkList.length>1"
+              @click="foldLink"/>
+            </div>
+            <div id="link-reference" slot="reference"></div>
+          </el-popover>
+        </div>
+      <!-- </div> -->
+    </div>
+    <!-- 添加链接 -->
+    <el-dialog :modal-append-to-body='false' title="添加链接" :visible.sync="addLinkShow" 
+    :close-on-click-modal="false" width="872px" top="5vh">
+      <div class="add-link-box">
+        <div class="link-box-option">
+          <el-select v-model="addLinkSearchParams.linkType" placeholder="链接类型" style="width: 240px;" @change="changeLinkType">
+            <el-option :label="item.label" :value="item.value" v-for="item in linkTypeList" :key="item.value"></el-option>
+          </el-select>
+          <el-select v-if="addLinkSearchParams.linkType==1"
+            v-model="search_dataBaseId"
+            v-loadMore="dataBaseSearchLoad"
+            ref="searchRef"
+            :filterable="!search_dataBaseId"
+            remote
+            clearable
+            placeholder="指标ID/指标名称"
+            style="width: 240px"
+            :remote-method="dataBaseSearch"
+            @click.native="dataBaseInputFocus"
+          >
+            <i slot="prefix" class="el-input__icon el-icon-search"></i>
+            <el-option
+              v-for="item in dataBaseOptions"
+              :key="item.EdbInfoId"
+              :label="item.EdbName"
+              :value="item.EdbInfoId"
+            >
+            </el-option>
+          </el-select>
+          <el-select v-else-if="addLinkSearchParams.linkType==2"
+            v-model="search_dataBaseId"
+            v-loadMore="dataBaseSearchLoad"
+            ref="searchRef"
+            :filterable="!search_dataBaseId"
+            remote
+            clearable
+            placeholder="图表名称"
+            style="width: 240px"
+            :remote-method="dataBaseSearch"
+            @click.native="dataBaseInputFocus"
+          >
+            <i slot="prefix" class="el-input__icon el-icon-search"></i>
+            <el-option
+              v-for="item in dataBaseOptions"
+              :key="item.ChartInfoId"
+              :label="item.ChartName"
+              :value="item.ChartInfoId"
+            >
+            </el-option>
+          </el-select>
+          <el-input v-else v-model="reportKeyWord" @input="searchReport"
+          placeholder="标题 / 创建人" style="width: 240px;" clearable >
+            <i slot="prefix" class="el-input__icon el-icon-search"></i>
+          </el-input>
+        </div>
+        <div class="link-box-content">
+          <!-- 指标 -->
+          <div class="link-content-dataIndex" v-if="addLinkSearchParams.linkType==1 && databaseTableData && databaseTableData.length>0">
+            <el-table :data="databaseTableData" border style="box-shadow: rgba(155, 170, 219, 0.2) 0px 3px 6px;">
+              <el-table-column label="指标Id" align="center">
+                <template slot-scope="scope">{{ scope.row.EdbCode }}</template>
+              </el-table-column>
+              <el-table-column label="指标名称" align="center" width="200">
+                <template slot-scope="scope">{{ scope.row.EdbName }}</template>
+              </el-table-column>
+              <el-table-column label="频度" align="center" width="50">
+                <template slot-scope="scope">{{ scope.row.Frequency }}</template>
+              </el-table-column>
+              <el-table-column label="单位" align="center">
+                <template slot-scope="scope">{{ scope.row.Unit }}</template>
+              </el-table-column>
+              <el-table-column label="起始时间" align="center" width="100">
+                <template slot-scope="scope">{{ scope.row.StartDate }}</template>
+              </el-table-column>
+              <el-table-column label="更新时间" align="center" width="160">
+                <template slot-scope="scope">{{ scope.row.ModifyTime }}</template>
+              </el-table-column>
+              <el-table-column label="来源" align="center">
+                <template slot-scope="scope">{{ scope.row.SourceName }}</template>
+              </el-table-column>
+              <el-table-column label="操作" align="center" width="50">
+                <template slot-scope="scope">
+                  <span class="delete-button">删除</span>
+                </template>
+              </el-table-column>
+            </el-table>
+            <ul 
+              class="value-ul" 
+              ref="valueUl" 
+              @scroll="databaseScrollHandle" 
+              v-show="databaseList.length">
+              <li
+                class="value-item"
+                v-for="item in databaseList"
+                :key="item.EdbDataId"
+              >
+                <span class="value-label">
+                  <span style="position: relative;">
+                    <i class="new-tag" v-if="databaseTableData[0].LatestDate===item.DataTime"></i>
+                    {{item.DataTime}}
+                  </span>
+                </span>
+                <span :class="['value-label',{'predict-act': databaseTableData[0].DataInsertConfig.Date===item.DataTime}]" style="min-width:200px;text-align:center;">
+                  <span :class="['value-style',{'predict-act': databaseTableData[0].DataInsertConfig.Date===item.DataTime}]">{{item.Value}}</span>
+                </span>
+              </li>
+              <li class="nodata value-item" v-if="!databaseList.length">暂无数据</li>
+            </ul>
+          </div>
+          <div class="link-content-chartIndex" v-else-if="addLinkSearchParams.linkType==2 && this.chartInfo && this.chartInfo.ChartInfoId">
+            <div class="chart-name">{{ this.chartInfo.ChartName }}</div>
+            <Chart :options="options" ref="chartRef" />
+          </div>
+          <div class="link-content-dataIndex" v-else-if="addLinkSearchParams.linkType==3 && this.reportList.length>0">
+            <el-table :data="this.reportList" border style="margin-bottom: 10px;" ref="reportTable"
+            @select="reportSelect" @select-all="reportSelect"> 
+              <el-table-column type="selection" width="40" align="center"></el-table-column>
+              <el-table-column label="报告标题" align="center" show-overflow-tooltip>
+                <template slot-scope="scope">
+                  <span >{{ scope.row.Title }}</span>
+                  <span  v-if="scope.row.MsgSendTime">
+                    ({{ scope.row.MsgSendTime.substring(5, 7)}}{{ scope.row.MsgSendTime.substring(8, 10) }})
+                  </span>
+                  <span v-else-if="scope.row.PublishTime">
+                    ({{ scope.row.PublishTime.substring(5, 7)}}{{ scope.row.PublishTime.substring(8, 10) }})
+                  </span>
+                  <span v-else-if="scope.row.CreateTime">
+                    ({{ scope.row.CreateTime.substring(5, 7)}}{{ scope.row.CreateTime.substring(8, 10) }})
+                  </span>
+                </template>
+              </el-table-column >
+              <el-table-column label="发布时间" align="center">
+                <template slot-scope="scope">
+                  <span>{{scope.row.PrePublishTime?scope.row.PrePublishTime:scope.row.PublishTime}}</span>
+                </template>
+              </el-table-column>
+            </el-table>
+            <m-page :page_no="reportParams.CurrentIndex" :pageSize="5" :total="reportTotal" @handleCurrentChange="pageChange"/>
+          </div>
+          <tableNoData text="暂无数据" v-else/>
+        </div>
+        <div class="link-box-tags">
+          <div class="link-box-tag" v-for="(item,index) in checkedLinkList" :key="item.RId">
+            <span @dblclick.stop="editLinkName(item)" v-if="!item.editing" @click="linkClick(item)">{{ item.Name }}</span>
+            <el-input v-else @blur="editLinkNameFinish(item)" 
+              v-model.trim="editingLabel" class="label-edit-input" ref="labelEditInput"/>
+            <img src="~@/assets/img/sand_new/delete_outline_1.png" @click="linkDelete(item,index)">
+          </div>
+        </div>
+        <div class="link-box-buttons">
+          <el-button type="info" style="width:120px;color:#333333;background-color:#F4F8FE" @click="addLinkShow=false">取消</el-button>
+          <el-button type="primary" style="width:120px;margin-left: 30px;" @click="saveLink">确定</el-button>
+        </div>
+      </div>
+		</el-dialog>
+  </div>
+</template>
+
+<script>
+// 路由的name 属性在main.js中更改
+// import '@antv/x6-vue-shape'
+import { myGraph } from '../common/gragh';
+import { myNodes,myNodeOption } from '../common/node';
+import { myEdgeOption } from '../common/edge';
+import { Addon } from '@antv/x6'
+import mindmap from "../common/mindmap"
+import {styleSettings,familyOptions,fontSizeOptions,colorsOptions,lineHeightOptions} from "../common/toolConfig"
+import { ElDropdownMenu as DropdownMenu } from 'element-ui';
+import { contextMenuOption } from '../common/options';
+import { contextEvent } from '../common/events';
+import { mapState } from 'vuex'
+import { dataBaseInterface,sandInterface,reportlist} from '@/api/api.js';
+import * as sheetInterface from "@/api/modules/sheetApi.js";
+import tree from "vue-giant-tree";
+import { chartSetMixin } from '../../dataEntry_manage/mixins/chartPublic'
+import Chart from '../../dataEntry_manage/components/chart.vue'
+import mPage from "@/components/mPage.vue";
+import { svgToBase64 } from '@/utils/svgToblob'
+
+  export default {
+    name:"sandFlowIndex",
+    components:{
+      DropdownMenu,tree,Chart,mPage
+    },
+    data() {
+      return {
+        contextMenuOption,
+        graph: null,
+        dnd:null,
+        initData: {},
+        activeToolTabName:"元素库",
+        addType:"",
+        styleActive:1,
+        canUndo:false,
+        canRedo:false,
+        mindmapDataUse:[],
+        mindmapAssistData:{
+          mindmapDataRecoverUse:[],//用于恢复撤销和重做时被删除的思维导图数据
+          // mindmapDataRecoverPtr:-1, //当前索引
+        },
+        sandSaveParams:{
+          Name:'',
+          SandboxClassifyId:'',
+          SandboxId:+this.$route.query.SandboxId || 0,
+        },
+        selectClassifyShow:false,
+        zTreeObj:{},
+        selectSetting:{
+          data:{
+            key:{
+              name:"SandboxClassifyName",
+              children:"Children"
+            }
+          },
+          view:{
+            showLine:false,
+            showIcon:false,
+            selectedMulti:false
+          }
+        },
+        treeData:[],
+        lockLoding: null,
+        loopTimer:null,
+        // 添加链接弹窗
+        addLinkShow:false,
+        addLinkSearchParams:{
+          linkType:1
+        },
+        linkTypeList:[
+          {value:1,label:"ETA指标/预测指标"},
+          {value:2,label:"ETA图库"},
+          {value:3,label:"ETA研报"}
+        ],
+        //添加链接:指标
+        search_dataBaseId:'',
+        dataBaseParams:{
+          pages:1,
+          searchText:'',
+          search_have_more:false
+        },
+        dataBaseOptions:[],
+
+        databaseTableData:[],
+        databaseList:[],
+        databaseHaveMore:false,
+        databasePageNo:1,
+        chartInfo:{},
+        edbData:[],
+        options:{},
+        reportKeyWord:'',
+        reportParams:{
+          CurrentIndex:1,
+          PageSize:5,
+        },
+        reportList:[],
+        selections:[],
+        checkedLinkList:[],
+        reportTotal:0,
+        editingLabel:'',
+        activeItemRId:'',
+        popoverVisible:false,
+        popoverDom:null,
+        popoverTriggerDom:null,
+        popoverTimeout:null,
+        linkNode:null,
+        popoverFlod:true,
+        isSlideLeft:false
+      }
+    },
+    mixins:[mindmap,chartSetMixin],
+    watch: {
+      initData(newval) {
+        // console.log(newval)
+        this.init()
+        this.$nextTick(()=>{
+          this.graph.fromJSON(newval);
+          this.graph.zoomToFit()
+        })
+      },
+      /* 选中搜索指标 展开目录 选中指标 展示数据 */
+      search_dataBaseId(newval) {
+        if (newval) {
+          if(this.addLinkSearchParams.linkType==1){
+            let search_obj = this.dataBaseOptions.find(
+              (item) => item.EdbInfoId === newval
+            );
+            // console.log(search_obj,'search_obj');
+            if(search_obj){
+              this.checkedLinkList.push({
+                RId:this.addLinkSearchParams.linkType+'-'+search_obj.EdbInfoId,
+                Id:search_obj.EdbInfoId,
+                Name:search_obj.EdbName,
+                Type:this.addLinkSearchParams.linkType,
+                editing:false,
+                databaseType:search_obj.EdbInfoType, //0 普通指标 | 1 预测指标
+                detailParams:{
+                  code:search_obj.UniqueCode,
+                  id:search_obj.EdbInfoId,
+                  classifyId:search_obj.ClassifyId
+                }
+              })
+              this.activeItemRId=this.addLinkSearchParams.linkType+'-'+search_obj.EdbInfoId
+              this.initGetData()
+            }
+          }else{
+            let search_obj = this.dataBaseOptions.find(
+              (item) => item.ChartInfoId === newval
+            );
+            if(search_obj){
+              this.checkedLinkList.push({
+                RId:this.addLinkSearchParams.linkType+'-'+search_obj.ChartInfoId,
+                Id:search_obj.ChartInfoId,
+                Name:search_obj.ChartName,
+                Type:this.addLinkSearchParams.linkType,
+                editing:false,
+                detailParams:{
+                  code:search_obj.UniqueCode,
+                  id:search_obj.ChartInfoId
+                }
+              })
+              this.activeItemRId=this.addLinkSearchParams.linkType+'-'+search_obj.ChartInfoId
+              this.getChartDetail(search_obj.ChartInfoId)
+            }
+          }
+        }
+      },
+      edbData: {
+        handler(newval, oldval) {
+          newval.length && !this.chartInfo.WarnMsg && this.setChartOptionHandle(newval);
+        },
+        deep: true,
+    },
+	  },
+    computed:{
+      myNodes(){
+        return myNodes
+      },
+      myEdgeOption(){
+        return myEdgeOption
+      },
+      familyOptions(){
+        return familyOptions
+      },
+      fontSizeOptions(){
+        return fontSizeOptions
+      },
+      colorsOptions(){
+        return colorsOptions
+      },
+      lineHeightOptions(){
+        return lineHeightOptions
+      },
+      ...mapState({
+        selectCells: state => state.sand.selectCells,
+        styleOptions: state => state.sand.styleOptions,
+        toolStatus:state => state.sand.toolStatus,
+      }),
+      nodeTextDisable(){
+        return this.toolStatus.nodeDisabled && this.toolStatus.textDisabled
+      },
+      nodeEdgeDisable(){
+        return this.toolStatus.nodeDisabled && this.toolStatus.edgeDisabled
+      }
+    },
+    created(){
+      this.getSandboxClassify();
+      this.$route.query.SandboxId && this.getGraphData(this.$route.query.SandboxId)
+    },
+    mounted(){
+      // this.init()
+      document.getElementById('sand-mainBody-chart').addEventListener("dragover",this.edgeDragover)
+      document.getElementById('sand-mainBody-chart').addEventListener("drop",this.edgeDrop)
+      this.popoverDom = $('#link-popover')[0];
+      this.popoverTriggerDom = $('#link-reference')[0];
+      
+      this.popoverDom.addEventListener('mouseenter',this.clearPopoverTimeout)
+      this.popoverDom.addEventListener('mouseleave',this.closePopover)
+      
+      // Graph.registerNode("link-popover", {
+      //   inherit: "vue-shape",
+      //   component: popover,
+      // });
+    },
+    beforeDestroy(){
+      document.getElementById('sand-mainBody-chart').removeEventListener("dragover",this.edgeDragover)
+      document.getElementById('sand-mainBody-chart').removeEventListener("drop",this.edgeDrop)
+      this.popoverDom.removeEventListener('mouseenter',this.clearPopoverTimeout)
+      this.popoverDom.removeEventListener('mouseleave',this.closePopover)
+    },
+    methods: {
+      getSandboxClassify(){
+        sandInterface.getSandboxClassifyOnly().then(res=>{
+          if (res.Ret === 200) {
+            this.treeData=res.Data.AllNodes || []
+          }
+        })
+      },
+      getSelectZTree(zTree){
+        this.selectZTreeObj=zTree
+      },
+      selectClassify(event,treeId,treeNode,clickFlag){
+        this.sandSaveParams.SandboxClassifyId = treeNode.SandboxClassifyId
+        this.selectClassifyShow = false
+      },
+      getGraphData(Id){
+        let SandboxId=+Id ? +Id : this.sandSaveParams.SandboxId
+        sandInterface.getSandboxDetail({SandboxId}).then(res=>{
+          if(res.Ret == 200 && res.Data){
+            this.sandSaveParams.SandboxId=res.Data.SandboxId
+            this.sandSaveParams.Name=res.Data.Name
+            this.sandSaveParams.SandboxClassifyId=res.Data.SandboxClassifyId
+            this.initData = JSON.parse(res.Data.Content)
+            this.mindmapDataUse = res.Data.MindmapData?JSON.parse(res.Data.MindmapData):[]
+            this.autoSave();
+          }
+        })
+      },
+      /* 编辑页 自动保存 */
+      autoSave() {
+        // return 
+        this.loopTimer = setInterval(() => {
+          if(!this.sandSaveParams.Name || !this.sandSaveParams.SandboxClassifyId) return;
+          const { Name, SandboxClassifyId } = this.sandSaveParams;
+          sandInterface.sandboxSaveV2({
+            SandboxId:this.sandSaveParams.SandboxId,
+            Name,
+            SandboxClassifyId,
+            Content: JSON.stringify(this.graph.toJSON()),
+            PicUrl:'',
+            MindmapData:this.mindmapDataUse.length>0?JSON.stringify(this.mindmapDataUse):''
+          }).then((res) => {
+            if(res.Ret !== 200) return
+          });
+        }, 10000);
+      },
+      // 初始化画布
+      init() {
+        const graph = new myGraph('sand-chart-container',this.getMindmapDataUse,this.mindmapAssistData);
+        this.graph = graph;
+        
+        this.graph.history.on('change', (args) => { 
+          // console.log(args,'change-history');
+          this.canUndo = this.graph.canUndo()
+          this.canRedo = this.graph.canRedo()
+          // console.log(this.canUndo,this.canRedo);
+        })
+        this.graph.history.on('undo', (args) => { 
+          // console.log(args,'undo-history');
+          // let ids=[]
+          let mindmapNodes=args.cmds.filter(it => it.data.props && it.data.props.shape.indexOf('mindmap')!=-1 && it.data.node)
+          if(!(mindmapNodes && mindmapNodes.length>0)) return 
+          console.log(mindmapNodes,'mindmapNodes');
+          let mindmapUndoType=mindmapNodes[0].event
+          if(mindmapUndoType=="cell:added"){
+            this.mindmapRecoverRemove(mindmapNodes)
+          }else if(mindmapUndoType=="cell:removed"){
+            this.mindmapRecoverAdd()
+          }
+          // console.log(this.mindmapDataUse);
+        })
+        this.graph.history.on('redo', (args) => { 
+          // console.log(args,'redo-history');
+          // let ids=[]
+          let mindmapNodes=args.cmds.filter(it => it.data.props && it.data.props.shape.indexOf('mindmap')!=-1 && it.data.node)
+          if(!(mindmapNodes && mindmapNodes.length>0)) return 
+          // console.log(mindmapNodes,'mindmapNodes');
+          let mindmapUndoType=mindmapNodes[0].event
+          if(mindmapUndoType=="cell:added"){
+            this.mindmapRecoverAdd()
+          }else if(mindmapUndoType=="cell:removed"){
+            this.mindmapRecoverRemove(mindmapNodes)
+          }
+          // console.log(this.mindmapDataUse,'mindmapDataUse');
+        })
+        this.graph.on('node:mouseenter', ({ node, e }) => {
+          // console.log(node);
+          let data = node.data
+          this.linkNode = node
+          let isMindmap = node.shape.indexOf('mindmap')!=-1
+          this.contextMenuOption.map(item =>{
+            if(item.key=='copy'){
+              item.show=!isMindmap
+            }
+          })
+          if(data && data.linkData && data.linkData.length>0){
+            this.popoverFlod = data.linkFold
+            this.popoverVisible=false
+            clearTimeout(this.popoverTimeout)
+            this.popoverTimeout=null
+            let currentLinks = data.linkData
+            //指标id
+            let edbInfoIdList = data.linkData.filter(it => it.Type==1).map(it => it.Id)
+            // 图库id
+            let chartInfoIdList = data.linkData.filter(it => it.Type==2).map(it => it.Id)
+            // 报告id
+            let reportIdList = data.linkData.filter(it => it.Type==3).map(it => it.Id)
+            sandInterface.sandboxLinkCheck({EdbInfoIdList:edbInfoIdList,ChartInfoIdList:chartInfoIdList,ReportIdList:reportIdList}).then(res=>{
+              if(res.Ret == 200){
+                let EdbInfoIdList = res.Data.EdbInfoIdList || []
+                let ChartInfoIdList = res.Data.ChartInfoIdList || []
+                let ReportIdList = res.Data.ReportIdList || []
+                this.checkedLinkList = currentLinks.filter(link =>{
+                  return EdbInfoIdList.includes(link.Id) || ChartInfoIdList.includes(link.Id) || ReportIdList.includes(link.Id)
+                })
+                let clinetPositon=this.graph.localToClient(node.position())
+                let size=node.size()
+                // console.log(clinetPositon.y,'clinetPositon');
+                // console.log(this.popoverTriggerDom,'domdomdom');
+                this.popoverTriggerDom.style.left = clinetPositon.x+size.width/2 + 'px';
+                this.popoverTriggerDom.style.top = clinetPositon.y + 'px';
+                this.popoverVisible=true
+                if(!(this.checkedLinkList.length>0)){
+                  if(item.key=='addLink'){
+                    item.label='添加链接'
+                  }else if(item.key=='deleteLink'){
+                    item.show=false
+                  }
+                }
+              }
+              this.contextMenuOption.map(item =>{
+                if(item.key=='addLink'){
+                  item.label='编辑链接'
+                }else if(item.key=='deleteLink'){
+                  item.show=true
+                }
+              })
+            })
+          }else{
+            this.contextMenuOption.map(item =>{
+              if(item.key=='addLink'){
+                item.label='添加链接'
+              }else if(item.key=='deleteLink'){
+                item.show=false
+              }
+            })
+            // console.log(this.contextMenuOption,'contextMenuOption');
+          }
+        })
+      
+        this.graph.on('node:mouseleave', ({ node, e }) => {
+          if(!this.popoverTimeout){
+            this.popoverTimeout= setTimeout(()=>{
+              this.popoverVisible=false
+              this.popoverTriggerDom.style.left = '-99999px';
+              this.popoverTriggerDom.style.top = '-99999px';
+              this.popoverTimeout=null
+            },500)
+          }
+
+        })
+
+        this.graph.on('node:change:position', (args) => { 
+          if(args.node.data && args.node.data.linkData && args.node.data.linkData.length>0
+          &&this.popoverVisible){
+            // console.log(args.node.data.linkData);
+            this.changePopoverPositon(args)
+
+          }
+        })
+
+        this.dnd = new Addon.Dnd({
+          target: this.graph,
+          animation: true,
+          validateNode() {
+            return true;
+          },
+        });
+        this.setGraph()
+		  },
+      getMindmapDataUse(){
+        return this.mindmapDataUse
+      },
+      dragStart(data,e) {
+        // console.log(data,e,"触发了")
+        const { key,shape } = data;
+        
+        const	node = this.graph.createNode({
+          shape,
+          ...myNodeOption(key),
+        });
+        this.dnd.start(node,e);
+      },
+      edgeDragStart(type,e){
+        this.addType = type
+      },
+      edgeDragover(e){
+        e.preventDefault()
+      },
+      edgeDrop(e){
+        if(!this.addType){
+          return 
+        }
+        // console.log(this.addType,e);
+        // console.log(this.graph);
+        let position = this.graph.clientToLocal({x:e.clientX,y:e.clientY})
+        if(this.addType.indexOf("Mindmap")!==-1){
+          //插入思维导图
+          // console.log("插入思维导图");
+          this.generateMindmapData(position,this.addType)
+          this.mindMapRender(this.mindmapDataUse.length-1)
+        }else{
+          this.graph.addEdge({
+            shape:'edge',
+            ...this.myEdgeOption(this.addType,position.x,position.y)
+          });
+        }
+      },
+      generateMindmapData(position,addType){
+        let beId=this.mindmapDataUse.length>0?
+        parseInt(this.mindmapDataUse[this.mindmapDataUse.length-1].mindmapData.id)+1+'':'1'
+        let mindmapData={
+          id: beId,
+          type: 'topic',
+          label: '中心主题',
+          width: 160,
+          height: 50,
+          direction:'double',
+          children: [
+            {
+              id: beId+'-1',
+              type: 'topic-branch',
+              label: '分支主题1',
+              width: 100,
+              height: 40,
+              direction:'left',
+              children: [
+                {
+                  id: beId+'-1-1',
+                  type: 'topic-child',
+                  label: '子主题1',
+                  width: 60,
+                  height: 30,
+                  direction:'left',
+                },
+                {
+                  id: beId+'-1-2',
+                  type: 'topic-child',
+                  label: '子主题2',
+                  width: 60,
+                  height: 30,
+                  direction:'left',
+                },
+              ],
+            },
+            {
+              id: beId+'-2',
+              type: 'topic-branch',
+              label: '分支主题2',
+              width: 100,
+              height: 40,
+              direction:'right',
+            },
+          ],
+        }
+        this.mindmapDataUse.push({mindmapData,position,addType})
+      },
+      changeStyle(activeNum){
+        this.$store.commit("sand/SET_CELL_STYLE",activeNum)
+        let styleData=styleSettings[activeNum-1]
+
+        let cells = this.graph.getCells()
+        // console.log(cells);
+
+        for (let i = 0; i < cells.length; i++) {
+          const element = cells[i];
+          // console.log(element);
+          if(element.shape.indexOf("edge") !=-1){
+            element.setAttrs({
+              line:{
+                stroke:styleData.lineColor
+              }
+            })
+          }if(element.data && element.data.key == 'text'){
+            element.setAttrs({
+              text:{
+                fill:styleData.textColor
+              }
+            })
+          }else{
+            element.setAttrs({
+              body:{
+                fill:styleData.backgroundColor,
+                stroke:styleData.borderColor,
+              },
+              text:{
+                fill:styleData.color
+              }
+            })
+          }
+        }
+        this.styleActive = activeNum
+      },
+      /* 右键事件 */
+      handleContext(key) {
+        if(key=="addLink"){
+          this.addLinkDialogOpen()
+        }else if(key=="deleteLink"){
+          this.deleteLink()
+        }else{
+          contextEvent(this.graph, key,this.mindmapDataUse,this.mindmapAssistData);
+        }
+        this.hideContextMenu();
+      },
+
+      /* 隐藏右键menu */
+      hideContextMenu() {
+        const dom = $('#contextMenu-wrapper')[0];
+        dom.style.left = '-9999px';
+        dom.style.top = '-9999px';
+      },
+      addLinkDialogOpen(){
+        const select_cell = this.graph.getSelectedCells()[0]
+        // console.log(select_cell);
+        if(select_cell){
+          this.checkedLinkList = select_cell.data ? select_cell.data.linkData ||[]:[]
+        }else{
+          this.checkedLinkList=[]
+        }
+        // console.log(this.checkedLinkList);
+        this.addLinkSearchParams.linkType=1
+        this.changeLinkType()
+        this.addLinkShow=true
+      },
+      deleteLink(){
+        const select_cell = this.graph.getSelectedCells()[0]
+        if(select_cell){
+          select_cell.data.linkData=[]
+          this.$message.success('清除链接成功')
+        }
+      },
+      saveLink(){
+        // console.log("保存链接",select_cell);
+        const select_cell = this.graph.getSelectedCells()[0]
+        if(select_cell.data){
+          select_cell.data.linkData = this.checkedLinkList
+          select_cell.data.linkFold = true
+        }else{
+          select_cell.setData({linkData:this.checkedLinkList})
+          if(!select_cell.data.linkFold){
+            select_cell.data.linkFold=true
+          }
+        }
+        this.$message.success("链接保存成功")
+        this.addLinkShow=false
+      },
+      // backList(){
+      //   // this.sandSaveParams.SandboxId this.sandSaveParams.Name
+      //   this.$router.push({path:"/sandlist",query:{SandboxName:'阿巴阿巴A1',SandboxId:142}})
+      // },
+      copySandHandle: _.debounce(function() {
+        const { cells } = this.graph.toJSON();
+        if(!cells.length) return this.$message.warning('当前画布无可复制内容');
+
+        this.lockLoding = this.$loading({
+          lock: true,
+          text: '复制图片中...',
+          target: '.right-wrapper',
+          spinner: 'el-icon-loading',
+          background: 'rgba(255, 255, 255, 0.8)'
+        });
+        this.graph.toSVG(async(dataUri) => {
+          const canvas = document.createElement("canvas");
+          const ctx = canvas.getContext("2d");
+          const img = new Image();
+          img.crossOrigin = "Anonymous";
+          img.src = svgToBase64(dataUri);
+          img.onload = ()=>{
+            canvas.width = img.width;
+            canvas.height = img.height;
+            // console.log('width',img.width)
+            // console.log('height',img.height)
+            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+            ctx.fillStyle="#fff";
+            ctx.fillRect(0, 0, img.width, img.height);
+            ctx.drawImage(img, 0, 0);
+            if(window.ClipboardItem) {
+              canvas.toBlob(async (blob) => {
+                  const data = [new ClipboardItem({ [blob.type]: blob })]; 
+                  await navigator.clipboard.write(data).then(
+                  () => {
+                      this.$message.success('复制成功!')
+                  },
+                  () => {
+                      this.$message.warning('浏览器不支持')
+                  }
+                  ).finally(()=>{
+                      this.lockLoding && this.lockLoding.close();
+                  });
+              });
+            }else {
+              this.lockLoding && this.lockLoding.close();
+              this.$message.warning('当前协议暂不支持,仅支持https协议')
+            }	
+          }
+        },{
+          preserveDimensions:true,//让svg为实际图片大小
+          beforeSerialize:(svg)=>{
+            const {x,y,width,height} = this.graph.getContentBBox(cells)
+            let {tx,ty} = this.graph.translate()
+            //给导出的svg增加一点宽高
+            svg.setAttribute('width',width+50)
+            svg.setAttribute('height',height+50) 
+            //设置viewBox使图像居中
+            svg.setAttribute('viewBox',`${x-25} ${y-25} ${width+50} ${height+50}`)
+            let gNode = svg.getElementsByClassName('x6-graph-svg-viewport')[0]
+            // 去掉不该截图的添加图标
+            let leftImg = svg.getElementsByClassName('left-topic-image')
+            for (let i = 0; i < leftImg.length; i++) {
+              const element = leftImg[i];
+              element.parentElement.removeChild(element)
+              i--
+            }
+            let rightImg = svg.getElementsByClassName('right-topic-image')
+            for (let i = 0; i < rightImg.length; i++) {
+              const element = rightImg[i];
+              element.parentElement.removeChild(element)
+              i--
+            }
+            // console.log(leftImg,rightImg,'rightImg');
+
+            let textNode = document.createElement('text')
+            textNode.setAttribute('x',x-tx+width-90)
+            textNode.setAttribute('y',y-ty+height+22)
+            textNode.setAttribute('font-size','16px')
+            textNode.setAttribute('font-style','italic')
+            textNode.innerText = '来源:弘则研究'
+            gNode.appendChild(textNode)
+              },
+          copyStyles:false,
+          stylesheet: `
+              svg{
+                  background-color:white;
+              }
+            .x6-port {
+                visibility: hidden;
+            }` 
+            
+        })
+      },500),
+      saveChart: _.debounce( function(callback=null) {
+        if(!this.sandSaveParams.Name) 
+          return this.$message.warning('请填写逻辑图名称');
+        if(!this.sandSaveParams.SandboxClassifyId) 
+          return this.$message.warning('请选择所属分类');
+
+        if(!this.graph.toJSON().cells.length) return this.$message.warning('请绘制画布内容');
+
+        const { Name, SandboxClassifyId,SandboxId} = this.sandSaveParams;
+
+        this.lockLoding = this.$loading({
+          lock: true,
+          text: '保存中...',
+          target: '.sand-edit-container',
+          spinner: 'el-icon-loading',
+          background: 'rgba(255, 255, 255, 0.8)'
+        });
+        // return
+        const { cells } = this.graph.toJSON();
+        this.graph.toSVG(async (dataUri) => {
+          const params = new FormData();
+          params.append('Img',dataUri)
+          const { Data } = await dataBaseInterface.uploadImgSvg(params);
+          // if(!Data){
+          //   this.lockLoding.close();
+          //   return;
+          // }
+          let SandboxId = this.sandSaveParams.SandboxId
+          // console.log({
+          //   SandboxId,
+          //   Name,
+          //   SandboxClassifyId: Number(SandboxClassifyId),
+          //   Content: JSON.stringify(this.graph.toJSON()),
+          //   PicUrl: Data?Data.ResourceUrl:'',
+          //   SvgData: dataUri,
+          //   MindmapData:JSON.stringify(this.mindmapDataUse)
+          // });
+          // return 
+          const { Ret , Data : sandData} = await sandInterface.sandboxSaveV2({
+            SandboxId,
+            Name,
+            SandboxClassifyId: Number(SandboxClassifyId),
+            Content: JSON.stringify(this.graph.toJSON()),
+            PicUrl: Data?Data.ResourceUrl:'',
+            SvgData: dataUri,
+            MindmapData:JSON.stringify(this.mindmapDataUse)
+          })
+
+          if(Ret !== 200){
+            this.lockLoding.close();
+            return;
+          }
+          this.$message.success(`${SandboxId ? '编辑成功' : '保存成功'}`);
+          this.lockLoding.close();
+          //如果是新增,直接跳转到编辑页面
+          if(!SandboxId){
+            this.$router.replace({
+              path: '/sandflow',
+              query: {
+                SandboxId:sandData.SandboxId,
+              },
+            });
+          }
+          
+          callback && callback();
+        },{
+          preserveDimensions:true,//让svg为实际图片大小
+          beforeSerialize:(svg)=>{
+            const {x,y,width,height} = this.graph.getContentBBox(cells)
+            let {tx,ty} = this.graph.translate() // 画布偏移量
+            //给导出的svg增加一点宽高
+            svg.setAttribute('width',width+50)
+            svg.setAttribute('height',height+50) 
+            //设置viewBox使图像居中
+            svg.setAttribute('viewBox',`${x-25} ${y-25} ${width+50} ${height+50}`)
+            // 在图表右下方 加上"来源:弘则研究"字样
+            let gNode = svg.getElementsByClassName('x6-graph-svg-viewport')[0]
+            // 去掉不该截图的添加图标
+            let leftImg = svg.getElementsByClassName('left-topic-image')
+            for (let i = 0; i < leftImg.length; i++) {
+              const element = leftImg[i];
+              element.parentElement.removeChild(element)
+              i--
+            }
+            let rightImg = svg.getElementsByClassName('right-topic-image')
+            for (let i = 0; i < rightImg.length; i++) {
+              const element = rightImg[i];
+              element.parentElement.removeChild(element)
+              i--
+            }
+            let textNode = document.createElement('text')
+            textNode.setAttribute('x',x-tx+width-90)
+            textNode.setAttribute('y',y-ty+height+22)
+            textNode.setAttribute('font-size','16px')
+            textNode.setAttribute('font-style','italic')
+            textNode.innerText = '来源:弘则研究'
+            gNode.appendChild(textNode)
+              },
+          copyStyles:false,
+          stylesheet: `
+              svg{
+                  background-color:white;
+              }
+            .x6-port {
+                visibility: hidden;
+            }
+            ` 
+        })
+          
+      },500),
+      toolClickOptions(type,disabled){
+        // 设置元素属性的时候请一起设置,分几步设置,撤销的时候就要分几步。或者使用事务来设置
+        if(disabled) return
+        let value = arguments[2]
+        switch (type) {
+          case 'undo':
+            this.graph.undo()
+            break;
+          case 'redo':
+            this.graph.redo()
+            break;
+          case 'changeFamily':
+            this.styleOptions.fontF = value
+            this.setGraphStyle('text/fontFamily',value)
+            break;
+          case 'changeSize':
+            this.styleOptions.fontS = value
+            this.setGraphStyle('text/fontSize',value)
+            break;
+          case 'changeWeight':
+            this.styleOptions.fontW = this.styleOptions.fontW=='normal'?'bold':'normal'
+            this.setGraphStyle('text/fontWeight')
+            break;
+          case 'changeFontStyle':
+            this.styleOptions.fontStyle = this.styleOptions.fontStyle=='normal'?'italic':'normal'
+            this.setGraphStyle('text/fontStyle')
+            break;
+          case 'changeDecoration':
+            this.styleOptions.textDecoration = this.styleOptions.textDecoration=='none'?'underline':'none'
+            this.setGraphStyle('text/textDecoration')
+            break;
+          case 'changeColor':
+            this.setGraphStyle('text/fill',value)
+            break;
+          case 'changeLineHeight':
+            this.styleOptions.lineHeight = value
+            this.setGraphStyle('text/lineHeight',value)
+            break;
+          case 'changeTextAlign':
+            this.styleOptions.textAlign = value
+            this.setGraphStyle('text/textAnchor',value)
+            break;
+          case 'changeBackgroundColor':
+            this.setGraphStyle('body/fill',value)
+            break;
+          case 'changeLineColor':
+            this.setGraphStyle('body/stroke',value)
+            break;
+          case 'changeLineWidth':
+            this.styleOptions.lineWidth = value
+            this.setGraphStyle('body/strokeWidth',value)
+            break;
+          case 'changeStrokeDasharray':
+            this.styleOptions.lineStyle = value
+            this.setGraphStyle('body/strokeDasharray',value)
+            break;
+          case 'changeRouter':
+            this.styleOptions.connectStyle = value
+            this.setGraphStyle('router/name',value)
+            break;
+          case 'changeSourceMarker':
+            this.styleOptions.startArrow = value
+            this.setGraphStyle('line/sourceMarker',value)
+            break;
+          case 'changeTargetMarker':
+            this.styleOptions.endArrow = value
+            this.setGraphStyle('line/targetMarker',value)
+            break;
+          default:
+            break;
+        }
+      },
+      setGraphStyle(attr,value){
+        // console.log(attr,value);
+        let styleValue = value
+        let attrReal = attr
+        let refXValue;
+        let currentAttr= this.selectCells[0].getAttrs()
+        if(attr.indexOf('fontWeight')!=-1){
+          styleValue = currentAttr.text.fontWeight == "bold"?'normal':'bold'
+        }else if(attr.indexOf('fontStyle')!=-1){
+          styleValue = currentAttr.text.fontStyle == "italic"?'normal':'italic'
+        }else if(attr.indexOf('textDecoration')!=-1){
+          styleValue = currentAttr.text.textDecoration == "underline"?'none':'underline'
+        }else if(attr.indexOf('textAnchor')!=-1){
+          //需要特殊设置
+          if(styleValue=='start'){
+            refXValue='0%'
+          }else if(styleValue=='middle'){
+            refXValue=0.5
+          }else{
+            refXValue='100%'
+          }
+        }
+        // console.log(this.selectCells[0].attr(attr),styleValue,'styleValue');
+
+        if(this.selectCells[0].shape.indexOf('edge')!=-1){
+          // 边的情况
+          attrReal=attrReal.replace('body','line')
+        }
+        // console.log(attrReal,'attrReal');
+        if(this.selectCells[0].attr(attrReal) === styleValue){
+          return 
+        }
+        this.selectCells.map(cell=>{
+          // lineHeight和fontsize 是一个倍数关系,每一个都可能不一样。
+          if(attr.indexOf('lineHeight')!=-1){
+            let fontSizeCurrent = cell.getAttrs().text.fontSize
+            cell.setAttrs({
+              text:{
+                relativeLineHeight:styleValue, //设置相对行高
+                lineHeight:styleValue*fontSizeCurrent //设置行高
+              }
+            })
+          }else if(attr.indexOf('fontSize')!=-1){
+            let relativeLineHeightCurrent = cell.getAttrs().text.relativeLineHeight || 1.3
+            cell.setAttrs({
+              text:{
+                fontSize:styleValue,//设置字号
+                lineHeight:styleValue*relativeLineHeightCurrent //设置行高
+              }
+            })
+          }else if(attr=='body/stroke'){
+            // 线条颜色和边框颜色
+            cell.attr(attrReal,styleValue)
+          }else if(attr=='body/strokeWidth'){
+            // 线条粗细和边框粗细
+            cell.attr(attrReal,styleValue)
+          }else if(attr=='body/strokeDasharray'){
+            // 线条样式和边框样式
+            cell.attr(attrReal,styleValue)
+          }else if(attr=='router/name'){
+            // 1-直线  2-弯折线 3-圆角弯折线
+            let routerName=styleValue==1?'normal':'manhattan'
+            let connectorName=styleValue==3?'rounded':'normal'
+            this.graph.startBatch('setConnetorStyle')
+            if(routerName=='normal'){
+              // 变成直线,清除路径点
+              cell.setVertices([])
+            }else{
+              // 变弯折线 添加路径点
+              let sourcePoint = cell.getSourcePoint() //起始点
+              let targetPoint = cell.getTargetPoint() //终止点
+              let fisrtPoint = {x:(sourcePoint.x+targetPoint.x)/2,y:sourcePoint.y}
+              let secondPoint = {x:(sourcePoint.x+targetPoint.x)/2,y:targetPoint.y}
+              cell.setVertices([fisrtPoint,secondPoint])
+            }
+            cell.setRouter({name:routerName,args: {padding: {left: 10}}})
+            cell.setConnector({name:connectorName,args: { radius: 8 }})
+            this.graph.stopBatch('setConnetorStyle')
+          }else if(attr.indexOf('textAnchor')!=-1){
+            // 居左居中居右设置
+            // cell.attr('text/refX',refXValue)
+            cell.setAttrs({
+              text:{
+                refX:refXValue,//设置相对位置
+                textAnchor:styleValue //设置对齐方式
+              }
+            })
+          }else{
+            cell.attr(attr,styleValue)
+          }
+
+        })
+      },
+      // -------------------------------添加链接
+      changeLinkType(){
+        this.search_dataBaseId=''
+        this.dataBaseOptions=[]
+        this.dataBaseParams={
+          pages:1,
+          searchText:'',
+          search_have_more:false
+        }
+
+        this.databaseTableData=[]
+        this.databaseList=[]
+        this.databaseHaveMore=false
+        this.databasePageNo=1
+        
+        this.chartInfo={}
+        this.edbData=[]
+
+        this.reportKeyWord=''
+        this.reportList=[]
+      },
+      /* 搜索 */
+      dataBaseSearch(query) {
+        this.dataBaseParams.pages = 1;
+        this.dataBaseParams.searchText = query;
+        this.dataBaseSearchApi(this.dataBaseParams.searchText)
+      },
+      // 加载更多
+      dataBaseSearchLoad() {
+        if(!this.dataBaseParams.search_have_more) return;
+        this.dataBaseSearchApi(this.dataBaseParams.searchText,++this.dataBaseParams.pages);
+      },
+      /* 聚焦获取当前检索 */
+      dataBaseInputFocus(e) {
+        this.dataBaseParams.pages = 1;
+        this.dataBaseParams.searchText = e.target.value;
+        if(this.dataBaseParams.searchText) {
+          this.dataBaseSearchApi(this.dataBaseParams.searchText);
+        }else {
+          this.searchOptions = [];
+          this.dataBaseParams.search_have_more=false
+        }
+
+      },
+      dataBaseSearchApi(query,page=1) {
+        if(this.addLinkSearchParams.linkType==1){
+          sheetInterface.searchTarget({
+            KeyWord:query,
+            CurrentIndex: page
+          }).then(res => {
+            if(res.Ret !== 200) return
+            const { List,Paging } = res.Data;
+            this.dataBaseParams.search_have_more = page < Paging.Pages;
+            this.dataBaseOptions = page === 1 ? List : this.dataBaseOptions.concat(List);
+          })
+        }else{
+          dataBaseInterface.chartSearchByEs({
+            Keyword: query,
+            IsShowMe:false,
+            CurrentIndex: page
+          })
+          .then((res) => {
+            if (res.Ret !== 200) return
+            const { List,Paging } = res.Data;
+            this.dataBaseParams.search_have_more = page < Paging.Pages;
+            this.dataBaseOptions = page === 1 ? List : [...this.dataBaseOptions,...List];
+          });
+        }
+
+      },
+      initGetData() {
+        this.databasePageNo = 1;
+        if(this.$refs.edb_detail_data){
+          this.$refs.valueUl.scrollTop=0
+        }
+        this.getDatabaseList();
+      },
+      getDatabaseList(){
+        dataBaseInterface.targetList({
+          PageSize: 20,
+          CurrentIndex: this.databasePageNo,
+          EdbInfoId: this.search_dataBaseId,
+        }).then(res => {
+          if(res.Ret === 200) {
+            if(res.Data) {
+              this.databaseTableData = [res.Data.Item] || [];
+              this.databaseHaveMore =  this.databasePageNo < res.Data.Paging.Pages ? true : false;
+              this.databaseList = this.databasePageNo === 1 ? (res.Data.Item.DataList || []) : this.databaseList.concat(res.Data.Item.DataList);
+            }else {
+              this.databaseTableData = [];
+              this.databaseList = [];  
+            }
+          }
+        })
+      },
+      databaseScrollHandle:_.throttle(function() {
+        let scrollTop = this.$refs.valueUl.scrollTop;
+        let clientHeight = this.$refs.valueUl.clientHeight;
+        let scrollHeight = this.$refs.valueUl.scrollHeight;
+        if(scrollTop===0) return
+        if(scrollTop + clientHeight >= scrollHeight-10 && this.databaseHaveMore){
+          this.databasePageNo++
+          this.getDatabaseList()
+        }
+		  },200),
+      async getChartDetail(ChartInfoId) {
+        // console.log(ChartInfoId);
+        const res = await dataBaseInterface.getChartInfoById({ChartInfoId})
+        if (res.Ret !== 200) return;
+        this.chartInfo = res.Data.ChartInfo || {}
+        this.edbData = res.Data.EdbInfoList|| []
+
+        const chartTypeMap = {
+          7: this.initBarData, //柱形图
+          10: this.initSectionScatterData //截面散点
+        }
+
+        chartTypeMap[this.chartInfo.ChartType] && chartTypeMap[this.chartInfo.ChartType](res.Data);
+
+      },
+      editLinkName(item){
+        this.editingLabel = item.Name
+        item.editing=true
+        this.$nextTick(() => {
+          this.$refs.labelEditInput[0].focus();
+        });
+      },
+      linkClick(item){
+        if(this.activeItemRId == item.RId) return
+        this.activeItemRId = item.RId
+        // console.log(item);
+        if(item.Type==3){
+          this.$message.info('研报类型的暂无回显')
+        }else if(item.Type==1){
+          this.addLinkSearchParams.linkType=item.Type
+          this.changeLinkType()
+          this.initGetData() 
+        }else if(item.Type==2){
+          this.addLinkSearchParams.linkType=item.Type
+          this.changeLinkType()
+          this.getChartDetail(item.Id)
+        }
+
+
+        // console.log('回显');
+      },
+      linkDelete(item,index){
+        if(this.activeItemRId == item.RId){
+          this.activeItemRId=""
+          this.changeLinkType()
+        }
+        if(item.Type==3){
+          let deleteId=this.selections.filter(it=> it.Id==item.Id)
+          // console.log(deleteId);
+          if(deleteId[0]){
+            this.$refs.reportTable && this.$refs.reportTable.toggleRowSelection(deleteId[0],false)
+          }
+        }
+        this.checkedLinkList.splice(index,1)
+      },
+      editLinkNameFinish(item){
+        if (this.editingLabel) {
+          item.editing=false
+          item.Name = this.editingLabel
+        } else {
+          this.$message.warning('不能为空');
+        }
+      },
+      searchReport(){
+        this.reportParams.CurrentIndex=1
+        if(!this.reportKeyWord){
+          this.reportList=[]
+          this.reportTotal=0
+        }else{
+          this.getReportList()
+        }
+      },
+      getReportList(){
+        let params={
+          CurrentIndex: this.reportParams.CurrentIndex,
+          PageSize: this.reportParams.PageSize,
+          KeyWord:this.reportKeyWord,
+          State:2
+        }
+        reportlist(params).then((res) => {
+          if (res.Ret === 200) {
+            this.reportList = res.Data.List || [];
+            this.reportTotal = parseInt(res.Data.Paging.Totals);
+          }
+        });    
+      },
+      pageChange(page_no){
+        this.reportParams.CurrentIndex = page_no;
+        this.getReportList();
+      },
+      reportSelect(selection){
+        this.selections=selection
+        // console.log(selection);
+        let allIds = this.reportList.map(it => {
+          return it.Id
+        })
+        let simpleSelections = selection.map(it => {
+          return {Id:it.Id,Code:it.ReportCode,Name:it.Title}
+        })
+
+        let unselectIds=[]
+
+        if(simpleSelections.length>0){
+          allIds.map(id =>{
+            if(simpleSelections.every(sele => id!=sele.Id )){
+              unselectIds.push(id)
+            }
+          })
+        }else{
+          unselectIds=allIds
+        }
+        // console.log(simpleSelections,unselectIds);
+        //没有就增加
+        simpleSelections.map(item =>{
+          let rId = this.addLinkSearchParams.linkType+'-'+item.Id
+          if(this.checkedLinkList.every(sele => rId!=sele.RId )){
+            this.checkedLinkList.push({
+              RId:rId,
+              Id:item.Id,
+              Name:item.Name,
+              Type:this.addLinkSearchParams.linkType,
+              editing:false,
+              detailParams:{id:item.Id,code:item.Code}
+            })
+          }
+          // else{
+          //   this.$message.warning(`${item.Name}已添加,请勿重复添加`)
+          // }
+        })
+        //有就去掉
+        unselectIds.map(item =>{
+          let rId = this.addLinkSearchParams.linkType+'-'+item
+          let index = this.checkedLinkList.findIndex(link => rId==link.RId)
+          // console.log(index);
+          if(index!=-1){
+            this.checkedLinkList.splice(index,1)
+          }
+        })
+        // console.log(this.checkedLinkList,'this.checkedLinkList');
+      },
+      foldLink(){
+        this.popoverFlod=!this.popoverFlod 
+        if(this.linkNode){
+          this.linkNode.data.linkFold = this.popoverFlod
+        }
+        this.popoverVisible=false
+        this.$nextTick(()=>{
+          this.popoverVisible=true
+        })
+      },
+      clearPopoverTimeout(){
+        clearTimeout(this.popoverTimeout)
+        this.popoverTimeout=null 
+      },
+      closePopover(){
+        this.popoverVisible=false
+      },
+      navigateTo(item){
+        // console.log(item,'item');
+        if(item.Type == 1){
+          if(item.databaseType==0){
+            // 普通指标
+            const { href } = this.$router.resolve({ path: '/database',query:item.detailParams});
+            window.open(href, '_blank');
+          }else{
+            // 预测指标
+            const { href } = this.$router.resolve({ path: '/predictEdb',query:item.detailParams});
+            window.open(href, '_blank');
+          }
+        }else if(item.Type == 2){
+          // 跳转到图库详情
+          const { href } = this.$router.resolve({ path: '/chartsetting',query:item.detailParams});
+          window.open(href, '_blank');
+        }else if(item.Type == 3){
+          // 跳转到研报
+          const { href } = this.$router.resolve({ path: '/reportdtl',query:item.detailParams});
+          window.open(href, '_blank');
+        }
+      },
+      changePopoverPositon:_.throttle(function(args){
+        this.popoverVisible=false
+        let clinetPositon=this.graph.localToClient(args.current)
+        let size=args.node.size()
+        // console.log(this.popoverTriggerDom,'domdomdom');
+        this.popoverTriggerDom.style.left = clinetPositon.x+size.width/2 + 'px';
+        this.popoverTriggerDom.style.top = clinetPositon.y + 'px';
+        this.$nextTick(()=>{
+          this.popoverVisible=true
+        })
+      },200) ,
+      mindmapRecoverRemove(mindmapNodes){
+        let shouldOperations=[]
+        this.mindmapDataUse.map((item,index)=>{
+          let levelIds = mindmapNodes.filter(mindMap => mindMap.data.id.startsWith(item.mindmapData.id)).map(mindMap => mindMap.data.id)
+          // console.log(levelIds,'levelIds');
+          if(!(levelIds && levelIds.length>0)) return 
+          // console.log(levelIds,'levelIds');
+          let mindMapIds=[...levelIds]
+          for (let i = 0; i < levelIds.length; i++) {
+            const element = levelIds[i]
+            mindMapIds=mindMapIds.filter( id => id.indexOf(element) !=0 || id==element)
+          }
+          // console.log(mindMapIds,'mindMapIds');
+          shouldOperations.push(mindMapIds)
+        })
+        // 重做删除时 恢复数据指针前进
+        console.log(this.canRedo,'this.canRedo');
+        if(!this.canRedo){
+          console.log('canredo no');
+          this.mindmapAssistData.mindmapDataRecoverUse=[JSON.stringify(this.mindmapDataUse)]
+        }else{
+          console.log('canredo');
+          this.mindmapAssistData.mindmapDataRecoverUse.push(JSON.stringify(this.mindmapDataUse))
+        }
+
+        console.log(this.mindmapAssistData.mindmapDataRecoverUse,this.mindmapDataUse,this.mindmapAssistData.mindmapDataRecoverUse.length);
+        shouldOperations.map(it =>{
+          it.map(it1 =>{
+            this.deleteMindmapData(it1,this.mindmapDataUse)
+          })
+        })
+      },
+      mindmapRecoverAdd(){
+        // 重做添加时 恢复数据指针前进
+        console.log(this.mindmapAssistData.mindmapDataRecoverUse.length,this.mindmapAssistData.mindmapDataRecoverUse);
+        let recoverData = this.mindmapAssistData.mindmapDataRecoverUse.pop()
+        console.log(JSON.parse(recoverData) ,this.mindmapAssistData.mindmapDataRecoverUse.length,this.mindmapAssistData.mindmapDataRecoverUse,'回复的数据');
+        if(recoverData) this.mindmapDataUse=JSON.parse(recoverData) 
+        console.log(this.mindmapDataUse,'回复后');
+      },
+      deleteMindmapData(id,data){
+        // console.log(id,data,'id,data');
+        // return 
+        let ids = id.split('-')
+        let mindmapDataIndex = data.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
+        if(ids.length==1){
+          data.splice(mindmapDataIndex,1)
+          return 
+        }
+
+        let mindmapData = data[mindmapDataIndex].mindmapData
+        let findId = ids[0]
+        for (let i = 1; i < ids.length-1; i++) {
+          const element = ids[i];
+          findId = findId+'-'+element
+          mindmapData=mindmapData.children.find(it => it.id==findId)
+        }
+        let endId = ids[ids.length-1]
+        // console.log(mindmapData.children,'mindmapData.children');
+        let endIndex = mindmapData.children.findIndex(it => it.id == findId+'-'+endId)
+        mindmapData.children.splice(endIndex,1)
+        // console.log(data);
+      },
+      /* 向左收起 展开 */
+      slideHandle() {
+        this.isSlideLeft = !this.isSlideLeft;
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  #sand-edit-container{
+    height: calc(100vh - 120px);
+    min-height: 600px;
+    display: flex;
+    flex-wrap: nowrap;
+    .sand-toolbar{
+      min-width: 400px;
+      width: 400px;
+      background-color: white;
+      border-radius: 4px;
+      border: 1px solid #DCDFE6;
+      height: 100%;
+      box-sizing: border-box;
+      margin-right: 20px;
+      padding-bottom: 20px;
+      position: relative;
+      .sand-elements-tab{
+        padding: 0 30px;
+        overflow: auto;
+        height: calc(100vh - 220px);
+        .sand-elements{
+          padding: 30px 0 20px;
+          border-bottom: 1px solid #C8CDD9 ;
+          span{
+            display: inline-block;
+            margin-bottom: 10px;
+            font-size: 16px;
+            color: #999999;
+          }
+          .elements-row{
+            display: flex;
+            flex-wrap: wrap;
+            align-items: center;
+            margin-right: -10px;
+            .elements-shape-item,img{
+              margin-right: 20px;
+              margin-bottom: 8px;
+              cursor: pointer;
+            }
+            img{
+              height: 18px;
+              width: 18px;
+            }
+          }
+        }
+        .sand-elements-mind{
+          border-bottom: none;
+          padding-bottom: 0;
+        }
+
+      }
+      .elements-row-mind{
+        display: flex;
+        flex-wrap: wrap;
+        align-items: center;
+        justify-content: space-between;
+        img{
+          width: 100%;
+          margin-bottom: 20px;
+        }
+      }
+      .sand-style-tab{
+        width: 100%;
+        padding: 20px 30px;
+        display: flex;
+        flex-wrap: wrap;
+        box-sizing: border-box;
+        justify-content: space-between;
+        .sand-style-tab-item{
+          box-sizing: border-box;
+          border: 1px solid #C8CDD9;
+          padding: 10px 0;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          cursor: pointer;
+          width: calc(50% - 7px);
+          margin-bottom: 14px;
+          border-radius: 4px;
+          img{
+            width: 64px;
+            height: 54px;
+          }
+        }
+
+        .active{
+          border: 1px solid #0052D9;
+        }
+      }
+    }
+    .sand-main{
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      .sand-main-top{
+        padding: 20px 20px 10px;
+        display: flex;
+        // flex-wrap: wrap;
+        align-items: center;
+        justify-content: space-between;
+        background-color: white;
+        box-shadow: 0px 2px 12px 0px rgba($color: #000000, $alpha: 0.08);
+        border: 1px solid #DCDFE6;
+        border-radius: 4px;
+        margin-bottom: 20px;
+        .sand-mainTop-form{
+          margin-bottom: 10px;
+          margin-right: 20px;
+          white-space: nowrap;
+        }
+        .sand-mainTop-option{
+          display: flex;
+          align-items: center;
+          margin-bottom: 10px;
+          .sand-option-linkShow{
+            display: flex;
+            align-items: center;
+            margin-right: 30px;
+            cursor: pointer;
+            img{
+              height: 16px;
+              margin-right: 4px;
+            }
+          }
+        }
+      }
+      .sand-mainBody-chart{
+        .sand-mainBody-tool{
+          position: absolute;
+          height: 50px;
+          width: 100%;
+          background-color: #F5F6F7;
+          box-sizing: border-box;
+          border-radius: 4px;
+          display: flex;
+          align-items: center;
+          padding-right: 10px;
+          z-index: 1;
+          top: 0;
+          left: 0;
+          .sand-tool-item{
+            margin-left: 20px;
+            cursor: pointer;
+            .dropdown-box{
+              position: relative;
+              img{
+                height: 16px;
+                width: 16px;
+              }
+              .dropdown-content{
+                display: flex;
+                align-items: center;
+
+                .dropdown-content-text{
+                  width: 30px;
+                  white-space: nowrap;
+                  overflow: hidden;
+                  margin-right: 5px;
+                }
+              }
+              .disabled-item{
+                background-color: transparent;
+                cursor: not-allowed;
+                position: absolute;
+                top: 0;
+                right: 0;
+                left: 0;
+                bottom: 0;
+                z-index: 1;
+              }
+            }
+          }
+          .img-box{
+            height: 16px;
+            width: 17px;
+            img{
+              height: 16px;
+              width: 17px;
+            }
+          }
+          .sand-tool-img{
+            height: 16px;
+            min-width: 16px;
+            position: relative;
+            img{
+              height: 16px;
+              width: 16px;
+            }
+          }
+          .tool-disabled{
+            color: #C8CDD9;
+            cursor:not-allowed;
+          }
+        }
+      }
+    }
+    .add-link-box{
+      padding: 15px 40px 35px;
+      .link-box-option{
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin-bottom: 30px;
+      }
+      .link-box-content{
+        // height:300px;
+        // max-height:300px;
+        margin-bottom: 30px;
+        .link-content-dataIndex{
+          display: flex;
+          flex-direction: column;
+          .value-ul {
+            flex-grow: 1;
+            overflow-y: auto;
+            margin-top: 10px;
+            height: 200px;
+            border-bottom: 1px solid #EBEFF6;
+            //max-height: calc(100vh - 495px);
+            //overflow-y: auto;
+            .value-item {
+              /* width: 100%; */
+              padding: 10px 0;
+              border: 1px solid #dcdfe6;
+              border-bottom: none;
+              display: flex;
+              justify-content: space-around;
+              >span{
+                padding:0 16px;
+                box-sizing: border-box;
+              }
+              .value-label {
+                position: relative;
+                color: #666;
+                .value-style{
+                  /* background-color: #ECF2FE;
+                  color: #0052D9; */
+                  padding:5px;
+                  border-radius: 4px;
+                  &.predict-act {
+                    color: orange;
+                  }
+                }
+                &.date{
+                  &::after{
+                    content: '';
+                    position:absolute;
+                    right:0;
+                    top:-14px;
+                    height:calc(100% + 28px);
+                    width:1px;
+                    background-color: #dcdfe6;
+                  }
+                }
+              }
+              .predict-act {
+                color: orange;
+              }
+              .new-tag {
+                width: 6px;
+                height: 6px;
+                display: inline-block;
+                position: absolute;
+                left: -12px;
+                top: 50%;
+                transform: translateY(-50%);
+                border-radius: 50%;
+                background: #f00;
+              }
+            }
+            .nodata {
+              text-align: center;
+              padding: 40px 0;
+              color: #999;
+            }
+          }
+          .delete-button{
+            color: #AD352F;
+            font-size: 14px;
+            cursor: pointer;
+          }
+        }
+        .link-content-chartIndex{
+          .chart-name{
+            font-size: 16px;
+            text-align: center;
+            color: #333333;
+          }
+        }
+      }
+      .link-box-tags{
+        display: flex;
+        align-items: center;
+        overflow-x: auto;
+        .link-box-tag{
+          display: flex;
+          align-items: center;
+          padding: 0 8px;
+          height: 30px;
+          max-width: 250px;
+          background-color: #F8F8F8;
+          margin-right: 30px;
+          cursor: pointer;
+          &:last-child{
+            margin-right: 0;
+          }
+          span{
+            color: #666666;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+          img{
+            height: 16px;
+            width: 16px;
+            margin-left: 8px;
+          }
+        }
+      }
+      .link-box-buttons{
+        margin-top: 60px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+      }
+    }
+  }
+  #link-popover{
+    display: flex;
+    justify-content: space-between;
+    transition: all 0.3s ease;
+    overflow: hidden;
+    .link-box{
+      .link-item{
+        &:hover{
+          text-decoration: underline;
+          color: #0052D9;
+          cursor: pointer;
+        }
+      }
+    }
+    .link-fold{
+      transition: all 0.3s ease;
+      height: 16px;
+      width: 16px;
+      cursor: pointer;
+    }
+  }
+
+  .slide-icon {
+    padding: 20px 0;
+    /* display: block; */
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.3);
+    border-radius: 5px;
+    cursor: pointer;
+    position: absolute;
+    top: 50%;
+    transform: translateY(-50%);
+    z-index: 99;
+    &:hover {
+      background-color: rgba(0, 0, 0, 0.05);
+    }
+    &.slide-left {
+      right: 0;
+    }
+    &.slide-right {
+      left: 0;
+    }
+  }
+  @media screen and (max-width:1680px) {
+    #sand-edit-container{
+      .sand-toolbar{
+        min-width: 320px;
+        width: 320px;
+      }
+    }
+  }
+</style>
+<style lang="scss">
+  .sand-toolbar-tabs{
+    .el-tabs__header{
+      padding-top: 16px;
+      box-shadow: 0px 2px 12px 0px rgba($color: #000000, $alpha: 0.08);
+    }
+  }
+  .el-cascader{
+    .el-input{
+      width: 240px;
+    }
+  }
+  .sand-mainBody-chart{
+    height: calc(100% - 50px);
+    width: 100%;
+    flex: 1;
+    position: relative;
+    overflow: hidden;
+    display: flex;
+    background-color: white;
+    border: 1px solid #DCDFE6;
+    border-radius: 4px;
+    padding-top: 50px;
+    .contextMenu-wrapper {
+      position: fixed;
+      z-index: 99;
+      top: -9999px;
+      left: -9999px;
+      background: #fff;
+      padding: 10px 0;
+      box-shadow: 0 1px 4px #999;
+    }
+    #link-reference{
+      position: fixed;
+      z-index: -1;
+      top: -99999px;
+      left: -99999px;
+      background-color: transparent;
+    }
+    #sand-chart-container{
+      flex: 1;
+    }
+    .x6-graph-scroller {
+      flex: 1;
+    }
+
+    .x6-port-body {
+      display: none;
+    }
+
+    /* reseize 框样式 */
+    .x6-widget-transform {
+      .x6-widget-transform-resize {
+        border-radius: 0;
+      }
+    }
+    .minimap{
+      position:absolute;
+      right:6px;
+      bottom:6px;
+      box-sizing: border-box;
+      .x6-widget-minimap-viewport{
+        border-color: red;
+        .x6-widget-minimap-viewport-zoom{
+          border-color: red;
+        }
+      }
+      .x6-widget-minimap{
+        width: auto !important;
+        height: auto !important;
+      }
+    }
+  }
+  .left-topic-image,.right-topic-image {
+    visibility: hidden;
+    width: 0;
+    cursor: pointer;
+  }
+  .x6-node:hover .left-topic-image {
+    width: unset;
+    visibility: visible;
+  }
+  .x6-node:hover .right-topic-image {
+    width: unset;
+    visibility: visible;
+  }
+  .x6-node-selected rect {
+    stroke-width: 2px;
+  }
+  #sand-mainBody-tool{
+    .el-select{
+      .el-input{
+        .el-input__inner{
+          border: none;
+          background-color: #F5F6F7;
+          padding-left: 0;
+          padding-right: 30px;
+        }
+      }
+    }
+  }
+  .style-acitve{
+    color: #0052D9;
+  }
+  .style-acitve-back{
+    background-color: #C5CBDA;
+  }
+
+.vue-giant-tree{
+  li{
+    ul{
+      padding: 0 0 0 10px!important;
+    }
+    .button{
+      z-index: 1;
+      height: 30px;
+      width: 20px;
+      &::before{
+        border:none!important;
+        top: 50%!important;
+        left: 50%!important;
+        transform: translate(-50%,-50%)!important;
+        height: 16px!important;
+        width: 16px!important;
+      }
+    }
+    .button.noline_close{
+      &::before{
+        content: url('../../../assets/img/set_m/slide_black.png')!important;
+      }
+    }
+    .button.noline_open{
+      &::before{
+        content: url('../../../assets/img/set_m/down_black.png')!important;
+      }
+    }
+    a{
+      width: calc(100% - 22px);
+      height: 30px!important;
+      line-height: 30px!important;
+      .node_name{
+        width: calc(100% - 6px);
+        color: #333333!important;
+        border-radius: 0!important;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
+    .curSelectedNode{
+      position: relative;
+      z-index: 0;
+      display: inline-flex;
+      flex-wrap: nowrap;
+      align-items: center;
+      .node_name{
+        flex: 1;
+        color: unset;
+        background-color: transparent;
+        &::after{
+          content: '';
+          position: absolute;
+          top: 0;
+          left: -22px;
+          height: 30px;
+          width: calc(100% + 22px);
+          background-color: #ECF2FE;
+          z-index: -1;
+        }
+      }
+      .custom-button-zone{
+        display: flex!important;
+      }
+    }
+  }
+
+}
+  .classify-popper{
+    height: 400px;
+    overflow: auto;
+  }
+  .classify-cascader-popper{
+    display: none;
+  }
+  .label-edit-input{
+    width: 220px!important;
+    .el-input__inner{
+      height: 26px;
+      padding: 10px;
+    }
+  }
+</style>

+ 84 - 0
src/views/sandbox_manage/sandFlowNew/popover.vue

@@ -0,0 +1,84 @@
+<!-- "@antv/x6-vue-shape": "1.5.4", "@vue/composition-api": "^1.5.0", -->
+<template>
+  <el-popover
+    placement="top"
+    trigger="manual"
+    v-model="popoverVisible">
+    <div id="link-popover" :style="{height:popoverFlod?'20px':'unset'}">
+      <div class="link-box">
+        <div v-for="item in checkedLinkList" :key="item.RId" class="link-item">
+          {{ item.Name }}
+        </div>
+      </div>
+      <img src="~@/assets/img/sand_new/arrow_black_down.png" class="link-fold" 
+      :style="{transform:popoverFlod?'':'rotate(180deg)'}" v-show="checkedLinkList.length>1"
+      @click="foldLink"/>
+    </div>
+    <div id="link-reference" slot="reference">不可拖拽</div>
+  </el-popover>
+</template>
+
+<script>
+  export default {
+    name:'linkPopover',
+    inject: ["getGraph", "getNode"],
+    data() {
+      return {
+        checkedLinkList:[],
+        popoverFlod:true,
+        node:null,
+        popoverVisible:false
+      }
+    },
+    mounted(){
+      const self = this;
+      this.node = this.getNode();
+      const graph = this.getGraph()
+      console.log(this.node,'this.node');
+      graph.on("node:mouseenter", ({ node, e }) => {
+        console.log(node, e);
+        this.popoverVisible=true
+        if(node.data){
+          this.checkedLinkList = node.data.linkData || []
+        }
+        console.log(this.checkedLinkList);
+      });
+    },
+    methods:{
+      foldLink(){
+        this.popoverFlod=!this.popoverFlod 
+        if(this.node.data){
+          this.node.data.linkFold = this.popoverFlod
+        }
+        this.popoverVisible=false
+        this.$nextTick(()=>{
+          this.popoverVisible=true
+        })
+      },
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  #link-popover{
+    display: flex;
+    justify-content: space-between;
+    transition: all 0.3s ease;
+    overflow: hidden;
+    .link-box{
+      .link-item{
+        &:hover{
+          text-decoration: underline;
+          color: #0052D9;
+          cursor: pointer;
+        }
+      }
+    }
+    .link-fold{
+      transition: all 0.3s ease;
+      height: 16px;
+      width: 16px;
+      cursor: pointer;
+    }
+  }
+</style>

+ 164 - 3
src/vuex/modules/sand.js

@@ -1,5 +1,119 @@
 // 沙盘
-import { configOpt } from '@/views/sandbox_manage/common/toolConfig';
+import { configOpt,styleSettings } from '@/views/sandbox_manage/common/toolConfig';
+
+const setSandboxToolStatus=(state,payload)=>{
+	console.log(payload,'payload',state);
+	if(!(payload&&payload.length>0)){
+		state.toolStatus.textDisabled=true
+		state.toolStatus.edgeDisabled=true
+		state.toolStatus.nodeDisabled=true
+		state.styleOptions={
+			fontF:"微软雅黑",
+			fontS:18,
+			fontW:'normal',
+			fontStyle:'normal',
+			textDecoration:'none',
+			color:'#333333',
+			lineHeight:1.3,
+			textAlign:'none',
+			backgroundColor:'#DAE8FF',
+			lineColor:'#0052D9',
+			lineStyle:null,
+			lineWidth:2,
+			connectStyle:1,
+			startArrow:{},
+			endArrow:{}
+		}
+		return 
+	}else{
+		const fontFamilySet=new Set(),fontSizeSet=new Set(),fontWeightSet=new Set(),fontStyleSet=new Set(),
+		textDecorationSet=new Set(),colorSet=new Set(),lineHeightSet=new Set(),
+		textAlignSet=new Set(),backgroundColorSet=new Set(),lineColorSet=new Set(),
+		lineStyleSet=new Set(),lineWidthSet=new Set(),connectStyleSet=new Set(),
+		startArrowSet=new Set(),endArrowSet=new Set()
+
+		for (let i = 0; i < payload.length; i++) {
+			const element = payload[i];
+			const attrs = element.getAttrs()
+			console.log(element.shape);
+			if(element.shape.indexOf('edge')==-1){
+				state.toolStatus.textDisabled=false
+				state.toolStatus.edgeDisabled=true
+				fontFamilySet.add(attrs.text.fontFamily)
+				fontSizeSet.add(attrs.text.fontSize)
+				fontWeightSet.add(attrs.text.fontWeight)
+				fontStyleSet.add(attrs.text.fontStyle)
+				textDecorationSet.add(attrs.text.textDecoration)
+				colorSet.add(attrs.text.fill)
+				lineHeightSet.add(Math.round((attrs.text.lineHeight/attrs.text.fontSize)*10)/10)
+				textAlignSet.add(attrs.text.textAnchor)
+				if(!(element.data) || element.data.key!='text'){
+					// console.log('不是text');
+					backgroundColorSet.add(attrs.body.fill)
+					lineColorSet.add(attrs.body.stroke)
+					lineStyleSet.add(attrs.body.strokeDasharray)
+					lineWidthSet.add(attrs.body.strokeWidth)
+					state.toolStatus.nodeDisabled=false
+				}else{
+					state.toolStatus.nodeDisabled=true
+				}
+			}else{
+				state.toolStatus.edgeDisabled=false
+				state.toolStatus.textDisabled=true
+				state.toolStatus.nodeDisabled=true
+				lineColorSet.add(attrs.line.stroke)
+				lineStyleSet.add(attrs.line.strokeDasharray)
+				lineWidthSet.add(attrs.line.strokeWidth)
+				startArrowSet.add(attrs.line.sourceMarker)
+				endArrowSet.add(attrs.line.targetMarker)
+				const router = element.getRouter()
+				const connector = element.getConnector()
+				console.log(router,connector,'router,connector');
+				if(router && router.name=="normal"){
+					connectStyleSet.add(1)
+				}else{
+					if((!connector) || connector.name == 'normal'){
+						connectStyleSet.add(2)
+					}else{
+						connectStyleSet.add(3)
+					}
+				}
+			}
+		}
+		// 设置fontFamily回显
+		state.styleOptions.fontF=fontFamilySet.size==1?Array.from(fontFamilySet)[0]:"微软雅黑"
+		// 设置fontSize回显
+		state.styleOptions.fontS=fontSizeSet.size==1?Array.from(fontSizeSet)[0]:18
+		// 设置fontWeight回显
+		state.styleOptions.fontW=fontWeightSet.size==1?Array.from(fontWeightSet)[0] || 'normal':"normal"
+		// 设置fontStyle回显
+		state.styleOptions.fontStyle=fontStyleSet.size==1?Array.from(fontStyleSet)[0] || 'normal':"normal"
+		// 设置textDecoration回显
+		state.styleOptions.textDecoration=textDecorationSet.size==1?Array.from(textDecorationSet)[0] || 'none':"none"
+		// 设置color回显
+		state.styleOptions.color=colorSet.size==1?Array.from(colorSet)[0]:"#333333"
+		// 设置lineHeight回显
+		state.styleOptions.lineHeight=lineHeightSet.size==1?Array.from(lineHeightSet)[0]:1.3
+		// 设置textAlign回显
+		state.styleOptions.textAlign=textAlignSet.size==1?Array.from(textAlignSet)[0]:'none'
+		// 设置backgroundColor回显
+		state.styleOptions.backgroundColor=backgroundColorSet.size==1?Array.from(backgroundColorSet)[0]:'#DAE8FF'
+		// 设置lineColor回显
+		state.styleOptions.lineColor=lineColorSet.size==1?Array.from(lineColorSet)[0]:'#0052D9'
+		// 设置lineStyle回显
+		state.styleOptions.lineStyle=lineStyleSet.size==1?Array.from(lineStyleSet)[0]:null
+		// 设置lineWidth回显
+		state.styleOptions.lineWidth=lineWidthSet.size==1?Array.from(lineWidthSet)[0]:2
+		// 设置connectStyle回显
+		state.styleOptions.connectStyle=connectStyleSet.size==1?Array.from(connectStyleSet)[0]:1
+		// 设置sourceMarker回显
+		state.styleOptions.startArrow=startArrowSet.size==1?Array.from(startArrowSet)[0]||{}:{}
+		// 设置targetMarker回显
+		state.styleOptions.endArrow=endArrowSet.size==1?Array.from(endArrowSet)[0]||{}:{}
+	}
+}
+
+
 
 const sand = {
 	namespaced: true,
@@ -11,6 +125,38 @@ const sand = {
 		DisableLine: true, //禁用线条设置
 		DisableBorder: true, //禁用线框设置
 		selectCell: null,
+		// 不同风格
+		style:1,
+		styleConfig:{
+			backgroundColor:'#BBCEFF',
+			color:'#1841AA',
+			textColor:'#1841AA',
+			borderColor:'#1841AA',
+			lineColor:'#1841AA'
+		},
+		toolStatus:{
+			nodeDisabled:true,
+			edgeDisabled:true,
+			textDisabled:true,
+		},
+		styleOptions:{
+			fontF:"微软雅黑",
+			fontS:18,
+			fontW:'normal',
+			fontStyle:'normal',
+			textDecoration:'none',
+			color:'#333333',
+			lineHeight:1.3,
+			textAlign:'none',
+			backgroundColor:'#DAE8FF',
+			lineColor:'#0052D9',
+			lineStyle:null,
+			lineWidth:2,
+			connectStyle:1,
+			startArrow:{},
+			endArrow:{}
+		},
+		selectCells:[]
 	}),
   mutations: {
 		/* 节点选中 toolbar设置状态 */
@@ -64,13 +210,28 @@ const sand = {
 		SET_SELECT_CELL(state,payload) {
 			state.selectCell = payload;
 		},
-
+		/* 设置选中节点 */
+		SET_SELECT_CELLS(state,payload) {
+			state.selectCells = payload;
+			// 设置工具状态栏的回显和禁用
+			setSandboxToolStatus(state,payload)
+		},
 		/* 设置加粗样式 */
 		SET_BOLD_STYLE(state,{attr,val}) {
 			state.initConfig.text.fontWeight = val;
 			state.selectCell.attr(`${attr}`,val);
+		},
+		// 设置节点风格
+		SET_CELL_STYLE(state,style){
+			if(!style) style=1
+			state.style = style
+			let styleData = styleSettings[style-1]
+			state.styleConfig.backgroundColor = styleData.backgroundColor
+			state.styleConfig.color = styleData.color
+			state.styleConfig.textColor = styleData.textColor
+			state.styleConfig.borderColor = styleData.borderColor
+			state.styleConfig.lineColor = styleData.lineColor
 		}
-
 	},
   actions: {