前言

对于复杂的逻辑或者流程来说,画一画流程图可以帮助我们更好的捋清楚逻辑。平时我女朋友也偶尔会用 来画一下流程图, 确实是一个很好的软件。

但是免费版只能创建 9 个文件,所以她平时在用的时候只能删了画、画了删,用起来不是那么方便,但是又不想为了这个东西开会员。

于是我找到了一个很棒的开源的流程图软件——draw.io,它同样也提供了在线的地址——在线地址

但她用了一会之后,感觉这个在线地址也不是那么的方便易用,提出了下面的问题:

这个在线地址部署在国外,平时使用会受网络影响本身不提供文件存储的功能,它对接了多种存储介质,比如你可以下载到本地、或者托管到一些云盘或者 ,虽然说配置起来不是很麻烦,但也并不是开箱即用本身不提供文件管理功能,它可以导入一个个文件,感觉就像是一个编辑器而已,并没有把我创建的、编辑的流程图统一管理起来,没有类似文件/文件夹列表的功能

安装antd命令_安装ant后在cmd中用不了_ant安装

所以,基于上面的种种问题,我就想着基于 魔改一个的绘图软件,并且自己后端实现存储,这样就可以让这个东西免费无限制且易用。

其实说是魔改,我们改的东西不多,主要是改变存储、读取的方式,以及有一些功能不需要的可以做一些删减,最后就是自己做一个平台把文件与流程图串起来。

这是项目的体验地址,欢迎大家体验:体验地址

前端实现

首先先把 的代码拉下来,拉下来之后只需要关注 src/main/ 这个目录,所有的前端代码都在里面。

ant安装_安装ant后在cmd中用不了_安装antd命令

把前端跑起来

入口是

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 的如下代码,绿色代码是我新加的。

ant安装_安装antd命令_安装ant后在cmd中用不了

mock 一下数据,把文件的标题与内容通过实例化一个 ,并调用 加载到画布上

然后我们就可以得到一幅流程图如下图所示

这里真正实现的时候,是根据 id 调用后端接口,去拿标题和内容,然后加载到编辑器中ant安装,到这一步,读取数据已经完成。

保存数据

由于我们上面选择的存储介质是 ,所以保存内容的逻辑在 这个类中,具体在下面打印的位置

安装antd命令_ant安装_安装ant后在cmd中用不了

在这个位置,我们可以把数据同步给后端进行更新。

除了内容之外,标题的更新我们也需要考虑。

ant安装_安装ant后在cmd中用不了_安装antd命令

标题更新的时候会走到下面这个方法,我们可以在这个方法中来发送接口给后端更新文档的标题

安装antd命令_ant安装_安装ant后在cmd中用不了

至此,基本的数据流向问题已经解决,在流程图层面,我们已经解决了读取数据及更新数据的问题,解决了这两个问题之后,我们就可以把流程图内容信息存在我们自己的服务器中。

其他配置项修改

还有一些其他配置项的修改,这个就根据我们自身的情况来,看看哪些东西是我们不想要的,哪些东西是要改的。

这里就得耐心去读一下它的源码了,没什么技巧,找到你自己想要改的地方,改它。这里我举两个例子。

第一个,图标的修改

安装ant后在cmd中用不了_ant安装_安装antd命令

上面框出来的图标,在下面的文件中,修改成你想要的图标就行。

第二个,菜单的修改,我这里对【文件】这个菜单删了很多,只保留了我觉得必要的东西

ant安装_安装antd命令_安装ant后在cmd中用不了

菜单在下面这个文件中,基本上就是找到你不想要的东西把它注释掉或者删掉。

安装antd命令_安装ant后在cmd中用不了_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;
    }

ant安装_安装ant后在cmd中用不了_安装antd命令

首屏加载优化

在打包部署完成之后,发现了加载还是挺慢的,一个是我服务器的带宽比较小,另一个是确实加载了几个比较大的 js 文件。

首先 app.min.js 这个是主包,是不能省略的。然后看到 .min.js 跟 .min.js ,看看他们可不可以不阻塞主流程。

大概看了一下 .min.js 的内容,它里面都是模版,好家伙,怪不得这么大;其次关于 .min.js ,看了一下 /etc/build/build.xml 打包文件,它大概是做一些拓展逻辑的,比如说一些导入导出之类的。

所以这两个包的加载完毕与否,是不影响正常的主绘制流程使用的。

这里便是上面提到的两个包的加载入口,让这个加载函数加载完第一个包之后就执行回调函数即可。

安装ant后在cmd中用不了_安装antd命令_ant安装

这样之后,我们不需要再等待这个包加载完成就能开始用主要的绘图逻辑,这个包加载了 13S ,也就是说,我们不需要再等这 13S 。

首屏加载速度提升了 10多秒 啊兄弟们,恐怖如斯~

现在没有缓存的情况下,首屏加载 3S 左右,还是挺丝滑的

平台实现

我另外用 React 实现了一个用户登录、管理文件的平台,目前做的功能有:

安装antd命令_安装ant后在cmd中用不了_ant安装

安装antd命令_ant安装_安装ant后在cmd中用不了

安装antd命令_ant安装_安装ant后在cmd中用不了

ant安装_安装ant后在cmd中用不了_安装antd命令

目前来说做的还比较简单,只提供了最基本的文件管理功能,这个平台跟上面的绘图页面可以理解为是两个项目。

在新建或者打开的时候,会从这个平台跳转到绘图项目:

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

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注