前言
对于复杂的逻辑或者流程来说,画一画流程图可以帮助我们更好的捋清楚逻辑。平时我女朋友也偶尔会用 来画一下流程图, 确实是一个很好的软件。
但是免费版只能创建 9 个文件,所以她平时在用的时候只能删了画、画了删,用起来不是那么方便,但是又不想为了这个东西开会员。
于是我找到了一个很棒的开源的流程图软件——draw.io,它同样也提供了在线的地址——在线地址
但她用了一会之后,感觉这个在线地址也不是那么的方便易用,提出了下面的问题:
这个在线地址部署在国外,平时使用会受网络影响本身不提供文件存储的功能,它对接了多种存储介质,比如你可以下载到本地、或者托管到一些云盘或者 ,虽然说配置起来不是很麻烦,但也并不是开箱即用本身不提供文件管理功能,它可以导入一个个文件,感觉就像是一个编辑器而已,并没有把我创建的、编辑的流程图统一管理起来,没有类似文件/文件夹列表的功能
所以,基于上面的种种问题,我就想着基于 魔改一个的绘图软件,并且自己后端实现存储,这样就可以让这个东西免费无限制且易用。
其实说是魔改,我们改的东西不多,主要是改变存储、读取的方式,以及有一些功能不需要的可以做一些删减,最后就是自己做一个平台把文件与流程图串起来。
这是项目的体验地址,欢迎大家体验:体验地址
前端实现
首先先把 的代码拉下来,拉下来之后只需要关注 src/main/ 这个目录,所有的前端代码都在里面。
把前端跑起来
入口是
src/main//index.html 这个文件,我使用了 起了一个服务,一来是充当静态资源服务器,二来是充当开发环境的代理,规避接口调用时的跨域问题。
新建一个 .js 文件,填入如下内容
// server.js
const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware");
const path = require("path");
const compression = require("compression");
const app = express();
app.use(compression());
// 静态资源服务
app.use(express.static(path.join(__dirname, "./src/main/webapp")));
// 接口转发
app.use(
"/draw-io",
createProxyMiddleware({
target: "http://draw.eztool.top",
changeOrigin: true,
pathRewrite: {
"^/draw-io": "/draw-io", // 转发的时候去掉前缀
},
})
);
// 启动服务器
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
然后运行一下这个 node 脚本,启动服务。
启动服务之后,这里有两点需要注意的地方:
如果你的服务器动在 3000 端口,那么你需要访问 :3000/?dev=1修改 src/main//index.html 的如下代码,不然静态资源的加载会有问题
随意修改一些东西,然后打开上述的链接,如果看到修改生效,那么就证明我们的开发环境启动成功了
初始化数据
大概看了一下 的代码,发现流程图的内容是 xml 格式的。对于文件的初始化流程,可以大概找到 App.js 的如下代码,绿色代码是我新加的。
mock 一下数据,把文件的标题与内容通过实例化一个 ,并调用 加载到画布上
然后我们就可以得到一幅流程图如下图所示
这里真正实现的时候,是根据 id 调用后端接口,去拿标题和内容,然后加载到编辑器中ant安装,到这一步,读取数据已经完成。
保存数据
由于我们上面选择的存储介质是 ,所以保存内容的逻辑在 这个类中,具体在下面打印的位置
在这个位置,我们可以把数据同步给后端进行更新。
除了内容之外,标题的更新我们也需要考虑。
标题更新的时候会走到下面这个方法,我们可以在这个方法中来发送接口给后端更新文档的标题
至此,基本的数据流向问题已经解决,在流程图层面,我们已经解决了读取数据及更新数据的问题,解决了这两个问题之后,我们就可以把流程图内容信息存在我们自己的服务器中。
其他配置项修改
还有一些其他配置项的修改,这个就根据我们自身的情况来,看看哪些东西是我们不想要的,哪些东西是要改的。
这里就得耐心去读一下它的源码了,没什么技巧,找到你自己想要改的地方,改它。这里我举两个例子。
第一个,图标的修改
上面框出来的图标,在下面的文件中,修改成你想要的图标就行。
第二个,菜单的修改,我这里对【文件】这个菜单删了很多,只保留了我觉得必要的东西
菜单在下面这个文件中,基本上就是找到你不想要的东西把它注释掉或者删掉。
打包部署
这个项目打包的工具用的 ant ,它是一个基于 java 的打包工具。所以要打包我们先要装好 java 的 jdk 以及 ant 。
安装好后进入到 /etc/build 这个目录下,执行 ant 命令,就可以发现打包成功了。
部署的时候使用 nginx 开了一个目录,然后我比较偷懒。我把整个 目录都丢了上去,但是呢 目录又很大,我也不想每次通过 ftp 工具去传。
于是我就建了一个 git 仓库,把在我本地的 目录推了上去,然后在服务器拉取这个仓库。这样做了以后,我每次通过 ant 打包完之后推送代码,然后在服务器 pull 一下,代码就更新了。
还有一点要注意的是,这个项目的前端并没有使用一些现代化的打包工具,打包出来的文件名不会有 hash 。
为了避免缓存导致代码不生效的问题,我在 nginx 配置的时候使用了协商缓存,配置如下
location ~* .(js|css|html)$ {
add_header cache-control no-cache;
}
首屏加载优化
在打包部署完成之后,发现了加载还是挺慢的,一个是我服务器的带宽比较小,另一个是确实加载了几个比较大的 js 文件。
首先 app.min.js 这个是主包,是不能省略的。然后看到 .min.js 跟 .min.js ,看看他们可不可以不阻塞主流程。
大概看了一下 .min.js 的内容,它里面都是模版,好家伙,怪不得这么大;其次关于 .min.js ,看了一下 /etc/build/build.xml 打包文件,它大概是做一些拓展逻辑的,比如说一些导入导出之类的。
所以这两个包的加载完毕与否,是不影响正常的主绘制流程使用的。
这里便是上面提到的两个包的加载入口,让这个加载函数加载完第一个包之后就执行回调函数即可。
这样之后,我们不需要再等待这个包加载完成就能开始用主要的绘图逻辑,这个包加载了 13S ,也就是说,我们不需要再等这 13S 。
首屏加载速度提升了 10多秒 啊兄弟们,恐怖如斯~
现在没有缓存的情况下,首屏加载 3S 左右,还是挺丝滑的
平台实现
我另外用 React 实现了一个用户登录、管理文件的平台,目前做的功能有:
目前来说做的还比较简单,只提供了最基本的文件管理功能,这个平台跟上面的绘图页面可以理解为是两个项目。
在新建或者打开的时候,会从这个平台跳转到绘图项目:
export const openDraw = (id) => {
const dev = location.href.includes("localhost");
let url;
if (dev) {
url = `http://localhost:3000?dev=1&id=${id}`;
} else {
url = `http://draw.eztool.top/draw?id=${id}`;
}
window.open(url);
};
后端
后端使用的 java ,使用的是 搭建的项目。
相关技术栈:
后端实现主要分为三块:
鉴权
在之前做 工具网站 的时候用到了 sa-token 框架,这个框架整体来说功能挺强大的,但对于小网站来说可能很多东西都不太需要。所以我这次基于 的工具类自己包了一层ant安装,实现了一个较为简洁的 JWT 鉴权流程。
@Slf4j
@UtilityClass
public class JwtUtil {
String jwtStr = "xxxxxxx";
public String getToken(User user) {
return JWTUtil.createToken(BeanUtil.toBean(user,Map.class), jwtStr.getBytes());
}
public Boolean verify(String token) {
return JWTUtil.verify(token, jwtStr.getBytes());
}
public void isLogin(String token){
if (StrUtil.isBlank(token)) {
throw new BusinessException("请先登录");
}
if (!verify(token)) {
throw new BusinessException("请先登录");
}
}
public User getUser(String token){
isLogin(token);
JWTPayload payload = JWTUtil.parseToken(token).getPayload();
if (Objects.isNull(payload)){
throw new BusinessException("用户信息为空");
}
return User.tpJWTPayload(payload);
}
}
这里封装了一个设置 token 以及解析 token 的工具类,登录成功后 token 就被设置到 中,请求过来时解析 中的 token 以获取用户信息。
用户信息
这里包含了用户的注册、登录、修改密码等功能。
用户信息表的结构如下:
{
"userId": "",
"ip": "",
"name": "",
"account": "",
"password": "",
"id": "",
"createTime": "",
"updateTime": ""
}
注册这里用到了邮箱验证码作为校验,验证码发送出去后会存在 redis 中,并设有有效期。
然后注册一个新邮箱作为发送验证码的邮箱,以 163邮箱 为例。
在这里开通 SMTP
然后引入邮件依赖
org.springframework.boot
spring-boot-starter-mail
配置yml
spring:
mail:
# 设置邮箱主机
host: smtp.163.com
# SMTP 服务器的端口
port: 587
# 设置用户名,这里使用你邮箱账号就行
username: 123456789@163.com
# 设置密码,该处的密码是邮箱开启SMTP的授权码而非邮箱密码
password: SODJSHAUHGQWRQWE
default-encoding: UTF-8
protocol: smtps
properties:
mail:
smtp:
ssl:
enable: true
具体的实现如下:
@Slf4j
@Service
@RequiredArgsConstructor
public class MailServiceImpl implements MailService {
private final JavaMailSender javaMailSender;
//发送邮件
@Override
public void sendEmail(String email,String subject, String text) {
try {
SimpleMailMessage mailMessage = new SimpleMailMessage();
//你的邮箱账号
mailMessage.setFrom("123456789@163.com");
//接收方的邮箱账号
mailMessage.setTo(email);
//标题
mailMessage.setSubject(subject);
//内容
mailMessage.setText(text);
//发送邮件
javaMailSender.send(mailMessage);
} catch (Exception e) {
e.printStackTrace();
log.info("发送邮箱失败:{}",e.getMessage());
}
}
}
文件表
文件表里包括文件夹跟文件,主要通过 type 去区分。更详尽的表结构字段如下:
{
"fileId": "", //文件ID
"parentId": "", //上级文件id
"name": "", //名称
"type": "", //文件类型 0=文件 1=文件夹
"content": "", //文件内容
"isSub": false, //是否有下级(针对文件夹点击下拉判断)
"createId": "", //创建用户id
"updateId": "", //修改用户id
"delFlag": "", //逻辑删除 0=正常 1=删除
"id": "",
"createTime": "",
"updateTime": ""
}
剩下的就是关于文件的一些增删改查逻辑,这里就不再放具体的代码。通过维护 跟 的对应关系,就可以实现文件树的逻辑。
最后
这就是我基于 魔改的一个在线绘图软件,对于我们自身的要求来说是够用了。后续的拓展的话,我尽量还是以平台拓展为主,绘图功能拓展为辅,因为这个绘图功能已经很强大了,甚至对我来说,这个绘图我常用的还不到它功能的 10% ,所以我也不太想花太多精力去改它。
后续可能会拓展的点:
如果你也有像我们一样的痛点,欢迎你体验我们的站点。如果你觉得有哪里用得觉得不舒服的地方,也欢迎随时与我们反馈。希望这个对你会有帮助~
限时特惠:本站持续每日更新海量各大内部创业课程,一年会员仅需要98元,全站资源免费下载
点击查看详情
站长微信:Jiucxh