| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- <template>
- <div class="mind-map-container">
- <div class="toolbar">
- <button v-if="isEdit" @click="addParentNode">添加父节点</button>
- <button v-if="isEdit" @click="addNode">添加节点</button>
- <button v-if="isEdit" @click="addChildNode">添加子节点</button>
- <button v-if="isEdit" @click="removeNode">删除节点</button>
- <button v-if="isEdit" @click="forward">前进</button>
- <button v-if="isEdit" @click="back">回退</button>
- <button @click="zoomIn">放大</button>
- <button @click="zoomOut">缩小</button>
- <button @click="resetZoom">重置缩放</button>
- <button v-if="isEdit" @click="exportToPNG">导出PNG</button>
- <!-- <button @click="exportToSvg">导出SVG</button> -->
- <!-- <button @click="exportToJson">导出JSON</button> -->
- <!-- <button @click="importFromJson">导入JSON</button> -->
- <!-- <button @click="saveData">保存数据</button> -->
- </div>
- <div ref="mindMapContainer" class="mind-map"></div>
- </div>
- </template>
- <script>
- import MindMap from 'simple-mind-map';
- import Export from 'simple-mind-map/src/plugins/Export.js';
- MindMap.usePlugin(Export);
- export default {
- name: 'MindMap',
- props: {
- isEdit: {
- type: Boolean,
- default: false,
- },
- projectId: {
- type: String,
- required: true,
- },
- mindMapJsonData: {
- type: Object,
- required: true,
- },
- },
- data() {
- return {
- mindMap: null,
- activeNodes: [],
- activeNodeIsRoot: false,
- isStart: true,
- isEnd: true,
- scale: 1,
- defaultData: {
- data: {
- uid: '001',
- text: '中心主题',
- },
- children: [],
- },
- };
- },
- mounted() {
- this.initMindMap();
- this.bindKeyEvents();
- },
- beforeDestroy() {
- this.destroyMindMap();
- },
- methods: {
- initMindMap() {
- let rootData = this.mindMapJsonData;
- rootData = { data: rootData?.root || this.defaultData };
- this.mindMap = new MindMap({
- el: this.$refs.mindMapContainer,
- mousewheelAction: 'zoom(放大缩小)、move(上下移动)', // zoom(放大缩小)、move(上下移动)
- // 当mousewheelAction设为move时,可以通过该属性控制鼠标滚动一下视图移动的步长,单位px
- mousewheelMoveStep: 100,
- // 鼠标缩放是否以鼠标当前位置为中心点,否则以画布中心点
- mouseScaleCenterUseMousePosition: true,
- // 当mousewheelAction设为zoom时,或者按住Ctrl键时,默认向前滚动是缩小,向后滚动是放大,如果该属性设为true,那么会反过来
- mousewheelZoomActionReverse: true,
- // 禁止鼠标滚轮缩放,你仍旧可以使用api进行缩放
- disableMouseWheelZoom: false,
- data: rootData.data,
- // theme: { template: 'default' },
- // theme: {
- // name: 'classic',
- // palette: ['#5B8FF9', '#5AD8A6', '#5D7092', '#F6BD16', '#E86452'],
- // bgColor: '#fff',
- // color: '#333',
- // root: {
- // fillColor: '#5B8FF9',
- // },
- // second: {
- // fillColor: '#5AD8A6',
- // color: '#fff',
- // borderColor: '#5AD8A6',
- // borderWidth: 1,
- // fontSize: 14,
- // },
- // },
- readonly: !this.isEdit,
- });
- // this.mindMap.renderer.activeNodeList
- // 激活节点
- this.mindMap.on('node_active', (node, activeNodeList) => {
- this.activeNodes = activeNodeList;
- this.activeNodeIsRoot = activeNodeList.some((p) => p.isRoot);
- });
- this.mindMap.on('back_forward', (index, len) => {
- this.isStart = index <= 0;
- this.isEnd = index >= len - 1;
- });
- this.mindMap.view.translateX(-400);
- this.mindMap.view.translateY(-50);
- if (!this.isEdit) {
- // 监听所有节点点击事件
- this.mindMap.on('node_click', (node) => {
- // console.log('点击的节点:', node?.nodeData?.data?.text || '文本');
- this.$emit('child-click', `${node.uid}`);
- });
- }
- },
- bindKeyEvents() {
- document.addEventListener('keydown', this.handleDeleteKey);
- },
- destroyMindMap() {
- if (this.mindMap) {
- this.mindMap.destroy();
- }
- document.removeEventListener('keydown', this.handleDeleteKey);
- },
- addParentNode() {
- if (!this.mindMap) return;
- if (this.activeNodeIsRoot) {
- this.$message.error('根节点不能添加父节点!');
- return;
- }
- this.mindMap.execCommand('INSERT_PARENT_NODE');
- },
- addNode() {
- if (!this.mindMap) return;
- if (this.activeNodeIsRoot) {
- this.$message.error('只能有一个根节点!');
- return;
- }
- this.mindMap.execCommand(
- 'INSERT_NODE',
- true,
- [],
- {
- uid: Math.random() * 100000,
- text: '请添加内容',
- },
- [
- {
- data: {
- text: '下级节点',
- },
- children: [],
- },
- ],
- );
- },
- addChildNode() {
- if (!this.mindMap) return;
- this.mindMap.execCommand('INSERT_CHILD_NODE');
- },
- removeNode() {
- if (!this.mindMap) return;
- const currentNode = this.mindMap.renderer.activeNodeList[0];
- const nodeData = currentNode?.nodeData.data;
- if (nodeData && (nodeData.type === 0 || nodeData.type === 1)) {
- this.$message.warning('章节节点不能删除!');
- return;
- }
- this.mindMap.execCommand('REMOVE_NODE');
- },
- handleDeleteKey(e) {
- if (e.key === 'Delete' || e.key === 'Del') {
- const currentNode = this.mindMap?.renderer.activeNodeList[0];
- const nodeData = currentNode?.nodeData.data;
- if (nodeData && (nodeData.type === 0 || nodeData.type === 1)) {
- e.preventDefault();
- e.stopPropagation();
- e.returnValue = false;
- this.$message.warning('章节节点不能删除!');
- return false;
- }
- }
- return true;
- },
- forward() {
- this.mindMap.execCommand('FORWARD');
- },
- back() {
- this.mindMap.execCommand('BACK');
- },
- zoomIn() {
- if (!this.mindMap) return;
- this.mindMap.view.setScale((this.scale += 0.1));
- },
- zoomOut() {
- if (!this.mindMap) return;
- this.mindMap.view.setScale((this.scale -= 0.1));
- },
- resetZoom() {
- if (!this.mindMap) return;
- this.mindMap.view.reset();
- },
- exportToPNG() {
- if (!this.mindMap) return;
- this.mindMap.export('png', true, 'mind-map.png');
- },
- exportToSvg() {
- if (!this.mindMap) return;
- this.mindMap.export('svg', true, 'mind-map.svg');
- },
- exportToPDF() {
- if (!this.mindMap) return;
- this.mindMap.export('pdf', true, 'mind-map.pdf');
- },
- exportToJson() {
- if (!this.mindMap) return;
- this.mindMap.export('json', true, 'mind-map.json');
- },
- importFromJson() {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = '.json';
- input.onchange = (e) => {
- const file = e.target.files[0];
- const reader = new FileReader();
- reader.onload = (event) => {
- try {
- const data = JSON.parse(event.target.result);
- this.destroyMindMap();
- this.$nextTick(() => {
- this.initMindMap();
- this.$nextTick(() => {
- this.mindMap.setData(data);
- });
- });
- } catch (error) {
- alert(`导入失败:${error.message}`);
- }
- };
- reader.readAsText(file);
- };
- input.click();
- },
- saveData() {
- // 获取完整数据
- const data = this.mindMap.getData();
- console.info('data', data);
- return data;
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- .mind-map-container {
- .toolbar {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- // justify-content: center;
- margin-bottom: 6px;
- button {
- padding: 6px 12px;
- color: #fff;
- cursor: pointer;
- background-color: #2d9aff;
- border: none;
- border-radius: 4px;
- }
- button:hover {
- background-color: #165dff;
- }
- }
- .mind-map {
- height: calc(100vh - 204px);
- background-color: #fff !important;
- }
- }
- </style>
|