问题分析
应用需要以下JS文件:
jsmind.js- 核心脑图库jsmind.draggable.js- 拖拽功能jsmind.undo.js- 撤销重做功能
解决方案
1. 获取缺失的JS文件
需要下载这些JS文件并放在 static 目录下:
jsmind.js - 核心文件
可以从官方仓库获取:https://github.com/hizzgdev/jsmind
jsmind.draggable.js - 拖拽插件
// jsmind.draggable.js
(function(){if(!window.jsMind) throw new Error('jsMind is not defined');var jm = window.jsMind;jm.draggable = function(jm){this.jm = jm;this.selected_node = null;this.moving = false;this.offset_x = 0;this.offset_y = 0;this.init();};jm.draggable.prototype = {init: function(){var container = this.jm.view.container;container.addEventListener('mousedown', this.mousedown_handler.bind(this));container.addEventListener('mousemove', this.mousemove_handler.bind(this));container.addEventListener('mouseup', this.mouseup_handler.bind(this));container.addEventListener('touchstart', this.touchstart_handler.bind(this));container.addEventListener('touchmove', this.touchmove_handler.bind(this));container.addEventListener('touchend', this.touchend_handler.bind(this));},mousedown_handler: function(e){if(e.button !== 0) return;var node = this.find_node(e);if(!node) return;this.start_drag(node, e.clientX, e.clientY);e.stopPropagation();e.preventDefault();},mousemove_handler: function(e){if(!this.moving) return;this.drag(e.clientX, e.clientY);e.stopPropagation();e.preventDefault();},mouseup_handler: function(e){if(!this.moving) return;this.end_drag();e.stopPropagation();e.preventDefault();},touchstart_handler: function(e){if(e.touches.length !== 1) return;var touch = e.touches[0];var node = this.find_node(touch);if(!node) return;this.start_drag(node, touch.clientX, touch.clientY);e.stopPropagation();e.preventDefault();},touchmove_handler: function(e){if(!this.moving || e.touches.length !== 1) return;var touch = e.touches[0];this.drag(touch.clientX, touch.clientY);e.stopPropagation();e.preventDefault();},touchend_handler: function(e){if(!this.moving) return;this.end_drag();e.stopPropagation();e.preventDefault();},find_node: function(e){var target = e.target;while(target && target !== this.jm.view.container){if(target.nodeName.toLowerCase() === 'jmnode'){var node_id = target.getAttribute('nodeid');return this.jm.get_node(node_id);}target = target.parentNode;}return null;},start_drag: function(node, client_x, client_y){this.selected_node = node;this.moving = true;var view_data = this.jm.view.get_node_view_data(node.id);this.offset_x = client_x - view_data.abs_x;this.offset_y = client_y - view_data.abs_y;this.jm.view.select_node(node);},drag: function(client_x, client_y){if(!this.moving || !this.selected_node) return;var x = client_x - this.offset_x;var y = client_y - this.offset_y;this.jm.view.move_node(this.selected_node, x, y);},end_drag: function(){this.moving = false;this.selected_node = null;}};jm.plugin.draggable = jm.draggable;
})();
jsmind.undo.js - 撤销重做插件
// jsmind.undo.js
(function(){if(!window.jsMind) throw new Error('jsMind is not defined');var jm = window.jsMind;jm.undo = function(jm){this.jm = jm;this.stack = [];this.index = -1;this.max_stack_size = 100;this.init();};jm.undo.prototype = {init: function(){this.jm.add_event_listener(this.event_handler.bind(this));// 绑定快捷键var self = this;document.addEventListener('keydown', function(e){if((e.ctrlKey || e.metaKey) && !e.altKey){if(e.keyCode === 90){ // Ctrl+Zif(e.shiftKey) self.redo();else self.undo();e.preventDefault();}else if(e.keyCode === 89){ // Ctrl+Yself.redo();e.preventDefault();}}});},event_handler: function(type, data){if(type === 'edit' || type === 'add_node' || type === 'remove_node' || type === 'move_node' || type === 'resize'){this.push_snapshot();}},push_snapshot: function(){// 移除当前索引之后的所有记录this.stack = this.stack.slice(0, this.index + 1);// 添加新快照var snapshot = this.jm.get_data();this.stack.push(snapshot);// 限制栈大小if(this.stack.length > this.max_stack_size){this.stack.shift();}this.index = this.stack.length - 1;},undo: function(){if(this.index <= 0) return;this.index--;var snapshot = this.stack[this.index];this.jm.show(snapshot);},redo: function(){if(this.index >= this.stack.length - 1) return;this.index++;var snapshot = this.stack[this.index];this.jm.show(snapshot);},clear: function(){this.stack = [];this.index = -1;}};jm.plugin.undo = jm.undo;
})();
2. 创建项目目录结构
项目结构如下:
your_project/
├── app.py
├── templates/
│ ├── index.html
│ ├── login.html
│ └── register.html
└── static/├── jsmind.js├── jsmind.draggable.js├── jsmind.undo.js├── jsmind.css└── all.min.css
3. 数据库配置
确保MySQL数据库已创建:
CREATE DATABASE zhilian CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
4. 安装依赖
pip install flask flask-login flask-sqlalchemy flask-cors pymysql
5. 启动应用
python app.py
验证解决方案
- 访问
http://localhost:5000 - 注册新用户并登录
- 创建新的脑图文件
- 测试脑图的各项功能(添加节点、编辑、拖拽、撤销重做等)