Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

控制台

为开发者提供好用且能降低软件研发成本,加快软件研发效率的业务产品,如:皮肤设计器、代码生成器、项目管理, 示意图

后台进入MStore安装模板

登录MCms后台,点击后台管理界面右上角四方块图标,输入平台账号密码登录人MStore

示意图

MStore远程安装模板

示意图

ApiKey

Mcms > 6.2.0 开始支持 ApiKey

  • 创建ApiKey

平台会员中心创建ApiKey

示意图

  • MCms后台APIKey配置

并在MCms后台 系统设置 -> APIKey配置 进行绑定

示意图

  • 使用场景一,皮肤设计器

    创建项目时,需设置正确的域名与ApiKey,在设计模板时可动态获取MCms的站点、栏目与文章数据,更方便展示设计效果并提升使用体验。域名的设置可参考下文内网穿透部分的说明

示意图

  • 使用场景二,MStore安装皮肤或插件

    可以更方便的通过远程方式安装模板与插件

示意图

通过MCms后台进入MStore是不需要域名与ApiKey,操作更方便

示意图

内网穿透

本地开发调试MCms系统的时候,在使用皮肤设计器、MStore需要做外网映射,方便皮肤设计器与MStore链接到本地的MCms服务,开发者根据自身的网络环境选择自己熟悉的映射配置方式(如花生壳、frp、ngrok等),如果一点都不清楚内网穿透技术的可以参考cpolar软件穿透配置 配置使用也很简单。

Tip

如果MCms已经部署在服务器上,可以直接使用对应的域名地址

快速创建项目

通过在线拖拽的方式来设计页面效果

Tip

我们皮肤设计器的优势

  • 生成的代码可以维护二开,代码生成规范,没有多余的代码结构
  • 组件可以反复使用,可以快速组织页面
  • 内部产品已经结合AI,只需要提供设计稿就可以快速生成页面效果

使用MStore模板

平台远程使用

域名 可以为访问到 MCms 系统的地址,如果是本地开发请参考 内网穿透

ApiKey 关于域名与ApiKey请参考 详细说明

使用场景一,皮肤设计器

后台本地使用

通过MCms后台进入MStore安装,不需要域名与ApiKey的配置 使用场景一,皮肤设计器

动图演示

使用场景一,皮肤设计器

原创UI设计稿

根据UI设计稿,拖动组件进行页面设计

数据绑定

  1. 绑定站点信息

示意图 2. 绑定栏目数据

示意图 3. 绑定文章列表 示意图

同步站点

示意图

Tip

需要设置域名与ApiKey才可以使用

下载模板代码

示意图

Tip

开通SVIP解锁下载源代码,SVIP\VIP用户购买MStore其他模板有更多折扣

界面介绍

  • 组件:可以重复拖拽使用的布局
  • 母板:可以重复使用的页面,一般是通过 <#include "母板.htm">
  • 模板:可以快速创建页面的模板,模板由多个组件组成
  • 样式:CSS样式,样式可以设置在组件上,也可以设置在页面上
  • 数据:绑定mcms的数据

设计器界面主要区域标记

img.png

组件

默认组件

img.png

每个组件都有对应的组件

公共组件

img.png

组件列表分为两部分,我的组件和模板列表。

我的组件

  • 新增我的组件

对于一些经常使用的组件模块,可以转换成我的组件,方便在各个项目中使用,不需要重复去托结构。

在图层点击顶层容器,在工具条点击转组件,填写组件信息。

img_14.png

img_15.png

页面

  • 新增页面

点击新增页面,填写页面信息。

注意:

  1. 一个项目内,文件名不能重复
  2. 文件名不需要填写后缀,系统会自动添加.htm后缀

img_7.png

母板

在一个项目里各个页面中的通用部分,如头部、底部等等,可以抽离成母板。

页面可以通过引用母板来继承这些通用结构,当母板内容发生变更时,所有引用该母板的页面会自动同步更新,无需逐一修改。

如果页面有引用母版,生成的代码会以<#include "母版文件名.htm" /> 形式引入母版

  • 抽离母版

如下图的头部,点击顶层容器,在工具条点击抽离母版,填写母板信息。

注意:

  1. 抽离母板只能在图层最顶层操作
  2. 图层最顶层必须为容器

img_8.png

img_9.png

  • 引用母版

先选中一个页面,在tab栏点击切换母版

img_10.png

鼠标悬停在母板名称前方的拖动图标上,在往页面拖拽,即可在页面中引用母板

img_11.png

img_12.png

图层

图层与PS中的图层是一个意思,在图层控制区可以对每一个图层进行编辑。

  • 图层定位

在内容展示区域任意选中一个图层,点击定位图标可以快速移动到该图层

img_6.png

  • 图层名称编辑

双击图层名称,可以对图层名称进行修改

img_3.png

  • 图层显示隐藏

点击图层标题前面的眼睛图标可以显示或隐藏该图层。

注意:如果父图层被隐藏,则该图层下的子图层也会被隐藏

img_2.png

  • 图层删除

点击图层标题后面的删除图标可以删除该图层

注意:如果父图层被删除,则该图层下的子图层也会被删除

img_4.png

  • 图层切换

在图层列表中上下拖拽图层,可以调整图层结构

img_5.png

样式

基础样式

基础样式:选中图层,在右侧样式面板中直接设置,如宽度高度,边距、背景等。

img_16.png

高级样式

高级样式:选中图层,在右侧样式面板中点击css高级设置,高级样式涉及css样式编码,可灵活实现各种效果。

当前组件图层样式

基础样式属性不满足需求时,如容器阴影效果,可采用css属性box-shadow,则在当前组件图层样式填写"boxShadow":"0px 0px 5px #000000"。(注意:属性用驼峰命名,值需要双引号包裹)

附加样式名称

为组件添加附加样式名,多个class以空格分割

当前组件样式

当前组件样式设置,直接写css代码,转组件时会携带css代码;遇见组件样式未生效,加 !important 处理

当前页面样式

作用在当前页面,直接写css代码,转组件时不会携带css代码

全局app.css样式

作用在整个项目,直接写css代码,转组件时不会携带css代码

img_17.png

模板

当前选中组件可以参考使用的模板风格

配置

数据

静态数据

绑定栏目数据

栏目的筛选条件,与channel标签对应

img_73.png

列表栏目开关说明

使用场景:当模板作为通用模板时,例如父子栏目页面共用同一个子栏目导航。

  • 开启后:源码会自动处理栏目 ID 或筛选条件
    • 如果当前是父栏目页面 → 自动获取子栏目数据
    • 如果当前是子栏目页面 → 自动获取同级栏目数据

img_74.png

绑定文章数据

文章的筛选条件,与arclist标签对应

img_76.png

分页开关说明

只能在列表模板中使用,需要与分页组件结合使用

  • 开启后:源码会自动处理栏目 ID,可作为通用列表模板

组件

直接拖动使用,每个组件对应生成html标签、CSS样式代码

Tip

div、span、img、icon、nav、search、loop、tab、custom 是对应生成的html标签

普通组件

img_19.png

容器 div

容器组件是页面布局的核心元素,用于对页面进行结构化划分,便于实现灵活的页面排版与响应式设计。

  • 下面以铭软官网头部为示例,每个红框可视为一个容器,从而完成初步布局

img_20.png

  • 在设计器中图层结构示例

img_21.png

Tip

也可以用容器组件设置一定高度宽度,可以达到间隔其他组件的效果。

文本 span

  1. 单击选中文本图层,右侧可以修改整个文本的样式,如:字体,背景、边框、间距等。在输入框可修改文本的内容。

img_22.png

  1. 在数据中配置链接地址,可将文本转换为超链接

  2. 双击文本组件的文字,会弹出富文本编辑栏,可以插入图片、链接、文字颜色等

img_67.png

Tip

文本设置超出省略,不需要设置高度,宽度按需设置。

图片 img

  1. 图片一般设置固定宽度,高度采用自适应
  2. 上传的图片的宽高尺寸比例一定要根据实际显示区域适配

图标 i

基于iconfont图标库,可快速实现图标的展示。

高级组件

导航 nav

显示栏目属性导航的栏目,可设置导航菜单交互效果

img_28.png

搜索设置圆角效果的时候需要在 输入框按钮 图层设置

img_51.png

img_52.png

轮播图 swiper

  • 数据配置为栏目 文章 轮播项也会同步相同的数据配置,与循环组件类似;

img_40.png

img_41.png

  • 数据配置为静态:不同轮播图可以设置不同的背景;

img_42.png

循环 loop

  • 数据配置为栏目 文章可以实现文章列表、栏目列表,每个循环项的样式与绑定的数据都是相同的。

img_57.png

img_56.png

  • 数据配置为静态情况下,循环项里的文本或图片的链接地址可以独立设置

img_53.png

  • 开启列表对齐,循环项的宽度由循环的宽度和单行数量决定,与循环项本身的宽度无关。

img_54.png

  • 关闭列表对齐不会清空边距,按需手动去除

img_55.png

Tip

循环项里面的元素只能从左侧组件列表拖入

禁止嵌套循环组件、轮播组件、导航组件、搜索组件、标签组件

循环项里面的每个元素的样式都会增加当前循环体的样式名称,避免多个循环项样式冲突,格式如:#w-loop-1770693115000_15 > .ms-loop .ms-loop-item

标签 tab

实现选项卡的效果,通过配置来设置标签组件的样式

img_58.png

自定义组件

基础组件

可以设计器手动转化的组件,达到复用组件的效果,

img_59.png

唯一 样式生成,同步组件里面所有子元素的 父层级样式(高级组件里的元素不会同步)

img_60.png

Tip

只有最外层是容器才能转组件,也只有一个最外层容器才有唯一配置 组件默认不开启唯一id;

模版组件

导航 搜索组件,将元素先转化为基础组件,在基础组件的json结构基础上新增模版、样式配置

  • 组件type=custom,需要写freemarker模板才可以正常使用

  • 图层结构不可调整,只能设置数据或样式

  • 不能嵌套其他组件

  • 样式配置,样式配置的优先级高于基础样式设置,如鼠标悬停样式效果

    • 在组件最外层级加上
    "options":{"config":{}},
    
    • 样式配置路径说明:顶级层级样式名 + 顶层样式名 + 配置的css选择器;

    img_61.png

    img_65.png

  • 组件freemarker模板

    • 在模板文本最外层加上自定义组件标识
    <div id="w-id-${component.id}-${component.key}"/  style="display:contents;"> 模板代码 </div> 
    
    • 模板代码如果涉及具体标签逻辑,需要在最外层加上<#noparse></#noparse>,防止被提前解析

    img_62.png

  • 组件样式扩展

    • 一般规定组件样式在组件最顶层写,在设置组件样式时,需在最外层加{id}样式名,例如:{id} .txt:hover {} ;

    • 在custom组件的{id}会替换成 w-id-${component.id}-${component.key} + 当前组件样式名称;

      img_63.png

      img_64.png

    • 如果不需要展示数据配置,删除data 配置

      img_66.png

分页

分页只能在列表页使用,一个列表分页只能开启一次

设计技巧

截图演示了基本网页布局的搭建,以及常用的名称的定义方式,相同颜色标注的部分表示通用样式

页面布局.jpg

Tip

  • 样式名称定义可以根据区域快来定义,如:头部 header 底部footer
  • 样式名称定义可以使用 - 连接符,如:头部logo区域,header-logo
  • 多个词组合使用 - 符号,如:头部logo区域,header-logo

自定义样式

{id} 当前组件样式名称

在其他层级的组件样式使用 {id} 会替换成当前组件样式名称

动画使用

https://ianlunn.github.io/Hover/

复制所需动画样式名,在组件的附加样式添加动画样式名

img_23.png

img_24.png

pc与mobile出现差异化配置, 如小图轮播pc展示4张,mobile 展示2张图片;

解决方案:托两个一样的组件,pc显示一个4张的配置,隐藏一个2张的配置;mobile同理

标签组件标题设置底部边框后,左边文字比右边高一点

解决方案: tab标题都添加边框,默认不设置边框颜色,只设置宽度;激活或悬停tab时才设置边框颜色

文章列表模块的更多链接处理

解决方案:

  • 方式一:在数据绑定中添加更多跳转的链接地址
  • 方式二:在外层用循环包裹文本,循环绑定具体栏目,文本绑定栏目地址

文本组件 样式设置与编辑器设置用法

  • 样式设置:适用于简单的字体大小、颜色等基础样式修改,操作简单快捷;

img_69.png

  • 编辑器设置:适用于复杂的文本格式调整,如不同文字大小比例(例如 70%)、混排样式等场景;

img_68.png

Tip

  • 优先级说明:编辑器设置的为行内样式,优先级最高;当样式设置与编辑器设置同时存在时,编辑器设置生效
  • 悬停效果:如果需要设置文字悬停(hover)效果,请使用样式设置

样式选择区输入框单位说明

如:边距、定位、边框、圆角等长度不需要设置单位,默认为px

img_70.png

样式属性未null说明

样式属性值显示为 null 属于正常现象。数据采用 JSON 的 key-value 结构,当清空某个属性值时,为了保持 JSON 结构完整性会显示为 null

img_71.png

循环组件边距说明

外边距用于循环组件的布局,循环组件的边距会自动计算,请勿手动调整

img_72.png

选中激活效果样式名称说明

设计器中使用 on 作为选中/激活状态的样式类名。

使用示例

  • 列表栏目循环开启后,当前选中的循环项项会自动添加 on 类名

img_75.png

文本包含动静内容

使用两个文本组件,分别绑定静态文字和动态数据

img_78.png

img_77.png

列表li结构说明

设计器生成的html结构没有li标签;li 对应设计器循环项,为div结构

img_80.png

img_79.png

往容器里托组件托不进去

可以适当增加最外层容器的高度,再托组件;等组件布局结束后清楚多余高度,使用自适应容器高度

img_81.png

当前组件样式新增说明

新增样式属性(如shadow),属性用驼峰命名,值需要双引号包裹

img_83.png

样式命名规范

  • 各个模块最大的盒子样式通用使用 pannel 命名;存在差异pannel-序号命名(如:pannel-first)
  • 内容块使用 body 命名
  • 左右使用 left/right 命名,手动添加父盒子层级样式

自定义链接

自定义链接的优先级最高

站内父子单篇栏目构建

如一个栏目 下有 4个子栏目单篇; 则建议父栏目 也为单篇,父栏目显示子栏目列表内容;站内跳转尽量少用自定义链接

img_85.png

分享模板

设计好的模板可以通过分享来获得长久的自动收益!

Tip

分享约束条件

  1. 通过MStore使用的模板不允许分享
  2. 分享的模板价格不允许二次修改
  3. 分享后项目会进入审核阶段,审核阶段项目 禁止编辑、删除等操作
  4. 分享的模板必须存在演示数据,会同步项目配置中 Api 域名 MCMS 服务所包含的文章、栏目及自定义模型数据

分享流程:

  1. 设计器会员端分享
  2. 在设计器后台下载压缩包(无html)
  3. 开源分享插件导入验证压缩包
  4. 导出压缩包 并在store部署
  5. 在store后台完善 pc、mobile图片、 描述 、 行业
  6. 设计器后台审核通过

常见问题

组件配置未生效

在配置组件样式时遵循设计器的结构,view模板 样式结构与设计器不一致,以设计器为准,

img_25.png

img_26.png

SaaS建站介绍

满足更多普通客户的需要,低成本的快速搭建网站

基本使用

创建站点

通过平台创建站点 ,支付成功后会自动生成一个临时的访问域名地址可以管理站点。

img.png

站点管理

登录平台后,再点击站点管理 ,可以看到已购买的站点列表

img_1.png

域名绑定

域名需要先解析才能使用。具体参考 域名解析方法

以下以阿里云解析为例

  1. 登录阿里云

img_9.png

  1. 在产品列表选择域名与网站

img_10.png

img_11.png

  1. 在域名管理选择域名列表

img_12.png

  1. 选择需要解析的域名

img_13.png

  1. 添加两条cname记录

img_14.png

  • 第一次: 记录类型选择CNAME , 主机记录选择 www, 记录值填写注册站点后提供的初始域名

img_15.png

  • 第二次: 记录类型选择CNAME , 主机记录选择 @, 记录值填写注册站点后提供的初始域名

img_16.png

Tip

若域名的主机记录 www 被绑定,则需修改绑定的记录

mcms平台修改绑定域名

推荐推荐域名填写http://www.xxx.com/,附属域名填写http://xxx.com/

img_2.png

管理员密码

点击修改密码,可修改当前站点的超级管理员密码

img_3.png

续费

网站到期前会通过公众号的方式通知,收到通知直接再公众号完成续费操作就可以。

img_6.png

升级

增加空间

img_5.png

img_7.png

管理网站

基本操作与开源系统一致,可以参考 开源系统后台使用

导入模板

导入MStore 选择的模板压缩包,导入完成后 点击右上角的"发布网站"按钮进行网站发布 img_4.png

代码生成器使用手册

降低软件研发成本,采用拖拽的方式设计表单生成SQL与Java代码。操作界面上大量采用?号帮助引导的信息,让用户快速上手使用。

特点

1、拖拽方式制作后台管理界面(列表、表单);

2、自动生成SQL DDL语句,暂时只支持MySQL;

3、自动生成规范的Java业务代码;

使用场景

1、快速创建项目:从零开始快速搭建项目;

2、自定义扩展:可用于生成MCms自定义模型、自定义表单的业务;

3、代码参考:可以快速生成Element控件、验证规则;

快速开发项目

在MCms的基础上,通过代码生成器快速开发新功能。

创建项目

这一步很重要,决定了生成Java包的结构、模块的名称与表名的生成规则

生成SQL中的表名称会拼接上设置好的表前缀

Note

注意:项目包+模块名必须在平台中唯一

创建业务表单

设计表单

根据实际业务通过拖拽控件的方式设计业务表单,每个控件都有对应的属性设置。

列表设置

根据实际业务通过拖拽字段控制列显示的先后顺序

  1. 默认值:在表单页会自动填写当前值
  2. 列表搜索:是否在列表页可搜索该字段

代码预览

保存之后,对应生成自定义模型与相关页面代码。 可导入到对应的自定义模块,也对源码进行下载。 导入到自定义模块更加简洁,无需调试代码。相关文档参考《自定义业务开发》。 点击此处预览源码

Tip

注意:预览代码前需要保存。

自定义模型

自定义模型复制即可导入到自定义模块。

导入之后自动解析生成后面的表单页面与SQL等等。无需进行后续的导入。

列表与表单

生成的表单与列表为ftl页面,需要添加页面则可在此路径src/main/webapp/WEB-INF/manager/cms(模块名)新增文件。

Tip

注意:此页面为后台界面文件,而非前台html文件,不可直接粘贴为前台页面

后台代码的生成

实体Entity、Action与Biz等。 此类业务代码,放置于此路径src/main/java/net/mingsoft(项目包)/cms(模块名)即可。

下载SQL

复制sql文件导入数据库中

源码的代码

若需要源码,在此处可下载。 1、目录层级自动生成,复制到项目工程即可使用(注意:合并复制)
2、修改net.mingsoft.MSApplication扫描包

@SpringBootApplication
@ComponentScan(basePackages = {"net.mingsoft","添加代码生成器项目包参数"})
@MapperScan(basePackages={"**.dao"})
@ServletComponentScan(basePackages = {"net.mingsoft"})
public class MSApplication {
    public static void main(String[] args) {
        SpringApplication.run(MSApplication.class, args);
    }

}

3、重启系统

安装菜单

初始化菜单

1、代码生成器项目列表初始化菜单数据

2、复制菜单数据

3、在系统菜单管理中导入菜单数据

定制页面情况

场景:增加一个菜单访问定制的页面

  1. 增加对应的接口,返回该页面名称,可以参考接口中的list和form接口;

  2. 定制页面放在WEB-INF下对应层级的位置,参考该接口list和form接口的页面;

  3. 增加菜单(子菜单),菜单地址为新增加接口的地址

ElementUI表单开发

常用的表单设计开发者不需要每次去阅读饿了么的手册,直接在代码生成器上预览并复制使用即可,提供表单业务开发效率。

ElementUI表单预览

自定义开发

自定义页面的开发

通过后台配置绑定对应模版方式实现。可以实现专题页、活动页的效果

  • 以商城活动页为例

代码生成器的新增和导入同上。

新增一个活动页

标题为页面名称,不能重复。 路径关键字决定了访问地址的路径。后面会自动拼接.do

Tip

注意 自定义页面不需要静态化生成,即可预览最新效果。

需要登录的页面

如果此页面需要登录才能查看,路径需带有people/前缀。

Tip

注意 登录才能查看的页面,需要安装会员插件。

如:个人中心"people/center",对应访问地址:"域名/people/center.do"

传参数

多个自定义页面之间,可以互相传递参数。 页面发起请求携带参数,模板之中可以使用${id}获取。 也可以通过 js 工具类 ms.util.getParameter("id")获取

自定义模型开发

可以快速扩展现有的业务表数据。 当现有字段不满足业务需求,不推荐直接在数据库中加字段。使用自定义模型会更好的管理。

  • 以扩展文章字段为例

自定义模型的上传

从代码生成器中拖拽需要补充的字段。复制与上传同上图。 类型选择文章。

栏目绑定自定义模型字段

从内容管理中绑定自定义模型。

再从此栏目文章中进行编辑或者新增,都会出现自定义模型。

可参考文档:

插件手册

Mcms手册

自定义模型字段的前端获取。

前端中模型的显示是通过标签。 具体可以参考自定义模型标签使用

自定义配置的开发

快速实现基本配置管理业务,例如:系统设置、功能设置、上传设置等。

以文件上传配置为例

如图所示拉取组件,如上例保存与导入。 同理,可以进行表单预览。

相关代码可参考文档 插件手册

自定义配置的数据保存与获取

可通过数据预览,直接进行数据的保存。 可以把自定义的配置通过复制菜单JSON结合到菜单,参考上述自定义业务。

自定义字典

自定义字典是管理数据属性的一个通用管理工具,主要是管理数据的状态或属性

列如一个订单有很多状态:已下单、已付款、已发货、已收货、已签收、取消。就可以通过绑定字典来进行处理。

Tip

注意 系统中的下拉、单选、多选都可以通过字典来实现。

自定义字典添加

  1. 打开自定义模块,点击自定义字典
  2. 点击新增,类型可以选择现有类型,也可以输入新的类型
  3. 名称对应自定义字典展示的名称,数据值对应名称的值(如:男=man)

自定义字典使用

  • 下拉选择框使用

在代码生成器拖一个下拉选择框控件,选择字典数据,输入对应的字典类型 可以组合使用,只需要输入对应的字典类型 效果图

自定义业务开发

这里的自定义指的是基于铭飞系统里面的自定义模块,如:自定义模型、自定义表单。直接通过代码生成器生成自定义模型代码,通过导入的方式就可以完成自定义业务的扩展。

  • 以留言板为例

快速实现基础后台数据管理,也可以实现表单数据提交。例如:留言反馈。

留言板模型的拖拽

进入代码生成器,根据业务新建一个表单。

编辑表的组件与顺序

留言板的导入

开发者打开代码生成器,对于想要的组件进行拖拽

而后在后台的自定义模型中导入。对留言板的前端提交选择进行勾选。

此时可以通过表单预览,查看导入的表单是否和拖拽的一致。

如果需要更新,也可以通过更新模型按键。

实现留言板的前端提交

此时我们有了一个留言板数据模型,如果实现前台的提交,还需要有一个前台界面。 前台界面必须要绑定所提交的自定义业务名称。

模型的绑定和界面代码参考,可以参考插件手册 参考文档

留言数据的查看

前端提交的数据,可以通过数据预览去查看和管理。

菜单的管理和调整

此时开发者可以通过自定义业务中的留言模块去管理留言。 如果需要在主页面直接打开留言管理,可以通过复制菜单JSON,由菜单管理导入。 Alt text Alt text

业务代码模版

列出一些常用的业务的视图代码,开发者可以直接复制使用。

权限设置

界面效果

代码模版

elementUI树形表格示例

<el-table v-loading="loading" height="calc(100vh - 148px)" border :data="modelChildList" 
header-row-class-name='ms-table-head' row-class-name='ms-table-row' row-key="modelId"
 default-expand-all :tree-props="{children: 'children'}">
    <template slot="empty">
        {{emptyText}}
    </template>
    <el-table-column label="模块标题" prop="modelTitle" width="300"></el-table-column>
    <el-table-column label="功能权限">
        <template slot-scope="scope" class="ms-row">
            <div v-if= "scope.row.modelChildList.length>0">
                <label  class='ms-check'>
                    <el-checkbox-group v-model="roleIds" @change="handleCheckedIdsChange">
                        <el-checkbox v-for="model in scope.row.modelChildList" :label="model.modelId" :value='model.modelId' :key="model.modelId">{{model.modelTitle}}</el-checkbox>
                    </el-checkbox-group>
                </label>
            </div>
        </template>
    </el-table-column>
</el-table>

关键js代码

将后台返回的代码组织成树形数据,并且排除掉功能权限菜单

 ms.http.get(ms.manager + "/model/modelList.do", {"roleId":id}).then(function (data) {
    if (data.total > 0) {
        //菜单modelIsMenu=1的为菜单数据,按钮为0
        that.modelChildList =  ms.util.treeData(data.rows.filter(f => f['modelIsMenu'] == 1),'modelId','modelModelId','children')
        //循环数组
        for(var i =0; i<data.rows.length;i++){
            if(data.rows[i].modelChildList.length>0){
                for(var j=0;j<data.rows[i].modelChildList.length;j++){
                    //判断是否选中
                    if(data.rows[i].modelChildList[j].chick ==1 ){
                        that.roleIds.push(data.rows[i].modelChildList[j].modelId);
                    }
                }
            }
        }
        that.emptyText='';
        that.loading = false;
    } else {
        that.loading = false;
        that.emptyText='暂无数据'
        that.modelChildList =[];
    }
}).catch(function (err) {
    console.log(err);
});

注:具体代码详情可以在权限管理中查看

表单组件

新建业务表单

  • 名称:业务表单名称,同项目内表单名称唯一
  • 业务表名:对应创建的数据库表名称

其余配置留意页面配置项下方提示

基于https://element.eleme.cn/#/zh-CN(饿了么),同时也定制了一些特殊的控件,例如:地图、城市、百度编辑器等

组件属性

主要控制四个方面(页面显示控制、表字段控制、java代码控制、验证提示控制),注意:建议标题、字段名根据实际业务进行修改

基础组件

单行文本

  • 标题:页面上显示的输入框名称

  • 字段名:对应数据库表中的字段名

  • 默认值:文本输入框的默认内容

  • 操作属性

    • 完全只读:只读,无法编辑
    • 禁用:无法选中
    • 显示清除按钮:鼠标移动到输入框,右边有按钮,点击清除所有文本输入内容
  • 表单验证

    • 必填:必须填写,在提交表单时会有校验
    • 类型:限制文本输入的类型,类型在下方 类型下拉框中可以选择
    • 长度:限制文本输入的长度,长度在下方 最大、最小长度输入框中填写
    • 正则:限制文本的输入格式,正则在下方输入框中填写规则
    • 不可重复:表单提交数据时唯一校验
  • 占位内容:文本输入提示信息

  • 宽度:组件页面宽度的占比

  • 是否显示:设置组件是否显示在页面上

  • 帮助信息:在组件下方设置文字,用于对组件输入内容的提示信息

  • 字段长度:数据库字段长度

多行文本

密码框

  • 操作属性
    • 显示密码:在密码输入框右侧,点击按钮查看密码

金钱

  • 默认显示2位小数

计数器

  • 步长:点击"+"或"-",一次变化的步幅

单选按钮

  • 布局方式

    • 行内:选项排列在一行
    • 块级:选项排列在竖行
  • 静态数据:直接在代码生成器中设置,按照 值 选项添加即可

  • 远端数据

    • 远端链接:请求选项的链接
    • 值:远端链接的请求的值
    • 标签:设置获取值的参数

字典数据:参考自定义开发中的新建自定义字典

多选按钮

时间选择器

  • 是否为范围选择:开启后,设置时间范围且需要设置两个参数开始时间和结束时间,如 09:00:00 - 10:00:00,

日期选择器

  • 显示类型

    • year:年 格式YYYY
    • month:年月 格式YYYY-MM
    • date:年月日 格式YYYY-MM-dd
    • dates:多个date类型
    • datetime:年月日时分秒 格式YYYY-MM-dd HH:mm:ss
    • datetimeRange:datetime范围,需要设置两个参数 开始datetime和结束datetime,如2023-01-01 09:00:00 -2023-02-02 10:00:00
    • dateRange:date范围,需要设置两个参数,开始date和结束date,如2023-01-01 - 2023-02-02
  • 是否获取时间戳:保存成时间戳

评分

  • 允许半选:允许选中半颗星

颜色选择器

  • 支持透明度选择:开启后,在颜色选择区域下方有调节透明度的滑块

下拉选择框

  • 是否多选:能否多选下拉框中的选项
  • 是否可搜索:在下拉框输入,可以显示匹配的选项
  • 是否显示标签:开启后,直接显示 值 用于选项

多选列表搜索说明

场景说明:不同顺序选择相同值多个值时,保存的值不一致;导致搜索结果不符合预期;

eg: 选择 值2,值1进行保存,但是搜索时 选择值1,值2,搜索没有匹配结果

前提:使用源码进行开发

方案1: 在保存、更新和列表搜索时,按照既定选项顺序去处理保存和搜索,参考文章表单页中文章类型的处理

Alt text

方案2:自定义列表list方法,通过xml处理搜索,参考下方截图

Alt text

开关

滑动

  • 显示输入框:显示输入框来设置值

高级组件

自定义

图片

  • 宽度:设置图片的宽度,如100px
  • 最大上传数:设置上传图片的数量

附件

  • 可上传的格式:设置可上传文件的类型

编辑器

级联选择器

高德定位

备注:使用该组件请用户申请高德地图对应的秘钥。

城市选择器

  • 类型:按需选择城市分级程度,如省市县、省市县镇等

备注:使用该组件请下载mstore插件中城市数据,获取最新的city表获取对应的城市数据源。

布局组件

栅格布局

  • 栅格间隔:设置每个栅格放置组件的间隔
  • 列配置项:总数为24,几个列配置就能放入几个组件
  • 内容水平对齐:设置组件在栅格布局中水平摆放的位置
  • 内容垂直布局:设置组件在栅格布局中垂直摆放的位置

模板的开发与制作

区别于mcms中生成htm的模板,代码生成器的模板更加强大,通过制作不同的模板可以生成任意类型任意内容的代码。

本文中所有模板均指代码生成器模板,和cms模板无关。

在代码生成器中一个项目中有多个表单(一个表单对应一张表),表单之中可以有多个组件,两两为一对多关系

项目图

表单图

新建模板组

模板组名称需要在0-30个字之间;描述不得超过120个字;价格如果选择为0则代表这套模板您设定为免费,任何人都可以使用你创建的这套模板,如果价格大于0那只有收费用户以及购买了这套模板的用户可以使用(商城暂未开放)

图1

Tip

请确保你创建的模板是原创的且合法合规不具有任何攻击性!

新建模板

同属于一套模板组的模板是一个整体,如果模板组被删除则从属模板也会被全部删除,在制作模板时建议时常备份避免误删

模板名称

模板名称长度需在0-30之间,名称可以是中文或者英文,同一套模板中不能存在相同的模板名称

生成根路径

在下载的代码压缩包中,以最外层目录为起点的目录层级,通常设置为项目的固定路径 如/src/main/java/net/mingsoft/cms

当然这样的写法不够灵活,我们提供了占位符的方式来自动获取项目配置的方式,如:/src/main/java/${projectPath}/${projectName},使用规则如下:

  1. ${projectPath} 项目包路径,如 net/mingsoft

  2. ${projectName} 项目模块名,如 cms

  3. ${beanNameUpperCase} 首字母大写的bean名称,如 PeopleLog,一般用于文件名字段

  4. ${beanNameLowerCase} 首字母小写单词之间用横岗隔开,如 people-log,一般用于前端模板的路径字段

注意!目前只有四种占位符可用,且只能用于 "生成根路径"、"路径"、"生成文件名" 这三个字段才有效,在模板代码中使用占位符是不会生效的!

路径

拼接在 "生成根路径" 之后的目录层级,一般可用来填写控制层的 controller或者action,也可以是 entity或dto 亦或者 biz/impl 等等,可用占位符

生成文件名

模板的生成文件名可以为空,但是不会生成文件也不会被预览看到,如果勾选了不生成文件但是想要能参与预览可以随意设置一个生成文件名;

一般填写 ${beanNameUpperCase}Action.java,文件后缀是必须的。

Tip

注意,文件名在固定项目风格后不应随意修改,否则会导致项目文件和文件内容的类名不一致编译失败的情况,请及时调整文件名或者模板代码

是否生成文件

未开启生成的模板在项目下载代码时不会生成实际文件,但是不会影响到预览功能

是否宏模板

宏模板不会生成文件也不会在预览代码中出现,它会在每个其他类型的模板渲染的时候自动注入,因此其他模板中可以使用宏模板中的自定义宏方法

自定义排序

在模板渲染时会按照模板顺序由大到小依次渲染读取(注意和展示顺序无关)

模板代码

模板代码遵循freemarker语法,如果熟悉freemarker语法可以跳过freemarker语法讲解的部分只阅读标签规则快速开发

freemarker语法快速解读

为了更好理解开发模板,这里需要对几个常用的freemarker语法进行简单讲解

Tip

注意,标签闭合需要带/,否则会渲染失败

assign标签

<#assign item = 1>
定义了一个item变量,值为:${item}

输出结果:定义了一个item变量,值为:1

assign是定义变量的标签,如上示例,item被定义后就可以在定义后的模板部分用freemarker语法进行获取了, ${} 是freemarker取值的方式 官方文档

if标签

<#assign i = 10>
<#if i gt 10>
    定义的变量i大于10
<#elseif i gt 5>
    定义的变量i介于5到10之间
<#else>
    定义的变量i小于5
</#if>

输出结果:定义的变量i介于5到10之间

if else标签和其他语言的语法本质相同,需要搭配布尔类型,可以结合函数运算或者freemarker其他语法,如<#if i+1 gt 10></#if> 或者 <#if item?has_content></#if> 官方文档

list标签

[
<#list formItem.options.attachField as _formItem>
    <#if _formItem?index gt 0>,</#if>{"name": "${_formItem.name}"}
</#list>
]

输出结果:[{"name": "name1"},{"name": "name2"},{"name": "name3"}]

list标签和for循环相同,本质都是对一个集合进行遍历,list标签可以嵌套list实现嵌套循环,_formItem?index 作用是当前元素的下标 官方文档

示例

便于理解,下面展示五段模板作为示例内容

1.searchJson

<#--搜索json结构-->
[
<@widget field="getWidgetVue" key="condition"></@widget>
]

2.script

var custom_model = Vue.component("custom-model",{
    el: '#custom-model',
    data:function() {
        return {

            modelId:0,
            modelName: "${project.projectModelObjects[0].pmoName}",
            <@widget field="getWidgetVue" key="data"></@widget>
            //表单数据
            form: {
                linkId:0,
                <@widget field="getWidgetVue" key="form"></@widget>
            },

            rules:{
                <@widget field="getWidgetVue" key="rule"></@widget>
            },
        }
    },
    watch:{
        <@widget field="getWidgetVue" key="watch"></@widget>
    },
    components:{
        <@widget field="getWidgetVue" key="component"></@widget>
    },
    computed:{
        <@widget field="getWidgetVue" key="computed"></@widget>
    },
    methods: {
        <@widget field="getWidgetVue" key="method"></@widget>
        <#if table.pmoDialog>
            open:function(id){
                <@widget field="getWidgetVue" key="create"></@widget>
                if (id) {
                    this.get(id);
                }
                <@widget field="getWidgetVue" key="get"></@widget>
                this.$nextTick(function () {
                    this.dialogVisible = true;
                })
            },
        </#if>
        validate:function(){
            var b = false
            this.$refs.form.validate(function(valid){
                b = valid;
            });
            return b;
        },
        save:function(callback) {
            var that = this;
            var url = this.formURL.save.url;
            if (that.form.id > 0) {
                url = this.formURL.update.url;
            }
            this.$refs.form.validate(function(valid) {
                if (valid) {
                    <@widget field="getWidgetVue" key="saveValidate"></@widget>
                    <#--注意下面进行了数据转换-->
                    <@widget field="getWidgetVue" key="save"></@widget>
                    var form = JSON.parse(JSON.stringify(that.form));
                    form.modelId = that.modelId;
                    ms.http.post(url, form).then(function (res) {
                        if(callback) {
                            callback(res);
                        }
                    });
                }
                else{
                    callback({
                        result:false,msg:'请检查表单输入项'
                    });
                }
            })
        },
        //获取当前${table.remarks}
        get:function(id) {
            var that = this;
            ms.http.get(this.formURL.get.url, Object.assign({"modelId":that.modelId},this.formURL.get.params)).then(function (res) {
                <@widget field="getWidgetVue" key="get"></@widget>
                if(res.result&&res.data){
                    that.form = res.data;
                }
            }).catch(function (err) {
                console.log(err);
            });
        },

    },
    created:function() {
        var that = this;
        //渲染create
        <@widget field="getWidgetVue" key="created"></@widget>
        that.get(this.form.linkId);
    }
});

Tip

数组值保存失败(如日期范围),一般都是模板中saveValidate的form指向错误; 如果需要对表单中的值做处理,需要在saveValidate中处理,避免修改双向绑定数据导致组件报错

3.html

<#assign beanName=table.beanName[0]?lower_case+table.beanName?substring(1)/>
<#--    引入文件处理转换数据-->

<template id="custom-model">
    <#assign formConfig=JSON.parse(table.attachJson)/>

    <#--这里是整个form-->
    <el-form ref="form" :model="form" :rules="rules" label-width="${formConfig.labelWidth}px" label-position="${formConfig.labelPosition}" size="${formConfig.size}">

        <#list JSON.parse(table.json) as row>
        <#--判断是否是栅格-->
            <#if row.type == 'grid'>
                <el-row
                        :gutter="${row.options.gutter}"
                        justify="${row.options.justify}" align="${row.options.align}">
                    <#-- 栅格就取列中的list进行遍历-->
                    <#list row.columns as column>
                        <el-col :span="${column.span}">
                            <#list column.list as formItem>
                            <#--这里循环渲染组件-->
                                <@widget field="getWidgetHtml" formItem=formItem></@widget>
                            </#list>
                        </el-col>
                    </#list>
                </el-row>
            <#else>
            <#-- 不是就直接渲染,因为里面的模板都用了formItem作为参数所以需要赋值-->
                <@widget field="getWidgetHtml" formItem=row></@widget>
            </#if>
        </#list>
    </el-form>
</template>

4.field

[
<#list JSON.parse(table.json) as formItem>
        <#if formItem.type !='primarykey' && (formItem.pmofType)?? && formItem.pmofAllowGenerater==1>
            <@filedJson formItem=formItem index=formItem?index></@filedJson>
            <#if formItem.options.attachField??>
                <#list formItem.options.attachField as _formItem>
                    <#if _formItem?index == 0 >,</#if><@filedJson formItem=_formItem index=_formItem?index></@filedJson>
                </#list>
            </#if>
        <#elseif formItem.type =='grid'>
            <#list formItem.columns as col>
                <#list col.list as __formItem>
                <@filedJson formItem=__formItem index=__formItem?index></@filedJson>
                </#list>
            </#list>
        </#if>
</#list>
]
<#macro filedJson formItem index>
        <#if index gt 0 >,</#if>{
        "model":"${JavaBeansUtil.getCamelCaseString(formItem.model,false)}",
        "key":"${formItem.model}",
        "field":"${formItem.model}",
        "javaType":"${formItem.pmofJavaType}",
        "jdbcType":"${formItem.pmofType}",
        "name":"${formItem.name}",
        "type":"${formItem.type}",
        <#if formItem.type == "date">
            "fmt":"${formItem.options.format}",
        </#if>
        <#if formItem.type == "date"  && (formItem.options.type=="daterange" || formItem.options.type=="datetimerange") && formItem.options.attachField??>
        "endModel":"${formItem.options.attachField[0].model?lower_case}",
        </#if>
    <#if formItem.type == "time" && formItem.options.isRange && formItem.options.attachField??>
        "endModel":"${formItem.options.attachField[0].model?lower_case}",
    </#if>
        "length":"${formItem.pmofLength?default(0)?c}",
        "isShow":"<#if formItem.options.table.show??>${formItem.options.table.show?c}<#else>false</#if>"
        }
</#macro>

5.modelSql

<#list project.projectModelObjects as projectModelObject>
    <#if projectModelObject?? && projectModelObject.pmoAllowGenerate==1>
         <#--_prefix判断是否提前配置好了前缀,一般用于自定义模型  -->
        <#assign _tableName= '{model}' + projectModelObject.pmoTableName?upper_case/>

        -- ${projectModelObject.pmoTableName?upper_case}
        CREATE TABLE `${_tableName}` (
            `id` BIGINT(19) UNSIGNED NOT NULL AUTO_INCREMENT,
            <#list projectModelObject.projectModelObjectFields as projectModelObjectField>
                <#if !ignoreFields?seq_contains(projectModelObjectField.pmofName?lower_case) && projectModelObjectField.pmofAllowGenerater==1>
                        <#if projectModelObjectField.pmofType == "DATE" || projectModelObjectField.pmofType == "DATETIME"  || projectModelObjectField.pmofType == "TIME" || projectModelObjectField.pmofType == "TEXT">
                `${projectModelObjectField.pmofName}` ${projectModelObjectField.pmofType} DEFAULT NULL COMMENT '${projectModelObjectField.pmofDescription}',
                        <#elseif projectModelObjectField.pmofType == "DECIMAL">
                `${projectModelObjectField.pmofName}` ${projectModelObjectField.pmofType?upper_case}(${projectModelObjectField.pmofLength?c},3) DEFAULT NULL COMMENT '${projectModelObjectField.pmofDescription}',
                        <#else>
                `${projectModelObjectField.pmofName}` ${projectModelObjectField.pmofType?upper_case}(${projectModelObjectField.pmofLength?c}<#if projectModelObjectField.pmofType == "DOUBLE">,0</#if>) DEFAULT NULL COMMENT '${projectModelObjectField.pmofDescription}',
                        </#if>
                </#if>
            </#list>
            `LINK_ID` BIGINT(20) DEFAULT NULL,
            <#--判断是否是自定义表单-->
            <#if projectModelObject.pmoTree>
            `LEAF` INT(1) NULL DEFAULT NULL COMMENT '是否是叶子节点',
            `TOP_ID` BIGINT(20) NULL DEFAULT NULL COMMENT '顶级ID',
            `PARENT_ID` VARCHAR(255) CHARACTER SET UTF8 COLLATE UTF8_GENERAL_CI NULL DEFAULT NULL COMMENT '父节点',
            `PARENT_IDS` VARCHAR(255) CHARACTER SET UTF8 COLLATE UTF8_GENERAL_CI NULL DEFAULT NULL COMMENT '父类型编号,多个ID逗号',
            </#if>
            `CREATE_DATE` DATETIME DEFAULT NULL COMMENT '创建时间',
            `CREATE_BY` VARCHAR(50) DEFAULT NULL COMMENT '创建人',
            `UPDATE_DATE` DATETIME DEFAULT NULL COMMENT '修改时间',
            `UPDATE_BY` VARCHAR(50) DEFAULT NULL COMMENT '修改人',
            `DEL` INT(1) DEFAULT 0 COMMENT '删除标记',
            PRIMARY KEY (`ID`) USING BTREE
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='${projectModelObject.pmoDescription}';
        ALTER TABLE `${_tableName}`
        ADD UNIQUE INDEX `${_tableName}_UNIQUE_LINK_ID`(`LINK_ID`) USING BTREE COMMENT 'link_id唯一索引';
    </#if>
</#list>

widget标签

widget标签用于获取一些组件的特殊代码,用法较为固定,实际开发模板过程中并不常用

如示例1的searchJson模板,widget标签用法为: <@widget field="getWidget*" * ></@widget>

其中field参数目前有五种写法:

1.getWidgetHtml,用于获取组件html代码,同样也常用于html模板中,固定写法 <@widget field="getWidgetHtml" formItem> (formItem是变量,具体根据实际模板中获取的值进行填写,所有模板都有html部分)

2.getWidgetHead,获取组件head代码,常用于html模板头部获取额外资源文件,固定写法 <@widget field="getWidgetHead"></@widget> 。(目前只有高德地图和mce编辑器两个组件有需要)

3.getWidgetOnload,获取组件预加载代码,常用于vue实例外在页面初始化时执行,固定写法 <@widget field="getWidgetOnload"></@widget> (目前只有高德地图组件有需要)

4.getWidgetCss,获取组件css代码,用于底部style代码中,固定写法 <@widget field="getWidgetCss"></@widget> (目前只有mce编辑器有需要)

5.getWidgetVue,获取组件在vue实例中特殊代码部分,常用于scrip代码中。格式 <@widget field="getWidgetVue" key="data"></@widget> (所有组件都有自己的vue对象)。

key值可以是 data、form、watch、method、created、get、rule、computed、component、save、saveValidate、condition,参考示例2。

Tip

虽然一个组件不会包含所有值,但是在编写模板的时候应尽量考虑通用性,如getWidgetCss,尽管可能不会用到mce编辑器,但模板中应使用widget标签取值确保模板通用性

可用参数

在模板中通过${}来获取项目或表单以及组件的参数值,除此之外还有一些其他的固定可用参数。

  1. beanName,首字母小写驼峰式的表名称 如peopleLog
  2. localEnable,是否国际化,布尔类型可以直接在if标签里当做条件判断
  3. title,表单名称(只有预览的时候有效)
  4. ignoreFields,排除字段,固定值 {"id", "del", "update_date", "update_by", "create_date", "create_by", "rand_code"}
  5. JSON,json工具类,可以在freemarker中通过 ${JSON.parse(str)} 的方式执行工具类方法输出,可用方法有: parse(Object o)toJSONString(String str)
  6. UnicodeUtil,unicode工具类,同json工具类,可用方法参考hutool5.8.9版本cn.hutool.core.text.UnicodeUtil
  7. JavaBeansUtil,bean工具类,同json工具类,可用方法有: getCamelCaseString(String str) 转首字母小写驼峰式
  8. ConversionUtil,字符串转换工具类,同json工具类,可用方法有: humpToLine(String str,String format) 驼峰转成单词之间用format隔开、 toJSONStr(String str) 将字符串转换成json格式,就算不是json格式也会转义"、'并且用双引号包裹
  9. project,项目数据(下面详解)
  10. table,表单数据(下面详解)
project参数

常见问题

5.5.0以下代码生成器使用

Tip

注意 5.5.0以下的版本,如需要下载源码使用,还需将新版本swagger替换为老版本的swagger,替换可以参考:swagger替换

Tip

注意 开启正则规则进行替换,idea可以通过ctrl+shift+R快速替换如图 Alt text

老版本swagger升级步骤

  1. 包名替换(换成v3的包)

import io.swagger.annotations.Api; 替换成 import io.swagger.v3.oas.annotations.tags.Tag;

import io.swagger.annotations.ApiImplicitParam; 替换成  import io.swagger.v3.oas.annotations.Parameter;\n  import io.swagger.v3.oas.annotations.enums.ParameterIn;

import io.swagger.annotations.ApiImplicitParams; 替换成  import io.swagger.v3.oas.annotations.Parameters;

import io.swagger.annotations.ApiOperation;  替换成 import io.swagger.v3.oas.annotations.Operation;

import springfox.documentation.annotations.ApiIgnore; 替换成 import io.swagger.v3.oas.annotations.Hidden;

Alt text

  1. @Api(tags="...")替换

可用规则
\@Api\(tags\s*=\s*?\s*"([^"]*)"\s*(?:,\s*"([^"]*)"\s*)*?\) 替换成 \@Tag\(name \= \"$1\"\)
        
\@Api\(value\s*=\s*?\s*"([^"]*)"\s*(?:,\s*"([^"]*)"\s*)*?\) 替换成 \@Tag\(name \= \"$1\"\)

Alt text

  1. @ApiOperation(value = "...") 替换

可用规则
\@ApiOperation\(\s*value\s*=\s*"([^"]*)"\s*\) 替换成 \@Operation\(summary \= \"$1\"\)

Alt text

  1. @ApiImplicitParam("....") 及 @ApiImplicitParams("....") 替换

\@ApiImplicitParam\(\s*name\s*=\s*\"(.*?)\"\s*,\s*value\s*=\s*\"(.*?)\"\s*,\s*required\s*=\s*(.*?)\s*,\s*paramType\s*=\s*\"query\"\s*\)  
替换成 
\@Parameter\(name = \"$1\", description \= \"$2\", required \= $3, in \= ParameterIn.QUERY)

\@ApiImplicitParam\(\s*name\s*=\s*\"(.*?)\"\s*,\s*value\s*=\s*\"(.*?)\"\s*,\s*required\s*=\s*(.*?)\s*,\s*paramType\s*=\s*\"path\"\s*\)
替换成
\@Parameter\(name = \"$1\", description \= \"$2\", required \= $3, in \= ParameterIn.PATH)

\@ApiImplicitParam\(\s*name\s*=\s*\"(.*?)\"\s*,\s*value\s*=\s*\"(.*?)\"\s*,\s*required\s*=\s*(.*?)\s*,\s*paramType\s*=\s*\"form\"\s*\)
替换成
\@Parameter\(name = \"$1\", description \= \"$2\", required \= $3, style \= ParameterStyle.FORM)

\@ApiImplicitParam\(name \= \"(.*?)\"\, value \= \"(.*?)\"\, required \=(.*?)\,paramType\=\"form\"\,defaultValue\=\"(.*?)\"\)
替换成
\@Parameter\(name = \"$1\", description \= \"$2\", required \= $3, style \= ParameterStyle.FORM, schema = @Schema(defaultValue = \"$4\"\))

@ApiImplicitParams 替换成  @Parameters

Alt text

  1. @ApiIgnore 替换
如果在类上或方法上
@ApiIgnore\n  替换成  @Hidden\n

Alt text

如果作为方法参数
@ApiIgnore  替换成  @Parameter(hidden = true)

Alt text

新版本swagger 换成 老版本swagger步骤

  1. 包名替换
将
import io.swagger.v3.oas.annotations.tags.Tag; 替换成 import io.swagger.annotations.Api;  

import io.swagger.v3.oas.annotations.Parameter;\nimport io.swagger.v3.oas.annotations.enums.ParameterIn; 替换成  import io.swagger.annotations.ApiImplicitParam; 

import io.swagger.v3.oas.annotations.Parameters;  替换成  import io.swagger.annotations.ApiImplicitParams; 

import io.swagger.v3.oas.annotations.Operation;  替换成 import io.swagger.annotations.ApiOperation;   

import io.swagger.v3.oas.annotations.Hidden;  替换成 import springfox.documentation.annotations.ApiIgnore;  

Alt text

  1. @Tag(name = "....")替换
可用规则
@Tag\(name\s*=\s*\{?\s*"([^"]*)"\s*(?:,\s*"([^"]*)"\s*)*\}?\) 替换成 \@APi\(tags \= \"$1\"\)

Alt text

  1. @Operation(summary = "...") 替换
可用规则
\@Operation\(\s*summary\s*=\s*"([^"]*)"\s*\) 替换成 \@ApiOperation\(value \= \"$1\"\)

Alt text

  1. @ApiImplicitParam("....") 及 @ApiImplicitParams("....") 替换
可用规则
@Parameters  替换成 @ApiImplicitParams 

\@Parameter\(\s*name\s*=\s*\"(.*?)\"\s*,\s*description\s*=\s*\"(.*?)\"\s*,\s*required\s*=\s*(.*?)\s*,\s*in\s*=\s*ParameterIn.QUERY\s*\)
替换成
\@ApiImplicitParam\(name = \"$1\", value \= \"$2\", required \= $3, paramType \= "query")


\@Parameter\(\s*name\s*=\s*\"(.*?)\"\s*,\s*description\s*=\s*\"(.*?)\"\s*,\s*in\s*=\s*ParameterIn.QUERY\s*\)
替换成
\@ApiImplicitParam\(name = \"$1\", value \= \"$2\", paramType \= "query") 

Alt text

  1. @ApiIgnore 替换
如果在类上或方法上
@Hidden\n  替换成  @ApiIgnore\n  

Alt text

如果作为方法参数
@Parameter(hidden = true) 替换成 @ApiIgnore  

Alt text

tiny-mce编辑器使用步骤

Tip

使用前需先在Mstore下载新版本富文本编辑器插件,并导入项目。导入参考

  1. 先重写head-file文件,在basic下找到head-file.ftl文件 Alt text

  2. 拷贝到mcms项目的WEB-INF/manager目录下 Alt text

  3. 引入资源和key,挂载组件

<!--tinymce编辑器-->
<script src="${base}/static/plugins/tinymce/5.10.2/tinymce.min.js"></script>
<script src="${base}/static/plugins/tinymce-vue/5.1.2-rc/tinymce-vue.min.js"></script>


app.component("Editor",Editor);

Alt text Alt text

高德地图使用步骤

Tip

高德地图需要提供第三方的key与密钥,故需要手动引入,步骤如下:

  1. 先重写head-file文件,在basic下找到head-file.ftl文件 Alt text

  2. 拷贝到mcms项目的WEB-INF/manager目录下 Alt text

  3. 引入资源和key,使用组件,高德地图key值获取

<!--高德地图-->
<script src="${base}/static/plugins/vue-amap/2.0.0/index.min.js"></script>
<link rel="stylesheet" href="${base}/static/plugins/vue-amap/2.0.0/style.min.css">

window.VueAMap.initAMapApiLoader({
        key: 'ea56505722bb1717xxxxx', 填写高德地图key
        securityJsCode:'070253fc3b619e83xxxxx' 密钥
});


app.use(VueAMap);

Alt text Alt text

  1. 正常使用代码生成器托高德地图即可

老版本(5.4.3以下)使用附件、图片组件,通过{@ms:file 图片或文件/}获取不到值

修改web-inf下的macro.ftl文件,或直接在mcms的web-inf下创建macro.ftl,效果一样

<#macro ms_file jsonString>
    <#if jsonString?? && jsonString != '' && jsonString != '[]'>
        <@compress>
            <#assign jsonObject = jsonString?eval[0]>
            ${jsonObject.url!'${jsonObject.path}'}
        </@compress>
    </#if>
</#macro>

<#macro ms_len text len>
    <#if text?length lt (len+1)?number>
        ${text}
    <#else>
        ${text[0..len?number]}...
    </#if>
</#macro>


老版本(5.4.0以下)MCms使用新版本代码生成器方法

准备head-file

<meta charset="utf-8">

<!--网络请求框架-->
<script src="${base}/static/plugins/axios/0.18.0/axios.min.js"></script>
<script src="${base}/static/plugins/qs/6.6.0/qs.min.js"></script>

<script type="text/javascript" src="${base}/static/plugins/vue/3.4.25/vue.global.js"></script>
<script src="${base}/static/plugins/vue-i18n/8.18.2/vue-i18n.js"></script>

<!-- 图标 -->
<link rel="stylesheet" type="text/css" href="${base}/static/plugins/iconfont/1.0.0/iconfont.css" />
<script src="${base}/static/plugins/iconfont/1.0.0/iconfont.js"></script>

<!-- element-plus引入 -->
<link rel="stylesheet" type="text/css" href="${base}/static/plugins/element-plus/2.7.1/index.css" />
<script src="${base}/static/plugins/element-plus/2.7.1/index.full.js"></script>
<script src="${base}/static/plugins/element-plus/2.7.1/zh-cn.js"></script>
<link rel="stylesheet"  type="text/css" href="${base}/static/plugins/element-icons/icon.css" />

<!--金额转换-->
<script src="${base}/static/plugins/accounting/0.4.1/accounting.js"></script>
<!--时间转换-->
<script src="${base}/static/plugins/moment/2.24.0/moment.min.js"></script>

<!--百度编辑器-->
<script src="${base}/static/plugins/vue-ueditor-wrap/3.0/vue-ueditor-wrap.umd.js"></script>

<!--复制-->
<script src="${base}/static/plugins/clipboard/clipboard.js"></script>
<!--通用样式-->
<link rel="stylesheet" href="${base}/static/css/app.css"/>
<!--主题-->
<link rel="stylesheet" href="${base}/static/css/theme.css">


<!--代码编辑-->
<script src="${base}/static/plugins/codemirror/5.48.4/codemirror.js"></script>
<link href="${base}/static/plugins/codemirror/5.48.4/codemirror.css" rel="stylesheet">
<script src="${base}/static/plugins/codemirror/5.48.4/mode/css/css.js"></script>
<script src="${base}/static/plugins/codemirror/5.48.4/addon/scroll/annotatescrollbar.js"></script>
<script src="${base}/static/plugins/codemirror/5.48.4/mode/xml/xml.js"></script>
<script src="${base}/static/plugins/codemirror/5.48.4/mode/javascript/javascript.js"></script>
<script src="${base}/static/plugins/codemirror-editor-vue3/2.5.8/codemirror-editor-vue3.umd.js"></script>
<!--自定义-->
<script src="${base}/static/plugins/vue3-sfc-loader/0.9.5/vue3-sfc-loader.js"></script>
<script>
    //必须先设置对象,否则自定义无法渲染
    window.vue3SfcLoader = window["vue3-sfc-loader"];
</script>
<!--铭飞-->
<script src="${base}/static/plugins/ms/3.0/ms.umd.js"></script>
<script src="${base}/static/plugins/ms/3.0/ms-el-form.umd.js"></script>
<link rel="stylesheet"  type="text/css" href="${base}/static/plugins/ms/3.0/ms-el-form.css" />
<script src="${base}/static/mdiy/index.js"></script>

<!-- 此部分是铭飞平台MStroe的客户端(MStore不在铭飞开源产品范围),如果不需要使用MStore可以删除掉 -->
<script src="${base}/static/plugins/ms/3.0/ms-store.umd.js"></script>
<link rel="stylesheet"  type="text/css" href="${base}/static/plugins/ms/3.0/ms-store.css" />

<!--高德地图-->
<script src="${base}/static/plugins/vue-amap/2.0.0/index.min.js"></script>
<link rel="stylesheet" href="${base}/static/plugins/vue-amap/2.0.0/style.min.css">


<script >
    window.VueAMap.initAMapApiLoader({
        key: '33307e966f7ce225e2073fcea538553e',
        securityJsCode:'701ae19d1eabdd830a68129d7415e4d0'
    });
    ms.base = "${base}";
    ms.login = "${managerPath}/login.do";
    ms.manager = "${managerPath}";
    ms.web = ms.base;

    ms.editorConfig= {
        imageScaleEnabled :true,
        autoHeightEnabled: true,
        autoFloatEnabled: false,
        scaleEnabled: true,
        compressSide:0,
        maxImageSideLength:1000,
        maximumWords: 2000,
        initialFrameWidth: '100%',
        initialFrameHeight: 400,
        serverUrl: ms.base + "/static/plugins/ueditor/1.4.3.3/jsp/editor.do?jsonConfig=%7BvideoUrlPrefix:\'\',fileManagerListPath:\'\',imageMaxSize:204800000,videoMaxSize:204800000,fileMaxSize:204800000,fileUrlPrefix:\'\',imageUrlPrefix:\'\',imagePathFormat:\'/${app.id}/editor/%7Btime%7D\',filePathFormat:\'/${app.id}/editor/%7Btime%7D\',videoPathFormat:\'/${app.id}/editor/%7Btime%7D\'%7D",
        UEDITOR_HOME_URL: ms.base + '/static/plugins/ueditor/1.4.3.3/'
    }

</script>
<script >


    /**
     * 封装vue创建过程,方便初始化组件
     * @param obj
     * @returns vue实例
     * @private
     */
    function _Vue(obj) {

        var app = Vue.createApp(obj);
        app.config.globalProperties.ms = ms;
        app.use(ElementPlus,{
            locale: ElementPlusLocaleZhCn
        });
        app.use(MsElForm);
        app.use(MsStore);
        app.use(VueAMap);
        app.use(VueUeditorWrap);
        app.use(InstallCodemirro, { componentName: "codemirror" });
        app.config.globalProperties.moment = window.moment;
        app.config.globalProperties.moneyFormatter = function (row, column, cellValue, index) {
            if (cellValue != undefined) {
                return accounting.formatMoney(cellValue, '¥')
            } else {
                return ''
            }
        }
    
        app.component(obj.components);
        return app.mount(obj.el);
    }
</script>
<style>
    .ms-admin-menu .is-active {
        border: 0px !important;
    }

</style>



其他资源文件

// 从开源最新版本获取下面的资源,并覆盖更新到当前版本src/main/webapp/static/plugins/目录下

src/main/webapp/static/plugins/element-icons
src/main/webapp/static/plugins/element-plus
src/main/webapp/static/plugins/vue-amap
src/main/webapp/static/plugins/ms
src/main/webapp/static/plugins/vue
src/main/webapp/static/plugins/vue3-sfc-loader
src/main/webapp/static/plugins/codemirror-editor-vue3
src/main/webapp/static/plugins/vue-ueditor-wrap

下载源码使用

对于源代码的ftl文件,需要将include的路径换成上面head-file的路径

引用新版本head-file示例图

自定义模型使用

推荐更新到最新版本

后台页面国际化设置

可以通过mysql-国际化模板组实现,以下是效果图 国际化-英文 国际化-中文

Tip

注意 已有页面不会直接设置成国际化,可以通过国际化模板下载代码后,参考添加

代码生成器生成脚手架页面

使用mysql-vue模板组,拖拽组件后,下载代码的结构如下 mysql-vue模板下载代码结构

  1. 下载后,将src文件夹覆盖到后端,views文件夹覆盖到脚手架;
  2. 通过代码生成器编辑菜单->初始化菜单->复制菜单,将菜单导入到系统中(权限管理->菜单管理处导入),完成脚手架路由的配置

组件设置不可重复属性

  1. 设置不可重复表示该组件的值唯一
  2. 后台新增和更新都对组件值做了唯一校验
  3. 前端校验需要手动添加,下面给一个示例 不可重复示例
// form.ftl 表单页
...
    data:function() {
            var checkRepeat = function (rule, value, callback){
                ms.http.get(ms.manager + "/norepeat/norepeatInput/verify.do",{
                    fieldName: "input_no_repeat",
                    fieldValue: value,
                    id: ms.util.getParameter("id"),
                    idName: "id"
                }).then(function (res){
                    if (res.result) {
                        if (!res.data) {
                            callback("名称值已存在!");
                        } else {
                            callback();
                        }
                    }
                })
            }
            return {
                //表单数据
                form: {
                    // 名称
                    inputA:'',
                },
                rules:{
                    // 名称
                    inputA: [{validator:checkRepeat,trigger: ['change']}],
                }
...

下载的代码放怎么使用?

代码是基于开源项目MCms,方便用户快速二次开发,使用的引用都在开源项目中,直接放在开源项目,导入菜单数据即可。

生成的代码怎么看界面?

  1. 先通过项目拖动好您需要的业务表单页,并保存
  2. 在代码生成器项目列表中获取菜单数据,菜单数据可以初始化,开发者也可以加所需要的菜单。
  3. 点击复制菜单,在后台菜单管理中点击导入菜单。 代码生成器菜单

导入菜单数据后图标显示不全?

确保开源项目代码是跟master分支同步,所引用的图标都在static目录中。

导入菜单的入口在哪,菜单管理中没有这个功能?

导入菜单功能是基于basic1.0.17以上版本,在项目中使用最新的依赖即可。

<dependency>
    <groupId>net.mingsoft</groupId>
    <artifactId>ms-basic</artifactId>
    <version>1.0.17</version>
</dependency>

生成的代码城市数据没有怎么办?

使用该组件请下载mstore插件中城市数据,获取最新的city表获取对应的城市数据源。

代码生成器数据库字段名为保留关键词怎么办?

  1. 先试用其他字段名保存业务表单
  2. 下载源码后,通过批量替换一下对应的sql和xml等相关涉及的地方

通过分享插件导入的模板,自定义业务筛选功能不可用

  1. 在自定义业务找到这个模型,复制,业务名称,表名 img.png
  2. 平台代码生成器 ,新建表单,表名去除前面的MDIY_FORM_前缀 img_1.png
  3. 在根据字段信息,托组件,注意字段名称需与原模型一致 img_3.png img_2.png
  4. 表单托好后复制代码,再更新模型即可正常恢复筛选 img_4.png img_5.png

通过分享插件导入模板,报时间戳新增异常

Alt text 原因:旧分享插件导出的自定义业务数据中时间为时间戳格式,mysql不支持直接新增时间戳

旧的模板无法导入解决方式:

找到下载的模板,在/data目录下,删除数据文件DATA_MODEL_JSON_MDIY_FORM_xx Alt text

然后在根目录下将data、模板、html打包成压缩包,

Tip

注意打包的压缩包,解压后的目录结构要与下载的模板目录结构一致。

Alt text

最后使用新打包的压缩包导入。

Tip

在最新的分享插件已经规范导入导出的时间,不会出现时间戳问题。

分享皮肤

价值源自分享!!! 通过分享给我们带来不断的社会价值,从现在开始打开您的电脑看看有没有之前或现在制作完成的皮肤,都可以分享到MStore。

模板分享插件

分享插件可以在Store安装

导出动图演示

安装好插件会新增 MStore分享 模块, 点击导出下载获得当前系统的模板与演示数据

导出的文档结构说明

--default.zip    
    |-- data 演示数据
    |   |   |-- upload 导出上传资源文件
    |   |   |-- APP.json 应用设置数据
    |   |   |-- CMS_CATEGORY.json 栏目数据
    |   |   |-- CMS_CONTENT.json 文章数据
    |   |   |-- MDIY_MODEL_*.json 自定义模型数据
    |   |   |-- MODEL_JSON_MDIY_MODEL_*.json 自定义模型配置数据   
    |-- default   模板文件夹(具体当前模板文件夹名称)  
    |   |-- *.htm 模板文件
    `-- html 静态化演示文件

Tip

  • 如果上传已经存在的模板,封号处理
  • 导出的模板必须进行导入验证,如果导入失败,模板退回处理。
  • 模板压缩包大小20M内,超过20M请自行压缩

分享模板

可以快速将模板分享到MStore赚取收益,导出的模板必须进行导入验证之后再分享到MStore。导入的模版静态化后页面正常显示就可以满足Mstore的分享条件

验证模板步骤

  1. 使用分享插件导出当前制作完成的模板压缩包。
  2. 删除 html\template\upload 文件夹(注意做好备份)。
  3. 将第1步导出的压缩包重新导入到系统中。
  4. 手动静态化 首页、文章、栏目 检查导入模板是否存在报错或显示异常。
  5. 若出现错误,请修改模板代码后,重新从第 1 步开始验证。

分享模板步骤

  1. 请确保已完成上述 验证模板步骤 并确认模板无误。
  2. 登录并访问 MS平台
  3. 点击主页分享模板按钮
  4. 根据表单要求填写模板信息。
  5. 核对信息无误后,点击 确认保存 按钮完成分享。

模板定价参考

目前定价参考只针对CMS建站类模板,后续其他的皮肤

仿站 10元起

增加条件价格(元)
支持响应式+(10~50)
内容栏目演示数据+(10~20)
自定义业务+(10~20)
自定义模型+(10~20)

原创 100元起

增加条件价格(元)
支持响应式+(50~100)
内容栏目演示数据+(10~20)
自定义业务+(10~20)
自定义模型+(10~20)

Tip

价格变动会第一时间更新次文档,如果上架模板不符合定价标准,系统将审核失败进行退回处理。

分享收益

导出的模板可以快速分享到MStore

MStore中具有响应式效果的皮肤可以通过分享插件快速导入,导入的数据与演示系统一致

收益提现

收费的皮肤会给分享者带来实际的收益,通过会员中心提现功能即可提取

使用模板

安装分享插件后,点击Store分享,根据输入框提示填写,填写完成后,点击保存

img.png

在Store使用模板弹窗中填写Store分享配置的信息,建议直接复制对应输入框

img_1.png

检查所有信息填写无误后,点击弹窗底部的确认按钮。 系统将自动处理模板导入,请耐心等待,直到出现 使用完成 的提示消息

Tip

请务必先安装分享插件,否则无法进行操作

常见问题

导入含有自定义业务的模板问题

新增自定义业务数据错误,异常信息id doesn't has default value,一般是由于自定义业务表的id是varchar类型导致的

上面情况的处理方法:

# 以mysql为例
ALTER TABLE 自定义表名 DROP PRIMARY KEY;
ALTER TABLE 自定义表名 MODIFY id INT AUTO_INCREMENT PRIMARY KEY;

规范问题

分享导出模板时,请遵守导出规范

  1. 对于所有使用在模板中的图片、视频等资源,一律存放在当前模板文件夹内的文件夹中,禁止放在upload文件夹下(除非这个资源有被文章、栏目引用),否则该资源会在导出时被删除 模板资源规范

  2. 对于所有在文章、栏目、自定义模型中使用到的资源,一律存放在upload文件夹下

模板导出失败

导出时,应用设置未绑定模板 应用设置绑定模板

导出模板超过100M异常的解决方法

  • 通过处理模板文件夹(推荐)
  1. 将当前模板文件夹(应用设置绑定的模板,默认是template/1/下,站群为template/appId下)下的大文件夹(如图片),手动从当前项目中移出后,再导出;

处理模板文件夹

  1. 将导出的压缩包解压,然后把移出的文件夹,放入模板名称文件夹下

还原模板文件

  1. 手动压缩文件,还原压缩包,注意压缩的层级要和导出的层级一致

还原压缩包

Tip

不可以将模板文件移出

  • 通过处理upload文件夹
  1. 将upload/appId(默认是1,站群为upload/当前站id)下的文件移出

处理upload资源文件

  1. 将导出的压缩包解压,然后把移出的文件夹,放入data/upload下

还原upload文件

  1. 手动压缩文件,还原压缩包,注意压缩的层级要和导出的层级一致

导入模板失败

根据系统异常日志信息,定位问题 以下列举一些情况

  1. 数据库配置忽略大小写 一般错误为xxx表doesn't exist 如:Alt text

Tip

解决方案参考: 数据库大小写忽略

  1. 表已存在, 在自定义业务或自定义模型中,找到表名那条数据删除

  2. 导入文件过大,修改yml配置ms.upload.multipart.max-file-size

Tip

解决参考:文件过大

导入模板覆盖数据说明

1、非站群环境(默认):执行导入zip模板过程 首先删除旧文章、栏目数据和upload/1文件夹下的文件,再导入新的文章、栏目、upload/1下文件和自定义模型数据。

2、站群环境:执行导入zip模板过程 当前站点id说明 执行导入的模板只会删除当前站点id下旧文章、栏目数据和upload/当前站点id文件夹下的文件,再在当前站点下导入新的文章、栏目、upload/当前站点id下文件和自定义模型数据。

分享插件

价值源自分享!!! 一起来分享好用、实用的插件。

源码包

源码包目录结构

包含完整的业务代码,源码包需上传完整的src文件夹

如果已经发布中央库可以直接使用maven依赖坐标配置

升级包

ms-mpay 支付插件升级包结构参考

Store插件升级包

升级包需上传的包结构 如net.mingsoft.pay.upgrade.xxx,只需包含升级文件,其余方法不需要上传。

菜单 menu.json

业务包.upgrade,只能包含一个json菜单文件,可以通过代码生成器下载

Tip

菜单json只能导入顶级菜单,如果是子菜单需要编写业务代码导入,参考下方常见问题第一点

数据库 sql

业务包.upgrade,可以包含多个不同数据库 sql 脚本文件 可以存在多个sql文件

  • ddl.sql 执行优先级最高,编写对表结构操作的语句;
  • data.sql 初始化数据,编写对应插件的初始化数据;

Tip

也可以编写多个sql文件,与data.sql执行的效果一致,统一视为初始化数据sql

执行类 Upgrade.java

如果sql文件不能满足业务数据的初始化,可以通过升级类来进行业务数据的处理。

  • 可以包含多个 Upgrade*.java 文件,只需要提供 public 公共方法;
  • 类文件不能使用任何spring的注解;
  • 通过 SpringUtil.getBean 来获取对应业务对象,可以处理复杂的业务逻辑

范例:


package net.mingsoft.mpay.upgrade;

 /**
  * 支付升级业务
  * 这就是普通的类文件,唯一注意点是就是不能使用注解
  */
public class Upgrade {

    private static String menuJson = "菜单json";

    
    public void upgrade() {
        //获取业务对象
        IModelBiz modelBiz = SpringUtil.getBean(IModelBiz.class);
        
    }
}


打包导出插件

分别需要导出插件 源码包升级包

pom.xml 片段参考,注意注释部分,打包好会回分别生成 *-sources.zip*-upgrade.zip

<?xml version="1.0" encoding="UTF-8"?>
...
<build>
    ...
    <plugins>
        <!--增加对应-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <descriptors>
                    <!--打包源码-->
                    <descriptor>assembly-source.xml</descriptor>
                    <!--打包升级包-->
                    <!-- <descriptor>assembly-upgrade.xml</descriptor> -->
                </descriptors>
            </configuration>
        </plugin>
    </plugins>
    ...
</build>
</project>
  • 打包源码脚本 assembly-source.xml
<!-- 源码包配置 -->
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>source</id>
    <includeBaseDirectory>false</includeBaseDirectory>
    <formats>
        <format>zip</format>
    </formats>
    <fileSets>
        <fileSet>
            <directory>src</directory>
            <outputDirectory>${file.separator}/src</outputDirectory>
            <excludes>
                <exclude>**/upgrade/**</exclude>
            </excludes>
        </fileSet>
    </fileSets>
</assembly>


  • 打包升级包 assembly-upgrade.xml
<!-- 升级包配置 -->
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>upgrade</id>
    <includeBaseDirectory>false</includeBaseDirectory>
    <formats>
        <format>zip</format>
    </formats>
    <fileSets>
        <fileSet>
            <directory>target${file.separator}classes</directory>
            <outputDirectory>${file.separator}</outputDirectory>
            <includes>
                <include>**/upgrade/**</include>
            </includes>
            <excludes>
                <exclude>**/upgrade/*.java</exclude>
            </excludes>
        </fileSet>
    </fileSets>
</assembly>

分享

将打包出来的插件分享到MStore

定价参考

条件价格(元)
只是简单基于模板插件(不存在新的业务表)+(10~50)
基于自定义模块+(50~100)
简单业务插件,存在3张表以内+(100~200)
复杂业务插件,大于3张表+(200~5000)

Tip

价格变动会第一时间更新次文档,如果上架模板不符合定价标准,系统将审核失败进行退回处理。

常见问题

二级菜单导入


// 资源引入 

public class Upgrade {

    // 菜单管理处 找到对应菜单 复制菜单json
    private static String menuJson = "...";

    public void upgrade() {
        IModelBiz modelBiz = (IModelBiz) SpringUtil.getBean(IModelBiz.class);
		LambdaQueryWrapper<ModelEntity> lqw = new LambdaQueryWrapper();
		lqw.eq(ModelEntity::getModelTitle, "所属父菜单标题 如 权限管理");
		ModelEntity model = (ModelEntity)modelBiz.getOne(lqw);
		ModelEntity modelEntity = (ModelEntity) JSONUtil.toBean(menuJson, ModelEntity.class);
		ManagerEntity manager = BasicUtil.getManager();
		modelBiz.importModel(modelEntity, manager.getRoleId(), (String)null, Integer.parseInt(model.getId()));
    }
}