这两天在网上研究了很多开源的自动cms生成工具,这里对这两天的收获做一下总结。本质上,自己做一个简单的也不费事儿,但是闭门造车容易,拥抱世界却很难。所以先看看有什么巨人的肩膀可以用是好的。
在低代码和零代码领域的应用,一般有四种类型:

  • bootstrap studio,自动拖拽生成页面样式模版,套一套数据就是页面
  • 自动生成小程序或者app,例如谷歌收购的apigee
  • 自动生成cms,包含crud操作和界面的增删查改搜等5项操作
  • 调用代码生成,也就是一般的sdk,帮助快速接入api和功能

这两天调研的功能主要属于第三种类型。下面我就每一种都简单介绍一下

  1. go-admin,4.4k☆
  2. goweb,385☆
  3. qor,4.6k★
  4. CCMS,19☆
  5. pinecms,35☆
  6. go-blog,154☆
  7. GoAdmin,118☆
    7.1 第一步:建表
    7.2 第二步:点击生成
    7.3 第三步:整合生成的代码
    7.4 第四步:添加菜单入口
    7.5 第五步:刷新goadmin后台,完成:
    7.6 后记
1.go-admin,4.4k☆

下载并运行:https://github.com/GoAdminGroup/go-admin
也有一个demo:https://github.com/GoAdminGroup/example.git
打开体验地址:http://localhost:9033/admin
文档地址:http://doc.go-admin.cn/zh/

image.png

评价

可以说,这个项目做得挺认真的,特别是当我们发现这是一个中山大学的大三学生的项目时,感觉更为不容易。为小兄弟点赞。

  • 整体利用后台golang代码控制前端显示和功能布局,对于后台能力强于前端的团队来说还可以考虑一下。
  • 该项目支持go.mod。
  • 文档写的不错。
  • 基于gin框架。
2.goweb,385☆

地址:https://github.com/iissy/goweb
如果遇到端口冲突,无法启动,则把

iris.Addr(":80"),

改为:

iris.Addr(":8083"),
image.png

评价

  • 安装后打开主页空空如也,即使是导入了hrefs.cn.sql(看不到效果,就不好感受是否好用了)。
  • 项目只能使用consul来做注册发现,如果要关掉consul,需要改一些代码,不明确的变更工作量。
3.qor,4.6k★

地址:https://github.com/qor/qor-example
文档地址;https://getqor.com/cn
https://doc.getqor.com/
这个项目看起来比较专业,但是无法运行起来。
报错信息:

Failed to find configuration config/smtp.yml, using example file config/smtp.example.yml
WARNING: AssetFS is used before overwrite it!

看起来依赖的AssetFS和smtp没有搞定。

评价

  • 这个只有英文文档网站。
  • example无法打开,无法体验效果。
  • 虽然建设看起来专业的,但是已经有2年没有维护了。
  • go mod vendor无法通过。
4.CCMS,19☆

地址:https://gitee.com/omyscode/CCMS.git
http://127.0.0.1:8081/ccms 普通用户;账号密码:453453453453/453453453453
http://127.0.0.1:8081/ccms/c_login 管理员;admin/admin;

image.png

评价

  • 这个网站就是一个定制的cms管理后台,功能只跟考生有关系,要想修改为自己所用有点费劲。本身不具有开放性。
  • 样式太老气了。
5.pinecms,35☆

地址:https://github.com/xiusin/pinecms.git
这个网站报错解决不了。无法编译通过。

# github.com/shirou/gopsutil/disk
../../go-miniapp/pkg/mod/github.com/shirou/gopsutil@v3.20.11+incompatible/disk/disk_darwin.go:64:52: cannot use stat.Mntfromname[:] (type []int8) as type []byte in argument to common.ByteToString
../../go-miniapp/pkg/mod/github.com/shirou/gopsutil@v3.20.11+incompatible/disk/disk_darwin.go:65:50: cannot use stat.Mntonname[:] (type []int8) as type []byte in argument to common.ByteToString
../../go-miniapp/pkg/mod/github.com/shirou/gopsutil@v3.20.11+incompatible/disk/disk_darwin.go:66:51: cannot use stat.Fstypename[:] (type []int8) as type []byte in argument to common.ByteToString
../../go-miniapp/pkg/mod/github.com/shirou/gopsutil@v3.20.11+incompatible/disk/disk_darwin.go:77:44: cannot use stat.Fstypename[:] (type []int8) as type []byte in argument to common.ByteToString

评价

  • 无法编译通过,应该是很长时间没有维护了。虽然只过了8个月。
6. go-blog,154☆

代码地址:https://github.com/1920853199/go-blog.git
体验地址:http://127.0.0.1:8088/

image.png

评价

  • 网站太粗糙了,感觉是自己做了个网站,顺便改了一下开源出来,并非一个扩展性很强的网站。
7.GoAdmin,118☆

代码地址:
https://github.com/CrazyRocks/autocreate.git 90☆
https://gitee.com/crazyrocks/goadmin.git 118☆
体验地址:
http://127.0.0.1:8081/ 在这里生成代码;GF代码生成器;
http://localhost:8192/ 在这里体验网站;goadmin后台;
注意啊,作者没有很详尽的readme,我看了源代码很久才折腾好了。

第一步:建表

image.png

注意:

  • project:用来做require代码包的,你想引用到哪里去,你就用啥project名,比如我想放到goadmin项目去,就填goadmin;
  • module:这个是模块名,在goadmin后台的module目录下,已经用了home、public、sys三个模块了,所以新生成的module也要放这里。比如site。
  • menu:这个没有看懂是用来干啥的,暂时其实也用不到,看起来是指用来生成了一个sql文件,导入到某个地方去支撑页面入口添加。无妨。
  • 表名:用处很大。看了源码里面,解析成路径path了。一般设置为两级目录,下划线分隔,例如module_path,module就是上面的module;path就是这个数据库表功能的名字,比如订单、用户、成绩、分数等都可以。

这一步,需要在goadmin的数据库中建表。例如:
先修改配置文件,支持链接数据库:

// 在config/config.toml 文件中,修改数据库配置,指定数据库名字;
host     = "localhost"
port     = "3306"
user     = "root"
pass     = "123456"
name     = "goadmin"
type     = "mysql"
role     = "master"
charset  = "utf8"
priority = "1"
debug  = true

然后自己在终端下面访问数据库,创建表,这里我一次性创建两个:

第二步:点击生成

表建好之后,运行GF代码生成器。
https://github.com/CrazyRocks/autocreate.git里面运行:

go run .

命令行提示,端口启动在8081,则访问:http://localhost:8081/#generator.html 即可访问GF代码生成器。

GF代码生成器

填写:
  • project为goadmin
  • module为site
  • menu为wxmenu
  • 选中site_weixin和site_wxuser
    点击生成代码按钮,提示成功。查看代码路径,发现增加了一个result目录:
result
├── html
│   └── site
│       ├── weixin.html
│       └── wxuser.html
├── js
│   └── site
│       ├── weixin.js
│       └── wxuser.js
├── site
│   ├── config
│   │   └── router.go
│   ├── controller
│   │   ├── site_weixin_controller.go
│   │   └── site_wxuser_controller.go
│   ├── model
│   │   ├── site_weixin_model.go
│   │   └── site_wxuser_model.go
│   └── module.go
├── sql
│   ├── site_weixin_menu.sql
│   └── site_wxuser_menu.sql
└── vue
    └── site
        ├── siteweixin-add-or-update.vue
        ├── siteweixin.vue
        ├── sitewxuser-add-or-update.vue
        └── sitewxuser.vue

第三步:整合生成的代码

作者只是说result就可以拿去用了,但是怎么用呢?这里我研究了一下,可以这样和goadmin进行整合:

  • result/html/site放到goadmin/template/site目录下;
  • result/js/site放到goadmin/public/modules/site目录下;
  • result/site放到goadmin/module目录下,和home、public、sys放一起;

第四步:添加菜单入口

为了能在goadmin左侧显示菜单入口,需要修改goadmin/template/layout/nav.html,在右侧导航加入菜单项:

<li class="nav-item">
    <a href="/sys/oss/index" class="nav-link "><i class="fas fa-cloud-upload-alt"></i>
        <span>文件管理</span></a>
</li>
<li class="nav-item">
    <a href="/site/weixin" class="nav-link "><i class="fas fa-cloud-upload-alt"></i>
        <span>微信绑定</span></a>
</li>
<li class="nav-item">
    <a href="/site/wxuser" class="nav-link "><i class="fas fa-cloud-upload-alt"></i>
        <span>微信用户</span></a>
</li>

第五步:刷新goadmin后台,完成:

image.png

后记

通过

egrep -r "weixin|site" result/*  | grep -v vue | grep -v wxuser > /tmp/one.txt

查看改动,为了省略篇幅,去掉vue和wxuser,只看site_weixin的结果。可以看到autocreate工程修改的范围,体会模版做的工作有哪些,感觉还是比较清晰的:

result/html/site/weixin.html:                <div class="breadcrumb-item"><a href="/site/weixin">绑定微信</a></div>
result/html/site/weixin.html:                <div class="breadcrumb-item"><a href="/site/weixin">列表</a></div>
result/html/site/weixin.html:                                                   v-model="siteweixin.Name"
result/html/site/weixin.html:                                                   v-model="siteweixin.ShortId"
result/html/site/weixin.html:                                                   v-model="siteweixin.Qrcode"
result/html/site/weixin.html:                                                   v-model="siteweixin.CreateTime"
result/html/site/weixin.html:                                                   v-model="siteweixin.UpdateTime"
result/html/site/weixin.html:                                                   v-model="siteweixin.Status"
result/html/site/weixin.html:<script src="/modules/site/weixin.js?_1608464436"></script>
result/js/site/weixin.js:        url: baseURL + 'site/weixin/page',
result/js/site/weixin.js:        siteweixin:{
result/js/site/weixin.js:            vm.siteweixin= {};
result/js/site/weixin.js:            var url = vm.siteweixin.Id ==null ? "site/weixin/save" : "site/weixin/update";
result/js/site/weixin.js:                data: vm.siteweixin,
result/js/site/weixin.js:                    url: baseURL + "site/weixin/delete",
result/js/site/weixin.js:                        url: baseURL + "site/weixin/delete",
result/js/site/weixin.js:        $.get(baseURL + "site/weixin/get/" +Id, function (r) {
result/js/site/weixin.js:            vm.siteweixin= r.data;
result/site/config/router.go:    "goadmin/module/site/controller"
result/site/config/router.go:    s.Group(urlPath+"/site", func(g *ghttp.RouterGroup) {
result/site/config/router.go:        siteWxuserController := new(controller.SiteWxuserController)
result/site/config/router.go:        siteWeixinController := new(controller.SiteWeixinController)
result/site/config/router.go:        g.ALL("/weixin", siteWeixinController)
result/site/config/router.go:        g.POST("/weixin/page", siteWeixinController.Page)
result/site/config/router.go:        g.GET("/weixin/get/{id}", siteWeixinController.Get)
result/site/config/router.go:        g.POST("/weixin/save", siteWeixinController.Save)
result/site/config/router.go:        g.POST("/weixin/update", siteWeixinController.Update)
result/site/config/router.go:        g.POST("/weixin/delete", siteWeixinController.Delete)
result/site/controller/site_weixin_controller.go:* @File:  site_weixin_controller
result/site/controller/site_weixin_controller.go:   "goadmin/module/site/model"
result/site/controller/site_weixin_controller.go:    base.WriteTpl(r, "site/weixin.html", g.Map{})
result/site/model/site_weixin_model.go:* @File:  site_weixin_model
result/site/model/site_weixin_model.go:    return "site_weixin"
result/site/module.go:package site
result/site/module.go:import "goadmin/module/site/config"
result/sql/site_weixin_menu.sql:    VALUES ('1', 'wxmenu', 'site/siteweixin', NULL, '1', 'config', '6');
result/sql/site_weixin_menu.sql:    SELECT @parentId, '查看', null, 'site:siteweixin:list,site:siteweixin:info', '2', null, '6';
result/sql/site_weixin_menu.sql:    SELECT @parentId, '新增', null, 'site:siteweixin:save', '2', null, '6';
result/sql/site_weixin_menu.sql:    SELECT @parentId, '修改', null, 'site:siteweixin:update', '2', null, '6';
result/sql/site_weixin_menu.sql:    SELECT @parentId, '删除', null, 'site:siteweixin:delete', '2', null, '6';

总体来说比较清爽,但是要注意,在阅读源代码时,发现搜索功能有个bug:


image.png

点击查询的时候,向后台传递的参数是search,


image.png

但是,代码中匹配时有问题:
image.png

这里却是按name取的字段,修改为:

    if form.Params != nil && form.Params["search"] != "" {
        where += " and name like ? "
        params = append(params, "%"+form.Params["search"]+"%")
    }

之后就好了:


image.png

评价

  • 支持go.mod;
  • 基于gof框架;
  • 作者在readme.md中的介绍实在是太简略了,导致填写和看代码才能看懂最后生成代码的填写方法;
  • 作为cms,增删查改都有,比较方便;可以作为初级使用,符合我自己简单搭建后台的需要。
  • image.png

    作者也够粗心的,连项目名字都会写错(这里应该是GoAdmin,哎,作者大概是着急赚钱去了吧,难怪还存在一个bug);

  • 关于vue的部分,不知道是不是个半成品;

感谢大家阅读到这里,后续如果还有星比较多的项目,我再体验一下,发给大家。