jsmind2.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <template>
  2. <!-- 普通菜单 -->
  3. <div class="jsmind_layout">
  4. <div class="noMind" v-if="mindV">
  5. <img src="../../assets/nominddata.png" alt />
  6. </div>
  7. <div :id="jsmindId" ref="container" class="jsmind_container"></div>
  8. </div>
  9. </template>
  10. <script>
  11. import "jsmind/style/jsmind.css";
  12. import jsMind from "jsmind/js/jsmind.js";
  13. window.jsMind = jsMind;
  14. require("jsmind/js/jsmind.draggable.js");
  15. require("jsmind/js/jsmind.screenshot.js");
  16. export default {
  17. props: {
  18. showBar: {
  19. // 是否显示工具栏,显示启用编辑
  20. type: Boolean,
  21. default: true,
  22. },
  23. theme: {
  24. // 主题
  25. type: String,
  26. default: "primary",
  27. },
  28. lineColor: {
  29. // 线条颜色
  30. type: String,
  31. default: "skyblue",
  32. },
  33. mindData: {
  34. type: Object,
  35. default: {},
  36. },
  37. jsmindId: {
  38. type: String,
  39. default: "jsmind_container",
  40. },
  41. },
  42. data() {
  43. return {
  44. mindV: false,
  45. i: 0,
  46. mind: {},
  47. jm: null,
  48. isZoomIn: false,
  49. isZoomOut: false,
  50. level: 0,
  51. nodeOptions: [
  52. { value: 1, label: "展开到一级节点" },
  53. { value: 2, label: "展开到二级节点" },
  54. { value: 3, label: "展开到三级节点" },
  55. { value: 0, label: "展开全部节点" },
  56. { value: -1, label: "隐藏全部节点" },
  57. ],
  58. themeOptions: [
  59. { value: "default", label: "default" },
  60. { value: "primary", label: "primary" },
  61. { value: "warning", label: "warning" },
  62. { value: "danger", label: "danger" },
  63. { value: "success", label: "success" },
  64. { value: "info", label: "info" },
  65. { value: "greensea", label: "greensea" },
  66. { value: "nephrite", label: "nephrite" },
  67. { value: "belizehole", label: "belizehole" },
  68. { value: "wisteria", label: "wisteria" },
  69. { value: "asphalt", label: "asphalt" },
  70. { value: "orange", label: "orange" },
  71. { value: "pumpkin", label: "pumpkin" },
  72. { value: "pomegranate", label: "pomegranate" },
  73. { value: "clouds", label: "clouds" },
  74. { value: "asbestos", label: "asbestos" },
  75. ],
  76. localTheme: this.theme,
  77. dialogVisible: false,
  78. nodeOption: {
  79. content: "",
  80. bgColor: "",
  81. fontColor: "",
  82. fontSize: "",
  83. fontWeight: "",
  84. fontStyle: "",
  85. },
  86. };
  87. },
  88. watch: {
  89. mindData: {
  90. handler: function (cur, old) {
  91. this.mind = cur;
  92. if (cur.data.length) {
  93. if (cur.data[0].topic === "" && cur.data.length === 1) {
  94. this.mindV = true;
  95. } else {
  96. this.mindV = false;
  97. }
  98. if (this.jm) {
  99. this.jm.show(this.mind);
  100. } else {
  101. this.open_empty();
  102. }
  103. }
  104. },
  105. deep: true, //对象内部的属性监听,也叫深度监听
  106. },
  107. },
  108. created() {},
  109. mounted() {
  110. this.getData();
  111. // this.mouseWheel();
  112. },
  113. methods: {
  114. beforeUpload(file) {
  115. // 上传文件之前钩子
  116. if (file) {
  117. jsMind.util.file.read(file, (jsmindData) => {
  118. const mind = jsMind.util.json.string2json(jsmindData);
  119. if (mind) {
  120. this.jm.show(mind);
  121. this.$message({ type: "success", message: "打开成功" });
  122. } else {
  123. this.prompt_info("不能打开mindmap文件");
  124. }
  125. });
  126. } else {
  127. this.prompt_info("请先选择文件");
  128. return false;
  129. }
  130. },
  131. upload() {},
  132. getData() {
  133. // this.$API({
  134. // name: "getMind",
  135. // })
  136. // .then((res) => {
  137. // this.mind = res.data;
  138. // this.open_empty();
  139. // })
  140. // .catch((error) => {
  141. // this.$message.error(error);
  142. // });
  143. if (
  144. !this.mind.data ||
  145. (this.mind.data[0].topic === "" && this.mind.data.length === 1)
  146. ) {
  147. this.mindV = true;
  148. } else {
  149. this.mind = this.mindData;
  150. this.open_empty();
  151. this.mindV = false;
  152. }
  153. },
  154. open_empty() {
  155. const options = {
  156. container: this.jsmindId, // 必选,容器ID
  157. editable: this.showBar, // 可选,是否启用编辑
  158. theme: this.localTheme, // 可选,主题
  159. view: {
  160. line_width: 2, // 思维导图线条的粗细
  161. // line_color: this.lineColor, // 思维导图线条的颜色
  162. },
  163. shortcut: {
  164. enable: true, // 禁用快捷键
  165. },
  166. layout: {
  167. hspace: 20, // 节点之间的水平间距
  168. vspace: 10, // 节点之间的垂直间距
  169. pspace: 13, // 节点与连接线之间的水平间距(用于容纳节点收缩/展开控制器)
  170. },
  171. mode: "side", // 显示模式,子节点只分布在根节点右侧
  172. };
  173. this.jm = jsMind.show(options, this.mind);
  174. // 改变窗口大小重置画布
  175. window.onresize = () => {
  176. this.jm.resize();
  177. };
  178. this.getDepth(this.jm.mind.root, 1);
  179. this.$forceUpdate();
  180. },
  181. // 获取层级数 i
  182. getDepth(obj, k) {
  183. this.i = Math.max(this.i, k);
  184. if (obj.children) {
  185. obj.children.forEach((v) => {
  186. this.getDepth(v, k + 1);
  187. });
  188. }
  189. },
  190. save_nodearray_file() {
  191. const mindData = this.jm.get_data("node_array");
  192. const mindName = mindData.meta.name;
  193. const mindStr = jsMind.util.json.json2string(mindData);
  194. jsMind.util.file.save(mindStr, "text/jsmind", mindName + ".jm");
  195. },
  196. screen_shot() {
  197. this.jm.screenshot.shootDownload();
  198. },
  199. expand_all() {
  200. this.jm.expand_all();
  201. },
  202. collapse_all() {
  203. this.jm.collapse_all();
  204. },
  205. expand_to_level(num) {
  206. switch (num) {
  207. case -1:
  208. this.collapse_all();
  209. break;
  210. case 0:
  211. this.expand_all();
  212. break;
  213. default:
  214. this.jm.expand_to_depth(num);
  215. break;
  216. }
  217. },
  218. zoomIn() {
  219. if (this.jm.view.zoomIn()) {
  220. this.isZoomOut = false;
  221. } else {
  222. this.isZoomIn = true;
  223. }
  224. },
  225. zoomOut() {
  226. debugger;
  227. if (this.jm.view.zoomOut()) {
  228. this.isZoomIn = false;
  229. } else {
  230. this.isZoomOut = true;
  231. }
  232. },
  233. prompt_info(msg) {
  234. this.$message({ type: "warning", message: msg });
  235. },
  236. get_nodearray_data() {
  237. const mindData = this.jm.get_data("node_array");
  238. const mindString = jsMind.util.json.json2string(mindData);
  239. this.$message({ type: "info", message: mindString });
  240. },
  241. set_theme(themeName) {
  242. this.jm.set_theme(themeName);
  243. },
  244. scrollFunc(e) {
  245. e = e || window.event;
  246. if (e.wheelDelta) {
  247. if (e.wheelDelta > 0) {
  248. this.zoomIn();
  249. } else {
  250. this.zoomOut();
  251. }
  252. } else if (e.detail) {
  253. if (e.detail > 0) {
  254. this.zoomIn();
  255. } else {
  256. this.zoomOut();
  257. }
  258. }
  259. this.jm.resize();
  260. },
  261. // 鼠标滚轮放大缩小
  262. mouseWheel() {
  263. if (document.addEventListener) {
  264. document.addEventListener("domMouseScroll", this.scrollFunc, false);
  265. }
  266. this.$refs.container.onmousewheel = this.scrollFunc;
  267. },
  268. // 新增节点
  269. addNode() {
  270. let selectedNode = this.jm.get_selected_node();
  271. if (!selectedNode) {
  272. this.$message({ type: "warning", message: "请先选择一个节点!" });
  273. return;
  274. }
  275. let nodeid = jsMind.util.uuid.newid();
  276. let topic = "new Node";
  277. let newNode = this.jm.add_node(selectedNode, nodeid, topic);
  278. if (newNode) {
  279. this.jm.select_node(nodeid);
  280. this.jm.begin_edit(nodeid);
  281. this.getDepth(this.jm.mind.root, 1);
  282. }
  283. },
  284. // 新增兄弟节点
  285. addBrotherNode() {
  286. let selectedNode = this.jm.get_selected_node();
  287. if (!selectedNode) {
  288. this.$message({ type: "warning", message: "请先选择一个节点!" });
  289. return;
  290. } else if (selectedNode.isroot) {
  291. this.$message({
  292. type: "warning",
  293. message: "不能在根节点添加,请重新选择节点!",
  294. });
  295. return;
  296. }
  297. let nodeid = jsMind.util.uuid.newid();
  298. let topic = "new Node";
  299. let newNode = this.jm.insert_node_after(selectedNode, nodeid, topic);
  300. if (newNode) {
  301. this.jm.select_node(nodeid);
  302. this.jm.begin_edit(nodeid);
  303. }
  304. },
  305. // 获取选中标签的 ID
  306. get_selected_nodeid() {
  307. let selectedNode = this.jm.get_selected_node();
  308. if (selectedNode) {
  309. return selectedNode.id;
  310. } else {
  311. return null;
  312. }
  313. },
  314. // 删除节点
  315. removeNode() {
  316. let selectedId = this.get_selected_nodeid();
  317. if (!selectedId) {
  318. this.$message({
  319. type: "warning",
  320. message: "请先选择一个节点!",
  321. });
  322. return;
  323. }
  324. this.jm.remove_node(selectedId);
  325. this.i = 0;
  326. this.getDepth(this.jm.mind.root, 1);
  327. },
  328. // 编辑节点
  329. editNode() {
  330. let selectedId = this.get_selected_nodeid();
  331. if (!selectedId) {
  332. this.$message({ type: "warning", message: "请先选择一个节点!" });
  333. return;
  334. }
  335. let nodeObj = this.jm.get_node(selectedId);
  336. this.nodeOption.content = nodeObj.topic;
  337. this.nodeOption.bgColor = nodeObj.data["background-color"];
  338. this.nodeOption.fontColor = nodeObj.data["foreground-color"];
  339. this.nodeOption.fontSize = nodeObj.data["font-size"];
  340. this.nodeOption.fontWeight = nodeObj.data["font-weight"];
  341. this.nodeOption.fontStyle = nodeObj.data["font-style"];
  342. this.dialogVisible = true;
  343. },
  344. sureEditNode() {
  345. let selectedId = this.get_selected_nodeid();
  346. this.jm.update_node(selectedId, this.nodeOption.content);
  347. this.jm.set_node_font_style(
  348. selectedId,
  349. this.nodeOption.fontSize,
  350. this.nodeOption.fontWeight,
  351. this.nodeOption.fontStyle
  352. );
  353. this.jm.set_node_color(
  354. selectedId,
  355. this.nodeOption.bgColor,
  356. this.nodeOption.fontColor
  357. );
  358. this.nodeOption = {
  359. content: "",
  360. bgColor: "",
  361. fontColor: "",
  362. fontSize: "",
  363. fontWeight: "",
  364. fontStyle: "",
  365. };
  366. this.dialogVisible = false;
  367. },
  368. },
  369. beforeDestroy() {
  370. // document.removeEventListener("domMouseScroll", this.scrollFunc, false);
  371. },
  372. };
  373. </script>
  374. <style scoped>
  375. .jsmind_layout {
  376. display: flex;
  377. flex-direction: column;
  378. width: 700px;
  379. height: calc(100%);
  380. /* height: 500px; */
  381. /* margin: 15px 5px 0 0; */
  382. background: #fff;
  383. overflow: hidden;
  384. flex-shrink: 0;
  385. position: relative;
  386. }
  387. .jsmind_title {
  388. position: absolute;
  389. top: 20px;
  390. left: 20px;
  391. font-size: 20px;
  392. color: #8d8d8d;
  393. }
  394. .noMind {
  395. position: absolute;
  396. display: flex;
  397. justify-content: center;
  398. align-items: center;
  399. width: 100%;
  400. height: 100%;
  401. z-index: 999;
  402. background: #fff;
  403. }
  404. .jsmind_layout .jsmind_toolbar {
  405. width: 100%;
  406. padding: 0 10px 10px 10px;
  407. height: auto;
  408. flex-shrink: 0;
  409. display: flex;
  410. align-items: center;
  411. flex-wrap: wrap;
  412. background-color: #f8f9fa;
  413. box-shadow: 0 0 4px #b8b8b8;
  414. }
  415. .jsmind_layout >>> .el-button--medium,
  416. .jsmind_layout >>> .el-input--medium {
  417. margin-top: 10px;
  418. }
  419. .jsmind_layout #jsmind_container {
  420. /* flex: 1 1 auto; */
  421. height: 100%;
  422. }
  423. .jsmind_layout .jsmind_container {
  424. /* flex: 1 1 auto; */
  425. height: 100%;
  426. }
  427. .jsmind_layout >>> .jsmind-inner {
  428. /* overflow: hidden auto !important; */
  429. /* height: auto; */
  430. }
  431. .jsmind_layout >>> .el-upload-list {
  432. display: none !important;
  433. }
  434. /* 隐藏滚动条 */
  435. .jsmind_layout .jsmind-inner::-webkit-scrollbar {
  436. display: none;
  437. }
  438. .jsmind_layout .pad {
  439. margin-right: 10px;
  440. }
  441. .jsmind_layout .pad-left {
  442. margin-left: 10px;
  443. }
  444. .jsmind_layout >>> jmnode.selected {
  445. background-color: #b9b9b9;
  446. color: #fff;
  447. box-shadow: 2px 2px 8px #777;
  448. }
  449. .jsmind_layout >>> jmnode:hover {
  450. box-shadow: 2px 2px 8px #777;
  451. }
  452. .jsmind_layout .form-con {
  453. padding-top: 20px;
  454. }
  455. .jsmind_layout .ele-width {
  456. width: 96%;
  457. }
  458. </style>