Mindmap.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <template>
  2. <div class="mindmap-container" ref="mindmapRef"></div>
  3. </template>
  4. <script setup lang="tsx">
  5. import {
  6. ref,
  7. onMounted,
  8. onBeforeUnmount,
  9. defineProps,
  10. watch,
  11. defineExpose,
  12. withDefaults,
  13. defineEmits,
  14. } from "vue";
  15. import MindMap from "simple-mind-map";
  16. import defaultTheme from "./defaultTheme";
  17. // mindmap plugins
  18. import Drag from "simple-mind-map/src/plugins/Drag";
  19. import SearchPlugin from "simple-mind-map/src/plugins/Search";
  20. MindMap.usePlugin(Drag);
  21. MindMap.usePlugin(SearchPlugin);
  22. export type MindMapInstance = {
  23. getInstance: () => MindMap;
  24. getActiveNodeList: () => any[];
  25. };
  26. const props = withDefaults(
  27. defineProps<{
  28. data?: any;
  29. readonly?: boolean;
  30. }>(),
  31. {
  32. readonly: false,
  33. }
  34. );
  35. const emit = defineEmits(["update:data"]);
  36. const mindmapRef = ref<HTMLDivElement | null>(null);
  37. const mindmap = ref<MindMap | null>(null);
  38. const activeNodeList = ref<any[]>([]);
  39. let loaded = false;
  40. onBeforeUnmount(() => {
  41. mindmap.value?.destroy();
  42. });
  43. watch(
  44. () => props.data,
  45. (data) => {
  46. console.log("data", data);
  47. data && mindmap.value?.updateData(data);
  48. },
  49. {
  50. deep: true,
  51. immediate: true,
  52. }
  53. );
  54. watch(
  55. () => props.readonly,
  56. (val) => {
  57. mindmap.value?.setMode(val ? "readonly" : "edit");
  58. }
  59. );
  60. onMounted(() => {
  61. const instance = new MindMap({
  62. el: mindmapRef.value!,
  63. data: props.data || {
  64. data: {
  65. text: "主物料",
  66. },
  67. children: [],
  68. },
  69. themeConfig: {
  70. ...defaultTheme,
  71. backgroundColor: "#eee",
  72. },
  73. // 只读
  74. readonly: !!props.readonly,
  75. isUseCustomNodeContent: true,
  76. customCreateNodeContent: (node: any) => {
  77. const { sourceData = {} } = node.getData();
  78. console.log("node", node, sourceData);
  79. // return你的自定义DOM节点
  80. let div = document.createElement("div");
  81. div.className = "mx-12px my-8px";
  82. div.style = "user-select: none;";
  83. div.innerHTML = `
  84. <div class="w-200px text-#666">
  85. ${
  86. // <div class="h-60px w-80px overflow-hidden m-auto mb-8px">
  87. // <img src="https://picsum.photos/80/60" class="m-auto"/>
  88. // </div>
  89. ""
  90. }
  91. <div class="border-b border-b-solid flex items-center justify-between pb-4px">
  92. <span class="text-sm font-light">零件号</span>
  93. <span class="text-sm font-medium">123445</span>
  94. </div>
  95. <div class="border-b border-b-solid flex items-center justify-between pb-4px">
  96. <span class="text-sm font-light">描述</span>
  97. <span class="text-sm font-medium">xxxx零件</span>
  98. </div>
  99. <div class="border-b flex items-center justify-between pb-4px">
  100. <span class="text-sm font-light">数量</span>
  101. <span class="text-sm font-medium">10</span>
  102. </div>
  103. </div>`;
  104. return div;
  105. },
  106. } as any);
  107. // 事件监听
  108. // 自适应
  109. instance.on("node_tree_render_end", () => {
  110. if (loaded) return;
  111. // @ts-ignore
  112. instance?.view?.fit();
  113. loaded = true;
  114. });
  115. // 激活节点
  116. instance.on("node_active", (_node: any, list: any[]) => {
  117. activeNodeList.value = list;
  118. });
  119. // 数据改变
  120. instance.on("data_change", (data: any) => {
  121. // console.log("data_change", data);
  122. emit("update:data", data);
  123. });
  124. // 视图数据改变
  125. // instance.on("view_data_change", (data: any) => {
  126. // console.log("view_data_change", data);
  127. // });
  128. mindmap.value = instance;
  129. });
  130. // 定义暴露给父组件的方法
  131. defineExpose({
  132. getInstance: () => mindmap.value,
  133. getActiveNodeList: () => activeNodeList.value,
  134. });
  135. </script>
  136. <style scoped>
  137. .mindmap-container {
  138. width: 100%;
  139. height: 100%;
  140. padding: 0;
  141. margin: 0;
  142. }
  143. </style>