draw.io是一款免费的开源绘图软件,主要用于创建各种类型的图表、流程图、组织结构图、UML图和网络拓扑图等
某一天,作为牛马的我被领导叫到了办公室,说小伙子,有一个工作需要你来做一下,好好干,干好了面包牛奶要啥有啥😯。 牛马像是被换了新的鞭子抽打,立马感觉到了浑身有劲,干就完了!
具体内容就是不知道哪里来的大聪明,提出了需要对drawio进行二次开发的需求,在其中需要根据具体业务需要进行一番修改。了解需求的我立马就开始吭哧吭哧的劳作。
一、下载代码
二开源码可点击查看 版本是真忘记了😅
├── CITATION.cff
├── CODE_OF_CONDUCT.md
├── ChangeLog
├── LICENSE
├── README.md
├── SECURITY.md
├── VERSION
├── docs
│ └── code-review.md
├── etc
├── src
│ └── main
│ ├── java
│ └── webapp
└── teams.html
其中src/main/webapp 是需要改的前端代码
├── META-INF
├── WEB-INF
├── clear.html
├── connect
├── dropbox.html
├── export-fonts.css
├── export3.html
├── favicon.ico
├── github.html
├── gitlab.html
├── images
├── img
├── index.html
├── js
├── math
├── monday-app-association.json
├── mxgraph
├── onedrive3.html
├── open.html
├── package.json
├── plugins
├── resources
├── service-worker.js
├── service-worker.js.map
├── shapes
├── shortcuts.svg
├── stencils
├── styles
├── teams.html
├── templates
├── vsdxImporter.html
├── workbox-12cca165.js
├── workbox-12cca165.js.map
├── workbox-4768a546.js
├── workbox-4768a546.js.map
├── workbox-50a29d49.js
├── workbox-50a29d49.js.map
├── workbox-5490a1bd.js
├── workbox-5490a1bd.js.map
├── workbox-7a2a8380.js
├── workbox-7a2a8380.js.map
├── workbox-99ba3a23.js
├── workbox-99ba3a23.js.map
├── workbox-9fe249eb.js
├── workbox-9fe249eb.js.map
├── workbox-bed83ea8.js
├── workbox-bed83ea8.js.map
├── workbox-d4d5b410.js
├── workbox-d4d5b410.js.map
├── workbox-dfbb910f.js
├── workbox-dfbb910f.js.map
├── workbox-eb9c7348.js
├── workbox-eb9c7348.js.map
├── workbox-f163abaa.js
├── workbox-f163abaa.js.map
├── workbox-fa8c4ce5.js
└── workbox-fa8c4ce5.js.map
二、本地运行
2.1 修改index.html
文件路径:src/mian/webapp/index.html (269行)
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.location.protocol == 'file:')
{
// Forces includes for dev environment in node.js
mxForceIncludes = true;
}
// ------增加如下代码--------------
// 本地开发环境
if (location.hostname == "localhost" || location.hostname == "127.0.0.1") {
drawDevUrl = `http://${location.host}/`;
geBasePath = `http://${location.host}/js/grapheditor`;
mxBasePath = `http://${location.host}/mxgraph`;
mxForceIncludes = true;
}
// ------------------------------
mxForceIncludes = false;
mxscript(drawDevUrl + 'js/PreConfig.js');
mxscript(drawDevUrl + 'js/diagramly/Init.js');
mxscript(geBasePath + '/Init.js');
mxscript(mxBasePath + '/mxClient.js');
// 其他代码...
}
2.2 本地安装serve
在终端前端项目的根目录下新建package.json文件 并安装serve依赖
➜ drawio: cd src/main/webapp
➜ webapp: npm init -y
➜ webapp: npm i serve -D
修改package.json
{
"name": "webapp",
"version": "1.0.0",
"description": "",
"main": "service-worker.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
// 新增
"dev": "serve -p 8888"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"serve": "^14.2.3"
}
}
命令行执行 npm run dev 打开浏览器 http://localhost:8888 即可看到drawio页面
这样启动修改代码不会同步更新 设置同步更新的方法
- 在访问链接后加上url参数 ?dev=1 即访问 http://localhost:8888?devb=1
- 修改index.html (39行)设置默认开发环境启动
var urlParams = (function(){
var result = new Object();
var params = window.location.search.slice(1).split('&');
for (var i = 0; i < params.length; i++){
var idx = params[i].indexOf('=');
if (idx > 0){
result[params[i].substring(0, idx)] = params[i].substring(idx + 1);
}
}
// ----------新增如下代码-------
if (!result.hasOwnProperty('dev')) {
result['dev'] = 1
}
// ----------新增代码结束-------
return result;
})();
三、vue项目使用
3.1 iframe嵌入二开后的drawio页面
<template>
<div style="width: 100%;height: 100%;">
<!-- 使用iframe嵌入外部页面 -->
<iframe
:src="iframeSrc"
width="100%"
height="100%"
frameborder="0"
sandbox="allow-scripts allow-same-origin"
></iframe>
</div>
</template>
<script>
export default {
computed: {
iframeSrc() {
const hostname = window.location.hostname;
const isDev = hostname.includes('localhost');
let baseUrl = window.location.origin + '/drawio/index.html';
if (isDev) {
baseUrl = 'http://localhost:8888/drawio/index.html';
}
if (this.isCreate) {
return (
baseUrl +
'?pro_id=' +
this.$route.query.pro_id +
'&pro_name=' +
encodeURIComponent(this.$route.query.pro_name) +
'&task_name=' +
encodeURIComponent(this.$route.query.task_name)
);
}
return (
baseUrl +
'?pro_id=' +
this.$route.query.pro_id +
'&pro_name=' +
encodeURIComponent(this.$route.query.pro_name) +
'&id=' +
this.$route.query.id +
'&drawio_url=' +
this.$route.query.drawio_url
);
},
},
}
</script>
四、初始化编辑器
4.1 设置默认语言、模式等
修改文件PreConfig.js 文件路径:src/main/webapp/js/PreConfig.js
// 新增如下代码
urlParams.lang = 'zh'; // 国际化默认中文
urlParams['browser'] = 1;
urlParams['gapi'] = 0;
urlParams['db'] = 0;
urlParams['od'] = 0;
urlParams['tr'] = 0;
urlParams['gh'] =0;
urlParams['gl'] =0;
urlParams['mode'] = 'browser';
4.2 修改语言选项
修改语言选项只有 英文与简体中文
修改文件Init.js (124行) 文件路径:src/main/webapp/js/diagramly/Init.js
// 注释或删除不需要的选项
window.mxLanguageMap = window.mxLanguageMap ||
{
// 'i18n': '',
// 'id' : 'Bahasa Indonesia',
// 'ms' : 'Bahasa Melayu',
// 'bs' : 'Bosanski',
// 'bg' : 'Bulgarian',
// 'ca' : 'Català',
// 'cs' : 'Čeština',
// 'da' : 'Dansk',
// 'de' : 'Deutsch',
// 'et' : 'Eesti',
'en' : 'English',
// 'es' : 'Español',
// 'eu' : 'Euskara',
// 'fil' : 'Filipino',
// 'fr' : 'Français',
// 'gl' : 'Galego',
// 'it' : 'Italiano',
// 'hu' : 'Magyar',
// 'lt' : 'Lietuvių',
// 'lv' : 'Latviešu',
// 'nl' : 'Nederlands',
// 'no' : 'Norsk',
// 'pl' : 'Polski',
// 'pt-br' : 'Português (Brasil)',
// 'pt' : 'Português (Portugal)',
// 'ro' : 'Română',
// 'fi' : 'Suomi',
// 'sv' : 'Svenska',
// 'vi' : 'Tiếng Việt',
// 'tr' : 'Türkçe',
// 'el' : 'Ελληνικά',
// 'ru' : 'Русский',
// 'sr' : 'Српски',
// 'uk' : 'Українська',
// 'he' : 'עברית',
// 'ar' : 'العربية',
// 'fa' : 'فارسی',
// 'th' : 'ไทย',
// 'ko' : '한국어',
// 'ja' : '日本語',
'zh' : '简体中文',
// 'zh-tw' : '繁體中文'
};
4.3 删除共享、帮助、同步以及创建副本等功能
- 删除共享
修改文件App.js (6045行) 文件路径:src/main/webapp/js/diagramly/App.js
注释或删除如下代码 可删除右上角分享按钮
// // Share
// if (this.getServiceName() == 'draw.io' &&
// urlParams['embed'] != '1' &&
// !this.isStandaloneApp())
// {
// if (file != null)
// {
// if (this.shareButton == null && Editor.currentTheme != 'atlas')
// {
// this.shareButton = document.createElement('button');
// this.shareButton.className = 'geBtn geShareBtn';
// this.shareButton.style.display = 'inline-block';
// this.shareButton.style.position = 'relative';
// this.shareButton.style.backgroundImage = 'none';
// this.shareButton.style.padding = '2px 10px 0 10px';
// this.shareButton.style.marginTop = '-10px';
// this.shareButton.style.cursor = 'pointer';
// this.shareButton.style.height = '32px';
// this.shareButton.style.minWidth = '0px';
// this.shareButton.style.top = '-2px';
// this.shareButton.setAttribute('title', mxResources.get('share'));
// var icon = document.createElement('img');
// icon.className = 'geInverseAdaptiveAsset';
// icon.setAttribute('src', this.shareImage);
// icon.setAttribute('align', 'absmiddle');
// icon.style.marginRight = '4px';
// icon.style.marginTop = '-3px';
// this.shareButton.appendChild(icon);
// if (Editor.currentTheme != 'atlas')
// {
// icon.style.filter = 'invert(100%)';
// }
// mxUtils.write(this.shareButton, mxResources.get('share'));
// mxEvent.addListener(this.shareButton, 'click', mxUtils.bind(this, function()
// {
// this.actions.get('share').funct();
// }));
// this.buttonContainer.appendChild(this.shareButton);
// }
// if (this.shareButton != null)
// {
// this.shareButton.style.display = (Editor.currentTheme == 'simple' ||
// Editor.currentTheme == 'sketch' || Editor.currentTheme == 'min')
// ? 'none' : 'inline-block';
// // Hides parent element if empty for flex layout gap to work
// if (Editor.currentTheme == 'simple' ||
// Editor.currentTheme == 'sketch')
// {
// this.shareButton.parentNode.style.display =
// (this.shareButton.parentNode.clientWidth == 0)
// ? 'none' : '';
// }
// }
// }
// else if (this.shareButton != null)
// {
// this.shareButton.parentNode.removeChild(this.shareButton);
// this.shareButton = null;
// }
// // Fetch notifications
// if (urlParams['extAuth'] != '1' &&
// Editor.currentTheme != 'atlas') //Disable notification with external auth (e.g, Teams app)
// {
// this.fetchAndShowNotification('online', this.mode);
// }
// }
// else
// {
// if (urlParams['notif'] != null) //Notif for embed mode
// {
// this.fetchAndShowNotification(urlParams['notif']);
// }
// // Hides button container if empty for flex layout gap to work
// if (this.isStandaloneApp() &&
// (Editor.currentTheme == 'simple' ||
// Editor.currentTheme == 'sketch'))
// {
// this.buttonContainer.style.display =
// (this.buttonContainer.clientWidth == 0)
// ? 'none' : '';
// }
// }
修改文件Menus.js (3504行) 文件路径:src/main/webapp/js/diagramly/Menus.js
注释或删除如下代码 可删除点击文件菜单内的分享
// 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;
- 删除帮助
修改文件Menus.js (33行) 文件路径:src/main/webapp/js/grapheditor/Menus.js
/**
* Sets the default font size.
*/
// Menus.prototype.defaultMenuItems = ['file', 'edit', 'view', 'arrange', 'extras', 'help'];
Menus.prototype.defaultMenuItems = ['file', 'edit', 'view', 'arrange', 'extras'];
- 删除同步
修改文件Menus.js (582行) 文件路径:src/main/webapp/js/diagramly/Menus.js
注释或删除如下代码 可删除点击文件菜单内的同步菜单
// var action = editorUi.actions.addAction('synchronize', function()
// {
// editorUi.synchronizeCurrentFile(DrawioFile.SYNC == 'none');
// }, null, null, 'Alt+Shift+S');
- 删除创建副本
修改文件Menus.js (3363行) 文件路径:src/main/webapp/js/diagramly/Menus.js
// 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);
// }
// }
// }));
修改文件EditorUi.js 文件路径:src/main/webapp/js/diagramly/EditorUi.js
// 18638行
// this.actions.get('makeCopy').setEnabled(!restricted);
// 18817行
// this.actions.get('makeCopy').setEnabled(file != null && !file.isRestricted());
五、自定义编辑器
5.1 修改保存至弹窗内容
因为对我们的需求来说,只需要一个自定义的保存和保存至Device设备就可以了。所以这里会删除其他的,并且新增加一种方式。
- 删除对应的图标,新增自定义图标
修改文件Dialogs.js (206行) 文件路径:src/main/webapp/js/diagramly/Dialogs.js
var addButtons = function()
{
count = 0;
/*
* 新增自定义图标
* 需要在国际化语言文件内新增对应的自定义功能的多语言
* 并且需要在App.js内新增新的mode App.MODE_NEW_BTN
*/
addLogo(IMAGE_PATH + '/osa_drive-harddisk.png', mxResources.get('new btn'), App.MODE_NEW_BTN);
// 删除无用的图标
// if (typeof window.DriveClient === 'function')
// {
// addLogo(IMAGE_PATH + '/google-drive-logo.svg', mxResources.get('googleDrive'), App.MODE_GOOGLE, 'drive');
// }
// if (typeof window.OneDriveClient === 'function')
// {
// addLogo(IMAGE_PATH + '/onedrive-logo.svg', mxResources.get('oneDrive'), App.MODE_ONEDRIVE, 'oneDrive');
// }
if (urlParams['noDevice'] != '1')
{
addLogo(IMAGE_PATH + '/osa_drive-harddisk.png', mxResources.get('device'), App.MODE_DEVICE);
}
// if (isLocalStorage && (urlParams['browser'] == '1' || urlParams['offline'] == '1'))
// {
// addLogo(IMAGE_PATH + '/osa_database.png', mxResources.get('browser'), App.MODE_BROWSER);
// }
// if (typeof window.DropboxClient === 'function')
// {
// addLogo(IMAGE_PATH + '/dropbox-logo.svg', mxResources.get('dropbox'), App.MODE_DROPBOX, 'dropbox');
// }
// if (editorUi.gitHub != null)
// {
// addLogo(IMAGE_PATH + '/github-logo.svg', mxResources.get('github'), App.MODE_GITHUB, 'gitHub');
// }
// if (editorUi.gitLab != null)
// {
// addLogo(IMAGE_PATH + '/gitlab-logo.svg', mxResources.get('gitlab'), App.MODE_GITLAB, 'gitLab');
// }
};
- 修改相关的后续方法
修改文件Dialogs.js (311行) 文件路径:src/main/webapp/js/diagramly/Dialogs.js
if (editorUi.mode == App.MODE_NEW_BTN) {
logo.src = IMAGE_PATH + '/onedrive-connector-atlas.png';
buttons.style.paddingBottom = '10px';
buttons.style.paddingTop = '30px';
service = mxResources.get('new btn')
} else {
logo.src = IMAGE_PATH + "/osa_drive-harddisk.png";
buttons.style.paddingBottom = "10px";
buttons.style.paddingTop = "30px";
service = mxResources.get("device");
}
修改文件Dialogs.js (403行) 文件路径:src/main/webapp/js/diagramly/Dialogs.js
if (editorUi.mode == App.MODE_NEW_BTN) {
storage = mxResources.get("new btn");
} else if (editorUi.mode == App.MODE_DEVICE) {
storage = mxResources.get("device");
}
修改文件App.js (4113行) 文件路径:src/main/webapp/js/diagramly/App.js
// 修改pickFile 方法 要不点击打开现有绘图会无反应
App.prototype.pickFile = function(mode)
{
try
{
mode = (mode != null) ? mode : this.mode;
if (mode == App.MODE_GOOGLE)
{
if (this.drive != null && typeof(google) != 'undefined' && typeof(google.picker) != 'undefined')
{
this.drive.pickFile();
}
else
{
this.openLink('https://drive.google.com');
}
}
else
{
var peer = this.getServiceForName(mode);
if (peer != null)
{
peer.pickFile();
}
else if (mode == App.MODE_DEVICE && EditorUi.nativeFileSupport)
{
// ...
}
// 修改此行 !!!!!!
// else if (mode == App.MODE_DEVICE && Graph.fileSupport){
else if ([App.MODE_DEVICE, App.MODE_NEW_BTN].includes(mode) && Graph.fileSupport){
// ....
}
};
5.2 修改保存弹窗
我们的需求是
1、首先保存类型在自定义的情况下,只支持xml类型的文件
修改文件Dialogs.js (5005行) 文件路径:src/main/webapp/js/diagramly/Dialogs.js
typeSelect = FilenameDialog.createFileTypes(editorUi, saveAsInput,
editorUi.editor.diagramFileTypes);
typeSelect.style.boxSizing = 'border-box';
typeSelect.style.width = '100%';
// 新增如下判断
if (editorUi.mode != App.MODE_NEW_BTN) {
typeSelect.disabled = true;
}
right.appendChild(typeSelect);
2、保存位置不需要其他的 只需要设备、下载、以及自定义三种方式
修改文件App.js (4545行) 文件路径:src/main/webapp/js/diagramly/App.js
// 修改 saveFile 方法 取消根据类型判断展示不同弹窗,而是直接显示选择弹窗
App.prototype.saveFile = function (forceDialog, success) {
var file = this.getCurrentFile();
var prev = this.mode;
if (file != null) {
// FIXME: Invoke for local files
var done = mxUtils.bind(this, function () {
if (EditorUi.enableDrafts) {
file.removeDraft();
}
if (this.getCurrentFile() != file && !file.isModified()) {
// Workaround for possible status update while save as dialog is showing
// is to show no saved status for device files
if (file.getMode() != App.MODE_DEVICE) {
this.editor.setStatus(
mxUtils.htmlEntities(mxResources.get("allChangesSaved"))
);
} else {
this.editor.setStatus("");
}
}
if (success != null) {
success();
}
});
var filename =
file.getTitle() != null ? file.getTitle() : this.defaultFilename;
var saveFunction = mxUtils.bind(
this,
function (name, mode, input, folderId) {
if (name != null && name.length > 0) {
// Handles special case where PDF export is detected
if (/(.pdf)$/i.test(name)) {
this.confirm(
mxResources.get("didYouMeanToExportToPdf"),
mxUtils.bind(this, function () {
this.hideDialog();
this.actions.get("exportPdf").funct();
}),
mxUtils.bind(this, function () {
input.value = name.split(".").slice(0, -1).join(".");
input.focus();
if (
mxClient.IS_GC ||
mxClient.IS_FF ||
document.documentMode >= 5
) {
input.select();
} else {
document.execCommand("selectAll", false, null);
}
}),
mxResources.get("yes"),
mxResources.get("no")
);
} else {
this.hideDialog();
if (prev == null && mode == App.MODE_DEVICE) {
if (file != null && EditorUi.nativeFileSupport) {
this.showSaveFilePicker(
mxUtils.bind(this, function (fileHandle, desc) {
file.fileHandle = fileHandle;
file.mode = App.MODE_DEVICE;
file.title = desc.name;
file.desc = desc;
this.setMode(App.MODE_DEVICE);
this.save(desc.name, done);
}),
mxUtils.bind(this, function (e) {
if (e.name != "AbortError") {
this.handleError(e);
}
}),
this.createFileSystemOptions(name)
);
} else {
this.setMode(App.MODE_DEVICE);
this.save(name, done);
}
} else if (mode == "download") {
var tmp = new LocalFile(this, null, name);
tmp.save();
} else if (mode == "_blank") {
window.openFile = new OpenFile(function () {
window.openFile = null;
});
// Do not use a filename to use undefined mode
window.openFile.setData(this.getFileData(true));
this.openLink(
this.getUrl(window.location.pathname),
null,
true
);
} else if (prev != mode) {
var createFile = mxUtils.bind(this, function (folderId) {
var graph = this.editor.graph;
var selection = graph.getSelectionCells();
var viewState = graph.getViewState();
var page = this.currentPage;
this.createFile(
name,
this.getFileData(
/(.xml)$/i.test(name) ||
name.indexOf(".") < 0 ||
/(.drawio)$/i.test(name),
/(.svg)$/i.test(name),
/(.html)$/i.test(name)
),
null,
mode,
done,
this.mode == null,
folderId,
null,
null,
mxUtils.bind(this, function () {
this.restoreViewState(page, viewState, selection);
})
);
});
if (folderId != null) {
createFile(folderId);
} else {
this.pickFolder(mode, createFile);
}
} else if (mode != null) {
this.save(name, done);
}
}
}
}
);
var allowTab = !mxClient.IS_IOS || !navigator.standalone;
var dlg = new SaveDialog(
this,
filename,
mxUtils.bind(this, function (input, mode, folderId) {
saveFunction(input.value, mode, input, folderId);
this.hideDialog();
}),
allowTab ? null : ["_blank"]
);
this.showDialog(
dlg.container,
420,
150,
true,
false,
mxUtils.bind(this, function () {
this.hideDialog();
})
);
dlg.init();
}
};
修改文件Dialogs.js (5206、5032) 文件路径:src/main/webapp/js/diagramly/Dialogs.js
// 新增保存至自定义
function addStorageEntries() {
// 保存至自定义
addStorageEntry("saveToCustomer");
var allowDevice =
!Editor.useLocalStorage ||
urlParams["storage"] == "device" ||
(editorUi.getCurrentFile() != null && urlParams["noDevice"] != "1");
// 保存至设备
if (EditorUi.nativeFileSupport && allowDevice) {
addStorageEntry(
App.MODE_DEVICE,
null,
null,
editorUi.mode == App.MODE_DEVICE ||
(disabledModes != null &&
mxUtils.indexOf(disabledModes, App.MODE_BROWSER) >= 0)
? true
: null
);
}
// 下载保存
if (allowDevice) {
addStorageEntry("download");
}
defaultValue = "saveToCustomer";
}
// 修改保存选项
// var localServices = ['browser', 'device', 'download', '_blank'];
var localServices = ['saveToCustomer', 'device', 'download'];
六、自定义业务逻辑
我的编辑器每次打开会有两种情况,分为
- 新增,新增的情况下,url上会带有name参数
- 编辑,编辑的情况下,url上会带有id、link参数。
6.1 根据url参数加载业务对应的drawio图
新增只需要在保存的时候单独处理,编辑需要根据link参数来解析出drawio图来正确显示
修改文件App.js (3817) 文件路径:src/main/webapp/js/diagramly/App.js
// 新增判断是编辑情况 增加如下代码
//-------------------------------------------------
else if (urlParams["id"] || urlParams["link"]) {
// 更新图信息
this.spinner.spin(document.body, mxResources.get("loading"));
this.getDrawioFileByUrl();
}
// ------------------------------------------------
// 根据drawio文件链接解析
App.prototype.getDrawioFileByUrl = function () {
const drawioFileUrl = urlParams["link"];
var fileCreated = mxUtils.bind(this, (file)=> {
this.spinner.stop();
this.fileCreated(file, null, null, null, null, null);
});
if (drawioFileUrl) {
// 从 URL 加载 draw.io 文件
mxUtils.get(
drawioFileUrl,
mxUtils.bind(this, function (req) {
const xml = req.getText(); // 获取到 .drawio 文件的内容
fileCreated(
new LocalFile(this, xml, this.defaultFilename, this.mode == null),
null,
null,
null,
null,
null
)
}),
function () {
this.handleError("drawio 图加载失败");
}
);
} else {
this.handleError("No drawio file URL received");
}
};
6.2 自定义业务保存(新增、编辑)
修改文件App.js (6524) 文件路径:src/main/webapp/js/diagramly/App.js
try
{
this.editor.setStatus('');
if (this.editor.graph.isEditing())
{
this.editor.graph.stopEditing();
}
// if (name == file.getTitle())
// {
// file.save(true, success, error);
// }
// else
// {
// file.saveAs(name, success, error)
// }
// -----------------
// 自定义保存
if (mode === "saveToCustomer") {
this.saveCustomerFunction(name, success, error);
} else {
if (name == file.getTitle()) {
file.save(true, success, error);
} else {
file.saveAs(name, success, error);
}
}
// -----------------
}
catch (err)
{
error(err);
}
// 自定义保存逻辑
App.prototype.saveCustomerFunction = function (name, success, error) {
var upload_success = (url) => {
if (urlParams["name"] !== undefined) {
// 创建任务
// 根据drawio图信息、url上的name字段来调用新增接口保存
const data = {
name: decodeURIComponent(urlParams["name"]),
draw_url: url,
};
const API = "api_url_add";
this.costomerXHR(data, API, success, error);
} else if (urlParams["id"] !== undefined) {
// 更新drawio图
// 根据更新后的drawio图信息、url上的id字段来调用更新接口进行保存
const data = {
id: Number(urlParams["id"]),
draw_url: url,
};
const API = "api_url_update";
this.costomerXHR(data, API, success, error);
}
success();
};
this.uploadCustomerFileFunction(name, upload_success, error);
};
因为当前需求接口需要的是将画完的图上传后得到的url,所以还需要封装一个上传方法,将图上传。
如果直接需要得到图数据而非上传,可通过this.getFileData(true)获取到图数据后自行处理。
不过需注意this指向问题
修改文件App.js 文件路径:src/main/webapp/js/diagramly/App.js
此处使用的原生的XMLHttpRequest 也可使用fetch、axios等请求
// 新增如下代码,位置任意
// 上传文件
App.prototype.uploadCustomerFileFunction = function (name, success, error) {
// 创建一个XMLHttpRequest对象
var xhr = new XMLHttpRequest();
const url = '上传文件接口'
// 配置请求
xhr.open(
"POST",
url,
true
);
// 设置请求头
// xhr.setRequestHeader("Content-Type", "application/json");
// 获取上传的文件
var file_data = this.getFileData(true);
// 创建一个Blob对象,指定MIME类型为text/xml
const blob = new Blob([file_data], { type: "text/xml" });
// 使用Blob来“模拟”一个File对象,因为File是Blob的一个子类
const file = new File([blob], name, { type: "text/xml" });
// 创建FormData对象并添加文件
const formData = new FormData();
formData.append("file", file);
// 发送请求
xhr.send(formData);
// 处理响应
xhr.onload = () => {
if (xhr.status === 200) {
const resp = JSON.parse(xhr.response);
if (resp.ErrNo === 0) {
success(resp.Data.filePath);
} else {
this.handleError(resp.Msg);
}
} else {
this.handleError("请求失败,状态码:" + xhr.status);
// 请求失败,处理错误情况
console.error("请求失败,状态码:" + xhr.status);
}
};
xhr.onerror = () => {
console.error("网络错误");
this.handleError("网络错误");
};
};
还需封装新增与编辑的接口请求方法
修改文件App.js 文件路径:src/main/webapp/js/diagramly/App.js
// 新增如下代码,位置任意
// XHR请求
App.prototype.costomerXHR = function (data, api, success) {
// 创建一个XMLHttpRequest对象
var xhr = new XMLHttpRequest();
// 配置请求
xhr.open("POST", api, true);
// 设置请求头
xhr.setRequestHeader("Content-Type", "application/json");
// 发送请求
xhr.send(JSON.stringify(data));
// 处理响应
xhr.onload = () => {
if (xhr.status === 200) {
const resp = JSON.parse(xhr.response);
if (resp.ErrNo === 0) {
success(resp.Msg);
// 接口请求成功后回调 、部署部分会说到-----
const url= '1111'
window.parent.postMessage(
"navigate-to-success",
url
);
// -------------------
} else {
this.handleError(resp.Msg);
}
} else {
this.handleError("请求失败,状态码:" + xhr.status);
// 请求失败,处理错误情况
console.error("请求失败,状态码:" + xhr.status);
}
};
xhr.onerror = () => {
console.error("网络错误");
this.handleError("网络错误");
};
};
七、部署
目前drawio的部署有
- ant打包部署 需要java环境
- 通过启动node服务的方式部署(npm run dev)
- 静态部署 即直接部署整套前端代码,以静态访问index.html的方式完成部署
我的需求是在其他平台内内嵌了iframe来实现访问drawio,所以采用了第三种部署方式
具体部署方式,将整个webapp文件夹上传至服务器,通过nginx代理或者后端起服务通过path指定路由访问index.html
父子页面通信
由于使用了iframe,且在画图新增或者更新完成后,需要回调给父页面结果,所以使用postMessage实现父子页面通信
// 处理响应
xhr.onload = () => {
if (xhr.status === 200) {
const resp = JSON.parse(xhr.response);
if (resp.ErrNo === 0) {
success(resp.Msg);
// 成功后向父页面发送消息 通知父页面
const url= '1111'
window.parent.postMessage(
"navigate-to-success",
url
);
// -------------------
} else {
this.handleError(resp.Msg);
}
} else {
this.handleError("请求失败,状态码:" + xhr.status);
// 请求失败,处理错误情况
console.error("请求失败,状态码:" + xhr.status);
}
};
<template>
// ...
</template>
<script>
export default {
data() {
return {};
},
mounted() {
window.addEventListener('message', this.handleIframeMessage);
},
beforeDestroy() {
window.removeEventListener('message', this.handleIframeMessage);
},
methods: {
onIframeLoad() {
this.iframeLoaded = true;
},
handleIframeMessage(event) {
// 检查消息来源,避免接收不安全的消息
if (event.origin !== window.location.origin) {
return;
}
if (event.data === 'navigate-to-success') {
this.$message.success('成功!');
// 获取回调后 后续逻辑处理(页面跳转等)
}
},
},
};
</script>
<style lang="less" scoped></style>
至此就搞完了,笑嘻嘻的交给了领导~