MindmapModal.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <template>
  2. <div v-if="visible" class="mindmap-modal">
  3. <div class="mindmap-modal_header">
  4. <div class="text-xl font-semibold">查看“BOM物料名称”</div>
  5. <el-button
  6. v-if="!hideClose"
  7. link
  8. @click="close"
  9. :icon="Close"
  10. style="font-size: 1.2em"
  11. ></el-button>
  12. </div>
  13. <div class="mindmap-modal_tool">
  14. <el-form ref="formRef" :model="formData" class="flex items-center" inline>
  15. <el-form-item label="收缩至" prop="level" class="mr-12px">
  16. <el-select
  17. v-model="formData.level"
  18. placeholder="请选择"
  19. style="width: 120px"
  20. @change="changeLevel"
  21. >
  22. <el-option v-for="item in 10" :key="item" :label="`${item}级`" :value="item" />
  23. </el-select>
  24. </el-form-item>
  25. <el-form-item label="布局" prop="layout" class="mr-12px">
  26. <el-select
  27. v-model="formData.layout"
  28. placeholder="请选择"
  29. style="width: 120px"
  30. @change="changeLayout"
  31. >
  32. <el-option label="水平" value="logicalStructure" />
  33. <el-option label="垂直" value="organizationStructure" />
  34. </el-select>
  35. </el-form-item>
  36. <el-form-item label="搜索" props="search">
  37. <div class="flex">
  38. <el-input v-model="formData.search" style="width: 120px" />
  39. <el-button
  40. class="ml-12px"
  41. :icon="Search"
  42. @click="handleSearch"
  43. ></el-button>
  44. </div>
  45. </el-form-item>
  46. </el-form>
  47. <div class="flex gap-4px">
  48. <el-tooltip content="添加">
  49. <el-button
  50. type="default"
  51. circle
  52. :icon="CirclePlusFilled"
  53. @click="addNode"
  54. />
  55. </el-tooltip>
  56. <el-tooltip content="详情">
  57. <el-button
  58. type="default"
  59. circle
  60. :icon="InfoFilled"
  61. @click="openNodeDetail"
  62. />
  63. </el-tooltip>
  64. <el-tooltip content="自适应">
  65. <el-button type="default" circle @click="autoFit">
  66. <img :src="AutoIcon" style="width: 1em" />
  67. </el-button>
  68. </el-tooltip>
  69. <el-tooltip content="定位到根节点">
  70. <el-button type="default" circle :icon="Aim" @click="zoomToRoot" />
  71. </el-tooltip>
  72. <el-tooltip content="放大">
  73. <el-button
  74. type="default"
  75. circle
  76. :icon="ZoomIn"
  77. @click="handleZoom(1)"
  78. />
  79. </el-tooltip>
  80. <el-tooltip content="缩小">
  81. <el-button
  82. type="default"
  83. circle
  84. :icon="ZoomOut"
  85. @click="handleZoom(0)"
  86. />
  87. </el-tooltip>
  88. </div>
  89. </div>
  90. <div class="mindmap-modal_body">
  91. <Mindmap v-model:data="editBomStore.mindmapData" ref="mindmapRef" />
  92. </div>
  93. </div>
  94. <ConfigDrawer ref="configDrawerRef" />
  95. </template>
  96. <script setup lang="ts">
  97. import { ref, defineExpose, reactive, defineProps } from "vue";
  98. import {
  99. Aim,
  100. CirclePlusFilled,
  101. Close,
  102. InfoFilled,
  103. Search,
  104. ZoomIn,
  105. ZoomOut,
  106. } from "@element-plus/icons-vue";
  107. import type { MindMapInstance } from "@/components/mindmap/Mindmap.vue";
  108. import type { FormInstance } from "element-plus";
  109. import { ElMessage } from "element-plus";
  110. import { bfsWalk } from "simple-mind-map/src/utils";
  111. import { useEditBomStore } from "@/store/editbom";
  112. import Mindmap from "@/components/mindmap/Mindmap.vue";
  113. import ConfigDrawer from "./ConfigDrawer.vue";
  114. import AutoIcon from "@/assets/auto.svg";
  115. const props = defineProps<{
  116. defaultOpen?: boolean;
  117. hideClose?: boolean;
  118. }>();
  119. const visible = ref(!!props.defaultOpen);
  120. const mindmapRef = ref<MindMapInstance>();
  121. const configDrawerRef = ref();
  122. const formRef = ref<FormInstance>();
  123. const editBomStore = useEditBomStore();
  124. const formData = reactive({
  125. level: undefined,
  126. layout: "logicalStructure",
  127. search: "",
  128. });
  129. // 定位到根节点
  130. const zoomToRoot = () => {
  131. const mindmap = mindmapRef.value?.getInstance();
  132. mindmap?.renderer?.setRootNodeCenter();
  133. };
  134. // 切换布局
  135. const changeLayout = (value: string) => {
  136. const mindmap = mindmapRef.value?.getInstance();
  137. mindmap?.setLayout(value);
  138. let fited = false;
  139. mindmap?.on("node_tree_render_end", () => {
  140. // @ts-ignore
  141. !fited && mindmap?.view?.fit();
  142. fited = true;
  143. });
  144. };
  145. // 切换层级
  146. const changeLevel = (level: number) => {
  147. const mindmap = mindmapRef.value?.getInstance();
  148. mindmap?.execCommand('UNEXPAND_TO_LEVEL', level);
  149. }
  150. const autoFit = () => {
  151. const mindmap = mindmapRef.value?.getInstance();
  152. // @ts-ignore
  153. mindmap?.view?.fit();
  154. };
  155. // 节点详情
  156. const openNodeDetail = () => {
  157. const activeList = mindmapRef.value?.getActiveNodeList();
  158. if (!activeList?.length) {
  159. ElMessage.warning("请选择节点");
  160. return;
  161. }
  162. configDrawerRef.value.open();
  163. };
  164. // 缩放
  165. const handleZoom = (num: number) => {
  166. const mindmap = mindmapRef.value?.getInstance();
  167. if (num) {
  168. // @ts-ignore
  169. mindmap?.view?.enlarge();
  170. } else {
  171. // @ts-ignore
  172. mindmap?.view?.narrow();
  173. }
  174. };
  175. // 添加节点
  176. const addNode = () => {
  177. const mindmap = mindmapRef.value?.getInstance();
  178. const activeList = mindmapRef.value?.getActiveNodeList();
  179. if (!activeList?.length) {
  180. ElMessage.warning("请选择父节点");
  181. return;
  182. }
  183. mindmap?.execCommand("INSERT_CHILD_NODE");
  184. };
  185. // 搜索
  186. const handleSearch = () => {
  187. const result = [];
  188. const mindmap = mindmapRef.value?.getInstance();
  189. const data = mindmap?.getData(false);
  190. console.log(data);
  191. // TODO: 处理搜索结果
  192. bfsWalk(data, (node: any) => {
  193. console.log('遍历节点:', node);
  194. if(node.data?.a?.includes(formData.search)) {
  195. result.push(node);
  196. }
  197. });
  198. };
  199. // 打开弹窗
  200. const open = () => {
  201. visible.value = true;
  202. };
  203. // 关闭弹窗
  204. const close = () => {
  205. formRef.value?.resetFields();
  206. visible.value = false;
  207. };
  208. defineExpose({
  209. open,
  210. close,
  211. });
  212. </script>
  213. <style lang="less" scoped>
  214. .mindmap-modal {
  215. position: absolute;
  216. top: 0;
  217. left: 0;
  218. width: 100%;
  219. height: 100%;
  220. z-index: 99;
  221. display: flex;
  222. flex-direction: column;
  223. align-items: center;
  224. &_header {
  225. height: 40px;
  226. width: 100%;
  227. box-sizing: border-box;
  228. background: #fff;
  229. display: flex;
  230. justify-content: space-between;
  231. align-items: center;
  232. padding: 0 20px;
  233. line-height: 40px;
  234. }
  235. &_tool {
  236. width: 100%;
  237. box-sizing: border-box;
  238. background: #fff;
  239. display: flex;
  240. justify-content: space-between;
  241. align-items: center;
  242. padding: 0 20px;
  243. }
  244. &_body {
  245. flex: 1;
  246. width: 100%;
  247. background: #fafafa;
  248. }
  249. }
  250. :deep(*) {
  251. .el-form-item {
  252. margin-top: 12px;
  253. margin-bottom: 12px;
  254. }
  255. }
  256. </style>