跳到主要内容

第一版

提示
  • Nop 平台还处于开发阶段, 本文档中的实践方案可能会部分失效,但本人精力有限,无法及时跟进,请自行按照最新代码调整;
  • 您可以与智谱清言 AI 进行问答互动以了解 Nop 平台相关内容;
  • 若此文对您有很大帮助,请投币支持一下吧;
版权声明

这一版将实现对 DepartmentRegionJobTitleEmployee 基本信息的增删改, 以及对关联关系的绑定。

下载 nop-demo.orm.v1.xlsx 并覆盖工程目录下的 model/nop-demo.orm.xlsx 文件,再通过 nop-cli gen 生成模型和前端页面:

# 注意,请根据当前运行环境修改 JDK 17+ 的安装路径
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk

${JAVA_HOME}/bin/java \
-Dfile.encoding=UTF8 \
-jar ./nop-cli.jar \
gen -t=/nop/templates/orm \
./model/nop-demo.orm.xlsx

模型定义

完整的 Excel 数据模型结构见 Nop 平台的 /nop/orm/imp/orm.imp.xml, 并阅读文档《Excel 数据模型》

定义 Excel 数据模型需注意以下几点:

  • 字段列表】中的【编号】必须是唯一的,不能重复,其将被用于识别字段的 Delta 变更。 其大小顺序即为各字段在前端列表中的默认显示顺序,取值范围为 1 ~ 1999。 按字段添加的先后顺序进行递增(注意,不是按相对位置递增),待删除的字段,仅需在【标签】中添加 del 标记,其余部分保持不变。注:【关联条件】下的【序号】列的数字不要求连续和唯一性,其仅用于标识行的有效性(其他表单中的【序号】列都是如此)
  • 所有的实体模型都必须定义一个【主键】(值为 PK)以及一个在前端的显示名称(【标签】包含 disp 的字段), 若不指定 disp 字段,则默认以主键列的值作为显示内容
  • 标签】包含 seqVARCHAR 类型的字段将默认使用 UUID 值,其字符串长度为 32
  • 标签】包含 parent 的字段代表该对象的父级属性,在前端维护对象的父子关系时将能够自动识别并正确赋值, 比如,在列表【操作】列点击【更多】下拉菜单中的【添加下级】时,将会在编辑框中自动设置新数据的父对象为当前行数据

  • 关联字段 PARENT_ID 在前端将实际显示为被关联对象的 disp 字段值, 因此,需将该字段视为被关联对象,在 【中文名】或【英文名】中不能再包含 ID 之类的字符(如,上级部门ID 需直接命名为 上级部门
  • 字段名】将自动与对象的属性进行映射,并按照驼峰命名规则,将字段名转换为属性名, 如,PARENT_ID 映射属性名为 parentId
  • 作为主键的【字段名】一般不命名为 ID,建议采用 OIDDEP_ID 形式, 因为,id 是 Nop 平台的保留名字,字段名 ID 映射的属性将被自动重命名为 id_,从而可能会产生预期之外的错误。若是必须使用 ID 作为主键名,则需要在【配置】表单中添加变量 allowIdAsColName 并设置为 true
  • 关联列表】中的【属性名】为必填项,其内容为当前对象与被关联对象绑定时的属性名称(命名规范自定), 【关联条件】中的【左属性】与【右属性】分别为当前对象和被关联对象在数据库层面做 join 的字段所映射的对象属性名称,例如,连接条件为 JOIN c.PARENT_ID = p.OID, 则左右属性分别为 parentIdoid
  • 表单 org_staff_employee 将采用自定义属性 fullName 做显示属性, 故而,未在【字段列表】中标记 disp 字段,同时将 FIRST_NAMELAST_NAME 的【显示】设置为 L,即,不在列表中显示

自定义修改

先按照定制化准备的说明,准备 Delta 专用模块。

向 Delta 层中新增的定制文件不会被自动识别,需要重新构建并重启服务。

修改应用名称

前端框架编译后的代码存放在 Nop 平台的 nop-web-site 工程中, 该工程的源代码在 nop-chaos 项目中。

未发现可定制化调整的位置,暂时不支持修改。

合并员工姓名

nop-demo-delta/src/main/resources/_vfs/_delta/v1/nop/demo/model/Employee/Employee.xmeta
<?xml version="1.0" encoding="UTF-8" ?>
<!-- x:extends="super" 表示继承自上一层 Delta -->
<meta xmlns:x="/nop/schema/xdsl.xdef"
x:schema="/nop/schema/xmeta.xdef"
x:extends="super">

<!-- 前端显示属性修改为 fullName -->
<displayProp>fullName</displayProp>

<props>
<!-- 新增计算属性 fullName -->
<prop name="fullName" displayName="姓名">
<getter>
entity.lastName + entity.firstName
</getter>
</prop>
</props>
</meta>

修改了 *.xmeta 需要重新构建 nop-demo-web 项目, 可通过启动 io.nop.demo.web.NopDemoWebCodeGen#main 编译 XView 视图。

计算属性 fullName 没有配置 <schema/> 子节点, 也即,其不是映射到数据库字段的属性,故而,默认不会将其添加到前端的显示列表中, 需要自行在 XView 视图中通过 Delta 机制添加:

nop-demo-delta/src/main/resources/_vfs/_delta/v1/nop/demo/pages/Employee/Employee.view.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- x:extends="super" 表示继承自上一层 Delta -->
<view xmlns:x="/nop/schema/xdsl.xdef"
x:schema="/nop/schema/xui/xview.xdef"
x:extends="super">

<grids>
<grid id="list">
<!-- 在最开始位置增加 姓名 列 -->
<cols x:override="prepend">
<col id="fullName"/>
</cols>
</grid>
</grids>

<forms>
<form id="view">
<!-- 查看表单只显示 fullName -->
<layout>
fullName[姓名]
regionId[地区]
departmentId[部门]
jobTitleId[职位]
createdBy[创建人] updatedBy[更新人]
createdAt[创建时间] updatedAt[更新时间]
</layout>
</form>
<form id="edit">
<!-- 编辑表单在一行内显示姓与名 -->
<layout>
lastName[姓] firstName[名]
regionId[地区]
departmentId[部门]
jobTitleId[职位]
</layout>
</form>
</forms>
</view>

这里通过在 cols 节点设置 x:override="prepend" 以在列表最开始位置插入其子节点。

若是需在列表中的指定位置插入新节点,则需采用x:override保序算法。 例如,将 fullName 插入到 regionId 之前:

  <grids>
<grid id="list">
<cols>
<col id="fullName"/>
<!-- 在自动生成的列 regionId 之前插入 fullName 列 -->
<col id="regionId"/>
</cols>
</grid>
</grids>

需要注意的是,在 nop-demo-web/src/main/resources/_vfs/nop/demo/pages/Employee/Employee.view.xml 中只引入了组件但未设置其他属性:

  <grids>
<grid id="list"/>
<grid id="pick-list"/>
</grids>

<forms>
<form id="view"/>
<form id="edit"/>
</forms>

<pages>
<crud name="main"/>
<picker name="picker"/>
</pages>

这不是为了占位用的,而是在其继承的 nop-demo-web/src/main/resources/_vfs/nop/demo/pages/Employee/_gen/_Employee.view.xml 中,这些组件都被标记为 x:abstract="true",也就是模板组件,在合并 DSL 时会被移除, 所以,在实现层必须显示引入,否则,页面将为空。

在前端列表显示数据版本

数据域】为 version 的字段为后端维护数据,默认在前端不显示,在 Excel 中无法开启显示。 若需要让其显示在列表中,则需要在 *.xmeta 中将其 internal 设置为 false

nop-demo-delta/src/main/resources/_vfs/_delta/v1/nop/demo/model/Employee/Employee.xmeta
  <props>
<!-- ... -->
<prop name="version" internal="false"/>
</props>

注意,需重新构建 XView 视图。

修改一级菜单名称

nop-demo-delta/src/main/resources/_vfs/_delta/v1/nop/demo/auth/nop-demo.action-auth.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- x:extends="super" 表示继承自上一层 Delta -->
<auth xmlns:x="/nop/schema/xdsl.xdef"
x:schema="/nop/schema/action-auth.xdef"
x:extends="super">

<site id="main">
<resource id="test-orm-nop-demo" displayName="组织管理">
<children/>
</resource>
</site>
</auth>

调整二级菜单项顺序

nop-demo-delta/src/main/resources/_vfs/_delta/v1/nop/demo/auth/nop-demo.action-auth.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- x:extends="super" 表示继承自上一层 Delta -->
<auth xmlns:x="/nop/schema/xdsl.xdef"
x:schema="/nop/schema/action-auth.xdef"
x:extends="super">

<site id="main">
<resource id="test-orm-nop-demo" displayName="组织管理">
<children>
<!-- Note:前端以 orderNo 为排序依据 -->
<resource id="Employee-main" orderNo="10000"/>
<resource id="Region-main" orderNo="10010"/>
<resource id="Department-main" orderNo="10020"/>
<resource id="JobTitle-main" orderNo="10030"/>
</children>
</resource>
</site>
</auth>

修改一级菜单资源的 id

nop-demo-delta/src/main/resources/_vfs/_delta/v1/nop/demo/auth/nop-demo.action-auth.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- x:extends="super" 表示继承自上一层 Delta -->
<auth xmlns:x="/nop/schema/xdsl.xdef"
x:schema="/nop/schema/action-auth.xdef"
x:extends="super">

<site id="main">
<resource id="test-orm-nop-demo" x:abstract="true"/>
<resource id="org-management"
x:prototype="test-orm-nop-demo"
displayName="组织管理" routePath="/org-management">
<!-- 其他合并内容 -->
</resource>
</site>
</auth>

也就是,将待修改的菜单资源标记为 x:abstract,让其可以从合并结果中被删除, 再让新节点 org-management 通过 x:prototype 继承该节点, 从而实现对 test-orm-nop-demo 资源的 id 的修改。

拆分二级菜单到新的一级菜单中

nop-demo-delta/src/main/resources/_vfs/_delta/v1/nop/demo/auth/nop-demo.action-auth.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- x:extends="super" 表示继承自上一层 Delta -->
<auth xmlns:x="/nop/schema/xdsl.xdef"
x:schema="/nop/schema/action-auth.xdef"
x:extends="super">

<site id="main">
<resource id="test-orm-nop-demo" x:abstract="true"/>

<resource id="org-management" x:prototype="test-orm-nop-demo"
displayName="组织管理" routePath="/org-management">
<children>
<resource id="Employee-main" x:override="remove"/>
<!-- 其他合并内容 -->
</children>
</resource>

<resource id="staff-management" x:prototype="test-orm-nop-demo"
displayName="人员管理" routePath="/staff-management">
<children x:prototype-override="bounded-merge">
<resource id="Employee-main"/>
</children>
</resource>
</site>
</auth>

首先,将待拆分的一级菜单 test-orm-nop-demo 标记为 x:abstract="true"

然后,新增两个继承(x:prototype)自 test-orm-nop-demo 的一级菜单:org-managementstaff-management

接着,从一级菜单 org-management 中移除(x:override="remove")待拆分的二级菜单 Employee-main

最后,在一级菜单 staff-management 中通过 x:prototype-override="bounded-merge" 合并 test-orm-nop-demochildren 节点,以仅保留待拆分出来的二级菜单:

      <children x:prototype-override="bounded-merge">
<resource id="Employee-main"/>
</children>

注意事项

  • 若修改了 *.xmeta,则需要重新编译 XView 视图, 可通过启动 io.nop.demo.web.NopDemoWebCodeGen#main 实现
  • 在 Nop 平台的模板 nop-codegen/src/main/resources/_vfs/nop/templates/orm-web/src/main/resources/_vfs/{moduleId}/pages/{metaInfo.forEntity}{metaInfo.baseObjName}/_gen/_{metaInfo.objName}.view.xml.xgen 中会过滤掉 prop.schema?.simpleSchemafalse 或者 prop.internaltrue 的属性
  • Nop 平台内置的数据域在 nop-codegen/src/main/resources/_vfs/nop/codegen/xlib/meta-gen.xlib 中识别

参考资料