下载代码
我下载的版本是:v24.7.1版本,代码下载之后解压。编辑器打开,并作如下修改
文件地址:src/main/webapp/index.html
文件中做如下修改
<script id="geBootstrap" type="text/javascript">
if (urlParams['dev'] == '1') {
// Used to request grapheditor/mxgraph sources in dev mode
var mxDevUrl = '';
// Used to request draw.io sources in dev mode
var drawDevUrl = '';
var geBasePath = 'js/grapheditor';
var mxBasePath = 'mxgraph/src';
// 如果开发模式,默认指向本地文件(增加这一段开始)
if(document.host === 'loacalhost') {
mxDevUrl = document.location.protocol
drawDevUrl = document.location.protocol
}
// 如果开发模式,默认指向本地文件(增加这一段结尾)
}
</script>
本地运行
可以使用以下两种方式运行代码。
1. 全局安装serve服务
npm install -g serve
安装之后,进入到项目src/main/webapp
,执行如下命令:
serve
2. 自己写一个node服务(我是用的这种方式)
- 命令行打开前端项目所在路径
src/main/webapp
- 执行如下操作,我安装
http-proxy-middleware
,是因为项目中请求的接口跨域了。
npm init -y
npm install express http-proxy-middleware -D
- 在
webapp
目录下,创建serve.js
文件
// serve.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const path = require('path');
const app = express();
// 设置代理
app.use('/api', createProxyMiddleware({
target: 'xxxxxxxxx.com', // 允许跨域请求的域名
changeOrigin: true,
}));
// 提供静态文件服务,假设你的静态文件在 public 目录中
app.use(express.static(path.join(__dirname)));
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在运行在 http://localhost:${PORT}`);
});
- 在
package.json
文件中配置执行脚本
"scripts": {
"dev": "node serve.js"
},
通过以上两种方式启动项目之后,直接访问你本地的服务链接,修改代码是不生效的,需要在链接后面传递一个dev=1
的参数,例如:http://localhost:300?dev=1
网页标题和流程图标题
- 由于我的
drawio
编辑器是从外部创建了项目跳转进来的,所以我这里禁止了从编辑器里面修改标题,而是通过url
参数将标题传递进来。 - 要修改的文件:
src/main/webapp/js/diagramly/LocalFile.js
LocalFile = function(ui, data, title, temp, fileHandle, desc, editable) {
DrawioFile.call(this, ui, data);
// 通过urlParams('title')获取url地址栏中的标题参数,解码之后赋值给标题
this.title = decodeURIComponent(urlParams['title']);
this.mode = (temp) ? null : App.MODE_DEVICE;
this.fileHandle = fileHandle;
this.desc = desc;
this.editable = editable;
};
- 禁止编辑器中修改流程图标题,文件地址:
src/main/webapp/js/diagramly/LocalFile.js
// 由return true改为return false即可
LocalFile.prototype.isRenamable = function() {
return false;
};
实现保存到私有数据库和初始化流程图功能
保存功能实现
由于我的需求不需要它原来的那些保存功能,只需要点击“保存”按钮,直接调用后端接口保存到私有数据库,所以我做了如下改动:
要修改的文件:src/main/webapp/js/diagramly/App.js
App.prototype.saveFile = function(forceDialog, success){
// 删除它原有的js代码,只增加如下代码
var file = this.getCurrentFile();
if (file != null) {
this.save(file.getTitle());
}
}
App.prototype.save = function(forceDialog, success){
// 删除它原有的js代码,在这里做调用后端保存数据接口的操作
// 获取当前file文件对象
var temp = this.getCurrentFile()
// 更新file对象中的数据,必须要执行,不然拿不到最新的数据
temp.updateFileData()
// 加载动画
this.spinner.spin(document.body, mxResources.get('saving'))
// 伪代码,调用接口保存
this.fetch({
url: 'xxx',
sucess: () => {
// 更改保存状态,这个也要有
this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('allChangesSaved')));
// 隐藏加载动画
this.spinner.stop()
}
})
}
初始化编辑器
- 每次我的编辑器打开,在它的
url
中都会有一个uuid
用来获取当前保存在数据库中的流程图信息。 - 通过
uuid
调用后端接口,获取到流程图信息之后,将其加载到编辑器中进行编辑。
要修改的文件:src/main/webapp/js/diagramly/App.js
。以下代码是通过setTimeout
模拟接口获取数据
if (this.editor.isChromelessView()) {
//.....
} else if(urlParams['uuid']) {
this.spinner.spin(document.body, mxResources.get('loading'))
setTimeout(() => {
const mockFile = {
title: '流程图.drawio',
data: '<mxfile host="localhost" modified="2021-09-02T09:50:39.574Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36" etag="PyBU88KGLdBE5FU7fpPf" version="@DRAWIO-VERSION@" type="device"><diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">7VhZc5swEP41PLbD4WDnsThx0tbNpE3aTB9lkIVqwVIhfOTXdzHCgPH4aOscnvjBo12WlbTft4dtOP1ofiVJEn6BgArDNoO54VwYtn3WNfE7VywKhduzCwWTPChUVqW4449UK/V7LOMBTRuGCkAonjSVPsQx9VVDR6SEWdNsDKK5a0IYbSnufCLa2gceqLDQ9uxupb+mnIXlzpZ7XjyJSGmsb5KGJIBZTeVcGk5fAqhiFc37VOSxK+Py8HHxIIYT9+rT1/Q3+e59vr/58a5wNjjkldUVJI3V/3WtsZwSkel46buqRRlACVkc0NyJaTheqCKBSwuXv6hSCw04yRSgCqQKgUFMxBAg0XZjiJU2s3KZxsGHHFiURwL8SaEacCH0Hihp+x5KqZIwWWGXO1gBkRsLMqLCI/6ELQ/aBwESH8UQ09xVgGTQd6kOd1lpvT1jqzFIIZM+3WLnaIoTyeg2f25hl5+vxlON3BWFiCq5QANJBVF82iQz0TnBVnYV7rjQ0B9AA6dFA5GNx4vHcAsbciRmIVf0LiHLkMywfjQZUkceL+cxQdJU47YD1sNgmVKp6HxrIMunrs5mXc56WpxVtcEqEz6s1YWOeaTQd1qh/4nl8i0J/yUJ3T2TsOxRO7NQk6Ukxt5JqT3dAsd7VSYwHqd4sHXqrDb8ezadtdh0Ay0y7cbjVdPtmcjUPYxL1ovnktvi0pBEiWG7AsPgjSSuWL5KRMYYkgXdxYYzaNeuEKJRlu5uFw2McwYNSMRFHq9rKqZUcZ9saCpEcIb7XvgIN5WbyYNb8pih5FbS/ZKsWH+P2Gy6zWazkuvdxtzQbXrH6jbdFqa3iF4BnW2KJb4n1vEdew2E8+du+b19ivSrLsFP3vHLarq75Vt7lmnNHvO9g58GgV7+FHC+10z5NgYch2H2qQ0C5QlrfPIyMWpPAqNMxstBADL1NglsmwTsTU3oSSeBshDWQP1GE8IRTHN4inPA+i//jvPcc0BZKZoQiDymtllk2IlhsD6LOdbxMECx+m+2KIXVH9zO5R8=</diagram></mxfile>'
}
const file = new LocalFile(this, mockFile.data, mockFile.title, this.mode);
this.fileLoaded(file);
this.spinner.stop()
}, 2000)
} else if() {
//......
}
菜单栏的修改
删除菜单栏中的“帮助”菜单
要修改的文件:src/main/webapp/js/grapheditor/Menus.js
,将下面数组的help
删除
// 根据自己需求,任意修改删除
Menus.prototype.defaultMenuItems = ['file', 'edit', 'view', 'arrange', 'extras', 'help']
精简“文件”菜单
以下所有的操作,将在src/main/webapp/js/diagramly/Menus.js
文件中进行
1. 删除“创建副本…”按钮
将以下代码删除或注释即可
editorUi.actions.addAction('makeCopy...', mxUtils.bind(this, function() {
var file = editorUi.getCurrentFile();
if (file != null) {
var title = editorUi.getCopyFilename(file);
if (file.constructor == DriveFile) {
var dlg = new CreateDialog(editorUi, title, mxUtils.bind(this, function(newTitle, mode) {
if (mode == '_blank') {
editorUi.editor.editAsNew(editorUi.getFileData(), newTitle);
} else {
// Mode is "download" if Create button is pressed, means use Google Drive
if (mode == 'download') {
mode = App.MODE_GOOGLE;
}
if (newTitle != null && newTitle.length > 0) {
if (mode == App.MODE_GOOGLE) {
if (editorUi.spinner.spin(document.body, mxResources.get('saving'))) {
// Saveas does not update the file descriptor in Google Drive
file.saveAs(newTitle, mxUtils.bind(this, function(resp) {
// Replaces file descriptor in-place and saves
file.desc = resp;
// Makes sure the latest XML is in the file
file.save(false, mxUtils.bind(this, function() {
editorUi.spinner.stop();
file.setModified(false);
file.addAllSavedStatus();
}), mxUtils.bind(this, function(resp)
{
editorUi.handleError(resp);
}));
}), mxUtils.bind(this, function(resp) {
editorUi.handleError(resp);
}));
}
} else {
editorUi.createFile(newTitle, editorUi.getFileData(true), null, mode);
}
}
}
}), mxUtils.bind(this, function() {
editorUi.hideDialog();
}), mxResources.get('makeCopy'), mxResources.get('create'), null, null, true, null, true, null, null, null, null, editorUi.editor.fileExtensions);
editorUi.showDialog(dlg.container, 420, 380, true, true);
dlg.init();
} else {
// Creates a copy with no predefined storage
editorUi.editor.editAsNew(this.editorUi.getFileData(true), title);
}
}
}));
2. 删除“共享…”按钮
将以下代码删除或注释即可
this.editorUi.actions.addAction('share...', mxUtils.bind(this, function() {
try {
var file = editorUi.getCurrentFile();
if (file != null) {
file.share();
}
} catch (e) {
editorUi.handleError(e);
}
})).isEnabled = isGraphEnabled;
3. 删除“打开最近使用文件”按钮
将以下代码删除或注释即可
this.put('openRecent', new Menu(function(menu, parent) {
var recent = editorUi.getRecent();
if (recent != null) {
for (var i = 0; i < recent.length; i++) {
(function(entry) {
var modeKey = entry.mode;
// Google and oneDrive use different keys
if (modeKey == App.MODE_GOOGLE) {
modeKey = 'googleDrive';
} else if (modeKey == App.MODE_ONEDRIVE) {
modeKey = 'oneDrive';
}
menu.addItem(entry.title + ' (' + mxResources.get(modeKey) + ')', null, function() {
editorUi.loadFile(entry.id);
}, parent);
})(recent[i]);
}
menu.addSeparator(parent);
}
menu.addItem(mxResources.get('reset'), null, function() {
editorUi.resetRecent();
}, parent);
}));
精简“其它”菜单
以下所有的操作,将在src/main/webapp/js/diagramly/Menus.js
文件中进行
1. 删除“语言”按钮
将以下代码删除或注释即可
this.put('language', new Menu(mxUtils.bind(this, function(menu, parent) {
var currentLanguage = mxLanguage;
if (urlParams['lang'] == null && isLocalStorage) {
currentLanguage = mxSettings.settings.language;
}
var addLangItem = mxUtils.bind(this, function (id) {
var lang = (id == '') ? mxResources.get('automatic') : mxLanguageMap[id];
var item = null;
if (lang != '') {
item = menu.addItem(lang, null, mxUtils.bind(this, function() {
editorUi.setAndPersistLanguage(id);
editorUi.alert(mxResources.get('restartForChangeRequired'));
}), parent);
if (id == currentLanguage || (id == '' && currentLanguage == null)) {
menu.addCheckmark(item, Editor.checkmarkImage);
}
}
return item;
});
addLangItem('');
menu.addSeparator(parent);
// LATER: Sort menu by language name
for(var langId in mxLanguageMap) {
addLangItem(langId);
}
})));
2. 删除“主题”按钮
将以下代码删除或注释即可
// 全部注释或删除
this.put('theme', new Menu(mxUtils.bind(this, function(menu, parent) {
var theme = (urlParams['sketch'] == '1') ? 'sketch' : mxSettings.getUi();
var autoItem = menu.addItem(mxResources.get('automatic'), null, function() {
editorUi.setCurrentTheme('');
}, parent);
var item = menu.addItem(mxResources.get('classic'), null, function() {
editorUi.setCurrentTheme((!Editor.isDarkMode()) ? 'kennedy' : 'dark');
}, parent);
var themeFound = false;
if (theme == 'kennedy' || theme == 'dark') {
menu.addCheckmark(item, Editor.checkmarkImage);
themeFound = true;
}
for (var i = 0; i < Editor.themes.length; i++) {
(mxUtils.bind(this, function(key) {
item = menu.addItem(mxResources.get((key == 'min') ? 'minimal' : key), null, function() {
editorUi.setCurrentTheme(key);
}, parent);
if (theme == key) {
menu.addCheckmark(item, Editor.checkmarkImage);
themeFound = true;
}
})(Editor.themes[i]));
}
if (!themeFound) {
menu.addCheckmark(autoItem, Editor.checkmarkImage);
}
})));
3. 删除“连接时复制”按钮
将以下代码删除或注释即可
// 全部注释或删除
this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-'], parent)
4. 删除“自动保存”按钮
将以下代码删除或注释即可
// 全部注释或删除
this.addMenuItems(menu, ['autosave'], parent);
保存或导出弹框的修改
主要是删除上图中的Google、OneDrive、Dropbox、GitHub、GitLab、下载、在新窗口中打开
文件地址:src/main/webapp/js/diagramly/Dialog.js
,删除或注释如下代码
/**
browser: 浏览器
device:设备
download:下载
_blank:在新窗口打开
可以根据自己的需求删除数组中的值即可
*/
var localServices = ['browser', 'device', 'download', '_blank'];
// 我的需求是保存设备,所以我的数组修改后为
var localServices = ['device'];
// 下面代码是它自带的线上保存的一些选项,根据自己需求注释或删除即可
// Google Drive - My Drive
addStorageEntry(App.MODE_GOOGLE, mxResources.get('myDrive'), 'root', null, null, 'root');
// Google Drive - 选择文件夹...
addStorageEntry(App.MODE_GOOGLE, null, null, null, null, 'pick');
if (editorUi.oneDrive != null) {
// OneDrive - My Files
addStorageEntry(App.MODE_ONEDRIVE, mxResources.get('myFiles'), OneDriveFile.prototype.getIdOf(editorUi.oneDrive.rootId), null, null, 'root');
// OneDrive - 选择文件夹...
addStorageEntry(App.MODE_ONEDRIVE, null, null, null, null, 'pick');
}
if (editorUi.dropbox != null) {
// Dropbox - Apps/drawio-diagrams
addStorageEntry(App.MODE_DROPBOX, 'Apps' + editorUi.dropbox.appPath);
}
// GitHub - 选择文件夹...
addStorageEntry(App.MODE_GITHUB, null, null, null, null, 'pick');
// GitLab - 选择文件夹...
addStorageEntry(App.MODE_GITLAB, null, null, null, null, 'pick');
修复“导出为”PNG、JPEG等功能报错问题
源码下载之后,它的“导出为”功能是有问题的,会报错,作如下修改:
文件地址:src/main/webapp/js/diagramly/Dialog.js
var firstTypeOption = typeSelect.options[0]
// 将上面的代码改为下面的即可
var firstTypeOption = typeSelect ? typeSelect.options[0] : {}
打包
打包需要使用java
环境和ant
环境,这两个环境如何安装大家可以自己百度一下。
修改一下自己的package.json
文件,这个文件在前端我们运行项目的时候,已经创建好了,做出如下改动:
"scripts": {
"dev": "node serve.js",
"build": "cd ../../../etc/build && ant && node ../../src/main/webapp/build.js"
}
cd ../../../etc/build
:由于我的package.json
是初始化在src/main/webapp
目录下的,所以在执行打包时,要cd
切换到etc/build
的目录中ant
:执行真实的ant打包命令node ../../src/main/webapp/build.js
:在src/main/webapp
目录下创建一个build.js
文件,js文件内容如下:
// build.js
const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, 'js/stencils.min.js');
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error('读取文件时出错:', err);
return;
}
const updatedData = data.replace(/f\['([^']*?)\\([^']*?)'\]/g, (match) => {
return match.replace(/\\/g, '\\\\');
});
// 将更新后的内容写回文件
fs.writeFile(filePath, updatedData, 'utf8', (err) => {
if (err) {
console.error('写入文件时出错:', err);
return;
}
console.log('打包成功啦~');
});
});
这样做的目的是,我的项目在打包完成之后,生成的stencils.min.js
文件中的\
不是转义后的\\
导致我的代码报错,所以我创建了build.js
文件,用来将打包后的stencils.min.js
中的\
转换为\\
然后,执行打包命令npm run build
。打包之后,直接将链接中的dev=1
去掉就可以看效果了。
在执行打包命令之前需要注意
在执行npm run build
命令之前需要做一些修改:
- 在
etc/build/build.xml
文件中,在所有的<concat>
标签上加上encoding="UTF-8"
。例如:
<concat destfile="${basedir}/../../build/shapes.js" fixlastline="yes" append="no" encoding="UTF-8">
<fileset dir="${war.dir}/shapes" includes="**/*.js"/>
<fileset dir="${war.dir}/stencils" includes="**/*.js"/>
</concat>
如果后面还有改动的地方,我会继续写的😔…