drawio的二次开发

下载代码

我下载的版本是: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>

如果后面还有改动的地方,我会继续写的😔…

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我吃小月饼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值