概述

EzAdmin DSL 配置生成助手用于生成和维护 EzAdmin DSL 配置文件,包括 list JSON、form JSON、express .ql 脚本和组件配置。

根据用户描述或数据库表结构,生成列表页 list、表单页 form、iframe 子表、组件配置及 express .ql 脚本。

工作流程

  1. 收集模块名、功能名、表名、主键字段、字段列表、枚举/外键/子表/流程信息。
  2. 读取同模块已有 DSL 文件,优先沿用当前项目风格。
  3. 生成或修改以下路径的文件。
  4. 对涉及的组件读取对应子 skill,按子 skill 里的详细配置生成。
  5. 修改完成后运行能覆盖本次改动的最小测试或语法检查。

文件结构

src/main/resources/topezadmin/config/layui/dsl/
├── form/{模块名}/
│   ├── {功能名}.json
│   ├── {功能名}_initExpress.ql
│   ├── {功能名}_submitExpress.ql
│   └── {功能名}_deleteExpress.ql
└── list/{模块名}/
    ├── {功能名}.json
    └── {功能名}_express_main.ql

URL 规则

框架路由

框架路由使用相对路径,不加 / 前缀:

"url": "form/page-product/base-product?ID={{=d.ID}}"
"url": "list/page-product/base-product"
"url": "form/delete-product/base-product?ID={{=d.ID}}"

业务 Controller

业务 Controller 使用绝对路径,保留 /

"url": "/purchase/inbound/label/printPage?inboundItemId={{=d.ID}}"
"url": "/mycamunda/check/history/Process_product?id=${ID}"

新增表单日期默认值

新增表单需要把日期字段默认成当天时,优先在打开表单的新增 URL 上传框架内置默认参数:

"url": "form/page-finance/finance-arap-flow?FINANCE_BILL_ID=${ID}&TRADER_TIME=EZ_TODAY_DATE_NORMAL"

EZ_TODAY_DATE_NORMAL 会被框架替换为 yyyy-MM-dd 格式当天日期。只给新增入口加;编辑入口带 ID 时不要加,避免覆盖数据库已有日期。

List DSL 要点

基本结构

{
  "id": "base-product",
  "name": "产品管理",
  "dataSource": "dataSource",
  "body": {
    "emptyShow": "-",
    "showIndex": true,
    "selectable": false,
    "rowActionWidth": 175
  },
  "tabList": [],
  "search": [{"row": []}],
  "column": [],
  "tableButton": [],
  "rowButton": [],
  "express": {
    "main": "@import(base-product_express_main.ql)",
    "orderBy": "ORDER BY t.PROD_ID DESC",
    "groupBy": ""
  }
}

搜索 operator 常用值

SQL 场景
like LIKE '%?%' 文本模糊搜索
EQ = 精确匹配、枚举
= = 精确匹配的另一种写法
BETWEEN BETWEEN ? AND ? 日期/数值区间,配合 props.range: true
IN IN (...) 多选
GTE / LTE >= / <= 数值范围

日期搜索默认规则

List 搜索项使用 component: "date" 时,默认生成 operator: "BETWEEN"props.range: true

{"item_name": "ADD_TIME", "label": "添加时间", "component": "date",
  "operator": "BETWEEN", "props": {"range": true}}
{"item_name": "ADD_DATE", "label": "添加日期", "component": "date", "operator": "EQ"}

多字段切换搜索(unioninput)

{"item_name": "PROD_NAME_PROD_MODEL_PROD_SPEC", "label": "编码", "component": "unioninput",
  "operator": "like", "props": {"placeholder": "请输入名称、型号、规格"},
  "options": [
    {"label": "产品名称", "value": "PROD_NAME", "operator": "like"},
    {"label": "产品型号", "value": "PROD_MODEL", "operator": "like"},
    {"label": "产品规格", "value": "PROD_SPEC", "operator": "like"}
  ]
},
{"item_name": "PROD_NAME", "label": "名称", "component": "hidden", "operator": "like"},
{"item_name": "PROD_MODEL", "label": "型号", "component": "hidden", "operator": "like"},
{"item_name": "PROD_SPEC", "label": "规格", "component": "hidden", "operator": "like"}

日期范围切换(uniondate)

{"item_name": "UNIONDATE", "label": "搜索", "component": "uniondate", "operator": "BETWEEN",
  "props": {"range": true},
  "options": [
    {"label": "添加时间", "value": "ADD_TIME"},
    {"label": "发货时间", "value": "DELIVERY_TIME"}
  ]
},
{"item_name": "ADD_TIME", "component": "hidden", "jdbcType": "DATETIME"},
{"item_name": "DELIVERY_TIME", "component": "hidden", "jdbcType": "DATETIME"}

Express 主查询

列表 express 主查询必须给主键别名 ID,主表建议别名 t

StringBuilder sql = new StringBuilder();
companyId = $$("COMPANY_ID");
sql.append("
    SELECT
        t.PROD_ID ID,
        t.PROD_CODE,
        t.PROD_NAME
    FROM T_BASE_PRODUCT t
    WHERE t.DELETE_FLAG = 0
      AND t.COMPANY_ID = " + companyId);
return search(sql);

search(sql) 自动拼接 search 栏的过滤条件。

Form DSL 要点

基本结构

{
  "id": "base-product",
  "name": "产品表单",
  "dataSource": "dataSource",
  "successUrl": "reload",
  "definitionKey": "",
  "cardList": [],
  "buttonList": [],
  "initExpress": "@import(base-product_initExpress.ql)",
  "submitExpress": "@import(base-product_submitExpress.ql)",
  "deleteExpress": "@import(base-product_deleteExpress.ql)",
  "statusExpress": [],
  "appendHead": "",
  "appendFoot": ""
}

successUrl 取值

说明
reload 刷新父页面(默认)
reloadlocal 刷新当前表单
reloadTableData 仅刷新父表格数据
refreshcard.{item_name} 刷新父表单指定 iframe card
parent.refreshcard.{item_name} 子表单在 iframe 内弹出时,刷新父窗口里指定的 iframe card

普通字段 card 示例

{
  "type": "card",
  "label": "基本信息",
  "fieldList": [
    {"row": [
      {"item_name": "PROD_NAME", "label": "名称", "component": "input",
        "classAppend": "layui-col-md8", "props": {"lay-verify": "required"}},
      {"item_name": "STATUS", "label": "状态", "component": "select",
        "classAppend": "layui-col-md4",
        "initData": {"dataJson": [{"value": 1, "label": "启用"}, {"value": 0, "label": "禁用"}]}}
    ]}
  ],
  "buttonList": []
}
栅格宽度:使用 Layui 12 栅格:layui-col-md12layui-col-md8layui-col-md6layui-col-md4layui-col-md3layui-col-md0(纯隐藏不占位)。同一 row 内字段宽度之和应 ≤ 12。

Express 速查

常用函数

函数 说明
$("KEY") 取请求参数
$$("KEY") 取 Session 参数
isBlank("KEY") / isNotBlank("KEY") 判断请求参数空/非空
search(sql) 列表查询,自动拼接 search 条件
select(sql) / selectOne(sql) 查询多行/单行
count(sql) 查询计数,常用于唯一性/存在性校验
insert(sql) / update(sql) 执行写入
insertSimple(InsertParam) / updateSimple(UpdateParam) 构建式新增/更新
spring("beanName").method(args) 调用 Spring Bean

Session 常用 key

  • COMPANY_ID
  • EZ_SESSION_USER_ID_KEY
  • EZ_SESSION_USER_NAME_KEY

生命周期

表单 appendFoot 在页面中按以下顺序执行:

  1. initGlobal
  2. 组件渲染
  3. 异步赋值
  4. initFunction(data)
  5. 用户交互
  6. submitHandler()(如有)
  7. submitSuccess()
注意:依赖已回填字段值的逻辑必须放在 initFunction(data),不要在顶层脚本或 layui.use 刚进入时读取异步回填值做业务初始化,此时数据可能还没回填。

initFunction 示例

function initFunction(data) {
    if ($("#ID").val()) {
        $("[name=FIELD]").attr("disabled", "disabled");
        layui.form.render();
    }
}

submitSuccess 示例

var submitSuccess = function(data) {
    if (data && data.code == 0) {
        layer.alert("保存成功", function(index) {
            layer.close(index);
            window.location.reload();
        });
        return;
    }
    layer.alert((data && data.message) || "保存失败");
}

校验规则

简单必填

使用 props.lay-verify: "required",或新式写法 props.required: true + props.validate.rule.required: true

复杂/条件校验

条件校验、跨字段校验、格式校验统一用 jQuery Validate 的 $.validator.addMethod 注册自定义规则名,DSL 侧 props.validate.rule.{规则名}: true 引用。

{
  "item_name": "PRODUCT_CATEGORY_ID",
  "component": "input",
  "props": {
    "validate": {
      "rule": {"categoryPairRequired": true},
      "message": {"categoryPairRequired": "新国标分类与旧国标分类不能同时为空"}
    }
  }
}

对应 appendFoot/initFunction 里注册规则:

$.validator.addMethod("categoryPairRequired", function(value, element){
    var a = $.trim($("[name='PRODUCT_CATEGORY_ID']").val() || "");
    var b = $.trim($("[name='OLD_CATEGORY_ID']").val() || "");
    return a !== "" || b !== "";
}, "新国标分类与旧国标分类不能同时为空");
常见陷阱:不要用 $('[name=FIELD]').rules('add', ...) 动态增删校验规则——validator 实例未初始化时会报错,优先在 DSL props.validate 里静态声明。

表单组件

组件细节不放在主 skill。需要使用某个组件时,必须读取对应子 skill。

input 组件

单行文本输入,最常用的表单字段组件。

基本配置

{
  "item_name": "PROD_NAME",
  "label": "产品名称",
  "component": "input",
  "classAppend": "layui-col-md8",
  "props": {
    "lay-verify": "required",
    "maxlength": 100,
    "placeholder": "请输入产品名称"
  }
}

props

配置 说明
lay-verify 简单必填校验
maxlength 最大输入长度
placeholder 占位提示文字
validate jQuery Validate 规则

lay-verify 取值范围

项目实际只出现过 "required" 单个取值。文档理论上支持 number/phone/email 及组合写法,但当前项目里没有真实用例,遇到需要格式校验的场景优先走 jQuery Validate 方式。

复杂/条件校验

{
  "item_name": "PRODUCT_CATEGORY_ID",
  "component": "input",
  "props": {
    "validate": {
      "rule": {"categoryPairRequired": true},
      "message": {"categoryPairRequired": "新国标分类与旧国标分类不能同时为空"}
    }
  }
}

textarea 组件

多行文本输入。

基本配置

{
  "item_name": "COMMENT",
  "label": "备注",
  "component": "textarea",
  "classAppend": "layui-col-md12",
  "props": {
    "rows": 4,
    "maxlength": 500
  }
}

props

配置 说明
rows 显示行数
maxlength 最大输入长度
placeholder 占位提示文字
建议:备注类字段一律用 layui-col-md12 占满整行。校验规则写法与 input 组件一致。

hidden 组件

隐藏域,不在页面上展示,但会随表单一起提交。常用于携带主键 ID、父级外键。

基本配置

{
  "item_name": "ID",
  "label": "ID",
  "component": "hidden",
  "classAppend": "layui-col-md0"
}

classAppend 取值

  • layui-col-md0 — 完全不占位,纯粹作为隐藏域存在(常用于 ID 字段)
  • layui-col-md12 — 占位一整行但不显示内容(历史写法,新写法优先用 md0
注意事项:hidden 组件不支持 initData、不需要 props。值的来源:新增时通常由 URL 参数或 initExpress 回填,编辑时由后端查询结果回填。

span 组件

只读展示,不可编辑。三种典型用途:

1. 占位提示(值尚未生成)

{
  "item_name": "PURCHASE_ORDER_NO",
  "label": "订单编号",
  "component": "span",
  "classAppend": "layui-col-md6",
  "props": {"placeholder": "自动生成"}
}

2. 说明文字

{
  "item_name": "PROD_REG_NAME",
  "label": "产品注册名称",
  "component": "span",
  "classAppend": "layui-col-md12",
  "props": {"description": "产品注册证上的名称,关联注册证后自动生成"}
}

3. 只读展示字典值

{
  "item_name": "ORDER_TYPE",
  "label": "订单类型",
  "component": "span",
  "classAppend": "layui-col-md6",
  "initData": {
    "dataJson": [
      {"label": "订单采购", "value": "1"},
      {"label": "批量采购", "value": "2"}
    ]
  }
}

props / jdbcType

配置 说明
props.placeholder 值为空时展示的占位文字
props.description 字段下方的说明文字
jdbcType: "DATETIME" 配合后端返回的日期值自动格式化展示
initData.dataJson 有字典值时按 value/label 展示对应文字

select 组件

下拉单选。initData 支持三种数据来源,按需选择。

1. dataJson 内联枚举

{
  "item_name": "STATUS",
  "label": "状态",
  "component": "select",
  "classAppend": "layui-col-md4",
  "initData": {
    "dataJson": [
      {"value": 1, "label": "启用"},
      {"value": 0, "label": "禁用"}
    ]
  }
}

2. dataJson 引用外部文件

{
  "item_name": "CHECK_STATUS",
  "component": "select",
  "initData": {
    "dataJson": "@import(checkstatus.json)"
  }
}

@import(checkstatus.json) 会去同目录下找 checkstatus.json,内容就是一个 [{"value":...,"label":...}] 数组。

3. dataSql 动态查询

{
  "item_name": "BRAND_ID",
  "label": "品牌",
  "component": "select",
  "classAppend": "layui-col-md12",
  "initData": {
    "dataSql": "select brand_id value,brand_name label from T_BASE_BRAND WHERE COMPANY_ID=${COMPANY_ID} and DELETE_FLAG=0",
    "dataSource": "datasource"
  }
}

SQL 结果集必须包含 valuelabel 两列(列名固定,大小写不敏感)。

常见陷阱:dataSql 里过滤条件别忘了 DELETE_FLAG=0COMPANY_ID=${COMPANY_ID}(多租户隔离)。需要多选时不要用 select + multiple,用专门的 select-multiple 组件。

select-span 组件

只读展示枚举值对应的文字,不可编辑。和 selectinitData 语法完全一致,区别只是渲染为纯文本而不是下拉框。

1. dataJson 静态枚举

{
  "item_name": "EXPIRY_STATUS",
  "label": "到期状态",
  "component": "select-span",
  "classAppend": "layui-col-md6",
  "initData": {
    "dataJson": [
      {"value": "VALID", "label": "有效"},
      {"value": "EXPIRING", "label": "即将到期"},
      {"value": "EXPIRED", "label": "已过期"},
      {"value": "UNKNOWN", "label": "未维护有效期"}
    ]
  }
}

2. dataSql 动态查询

{
  "item_name": "TRADER_ID",
  "label": "客户名称",
  "component": "select-span",
  "classAppend": "layui-col-md12",
  "initData": {
    "dataSql": "SELECT TRADER_ID value,TRADER_NAME label FROM T_BASE_TRADER WHERE TRADER_ID=${TRADER_ID} AND COMPANY_ID=${COMPANY_ID}",
    "dataSource": "dataSource"
  }
}
何时用 select-span 而不是 span:值本身就是要按 value/label 映射展示(枚举、外键名称)时用 select-span。纯文本、说明、自动生成提示用 span

select-multiple 组件(别名 xmselect)

多选下拉。项目里 select-multiplexmselect 是同一个组件的两种命名,写法完全一致。

1. 远程搜索多选

{
  "item_name": "PROD_ID",
  "label": "产品名称",
  "component": "select-multiple",
  "classAppend": "layui-col-md12",
  "props": {
    "remoteSearch": true,
    "toolbar": {
      "show": true,
      "list": ["ALL", "REVERSE", "CLEAR"]
    }
  },
  "initData": {
    "dataUrl": "/jxc/product/search.html"
  }
}

2. 限制为单选

{
  "item_name": "MANUFACTURER_ID",
  "label": "生产厂家",
  "component": "xmselect",
  "classAppend": "layui-col-md12",
  "props": {
    "max": 1,
    "remoteSearch": true,
    "required": true,
    "validate": {
      "rule": {"required": true},
      "message": {"required": "请选择生产厂家"}
    }
  },
  "initData": {
    "dataUrl": "/jxc/manufacturer/search.html"
  }
}

props

配置 说明
remoteSearch 是否启用远程搜索
max 最多可选数量,1 即"单选"
toolbar.show + toolbar.list 是否显示全选/反选/清空工具条
常见陷阱:数据量大的关联字段不要用 select + dataSql 一次性查全表,改用远程搜索。max:1 时提交的值仍然是数组/逗号分隔的形式。

pop-list-select 组件

用于表单字段需要打开列表页选择一行或多行,并把选中行 ID 保存到隐藏字段的场景。

基本配置

{
  "item_name": "MANUFACTURER_ID",
  "label": "生产厂家",
  "component": "pop-list-select",
  "classAppend": "layui-col-md12",
  "props": {
    "url": "list/page-med/manufacturer-select",
    "idField": "ID",
    "multiple": true,
    "displayFields": "MANUFACTURER_NAME,MANUFACTURER_NAME_EN",
    "title": "请选择生产厂家",
    "width": "900px",
    "height": "620px",
    "description": "可选择多个生产厂家"
  }
}

props

配置 必填 说明
url / pageUrl 弹框打开的列表页 URL
initUrl / dataUrl 初始化回显数据接口
idField 唯一字段,默认 ID
multiple 是否多选,默认 true
displayFields 回显表格展示字段,英文逗号分隔
title 弹框标题
width / height 弹框尺寸
description 按钮下方辅助说明

实例与外部 API

// 初始化页面上所有 pop-list-select
window.EZ_POP_LIST_SELECT.init();

// 只初始化某一个 item_name
window.EZ_POP_LIST_SELECT.init('MANUFACTURER_ID');

// 根据当前 hidden input 的 value 调初始化接口并渲染回显 table
window.EZ_POP_LIST_SELECT.initFromInputValue('MANUFACTURER_ID');

// 外部直接设置某个组件值,并触发初始化回显
window.EZ_POP_LIST_SELECT.setValue('MANUFACTURER_ID', '7994');

// 清空某个组件
window.EZ_POP_LIST_SELECT.clear('MANUFACTURER_ID');
注意:不要使用改名前的旧全局对象名 window.EZ_INPUT_POP_SELECT(该名字已废弃,2026-07-09 起统一改为 pop-list-select/EZ_POP_LIST_SELECT)。

cascader 组件

级联选择,用于分类树、地区树等有父子层级的数据。

基本配置

{
  "item_name": "CATEGORY_ID",
  "label": "分类",
  "component": "cascader",
  "classAppend": "layui-col-md12",
  "jdbcType": "NUMBER",
  "initData": {
    "dataSql": "select PARENT_ID parent_id,category_id value,category_id ID,category_name label from T_BASE_CATEGORY WHERE COMPANY_ID=${COMPANY_ID} and DELETE_FLAG=0",
    "dataSource": "datasource"
  }
}

dataSql 列名要求

列名 说明
value 节点值,保存到表单字段
parent_id 父节点值,0/NULL/根值表示顶层节点
label 节点展示文字

checkStrictly 配置

{
  "item_name": "REGION_ID",
  "label": "地区",
  "component": "cascader",
  "classAppend": "layui-col-md12",
  "initData": {
    "dataSql": "SELECT A.REGION_FULL_ID value, A.PARENT_ID parent_id, A.REGION_FULL_NAME label FROM T_BASE_REGION A where delete_flag=0 AND PARENT_ID>=0",
    "dataSource": "datasource"
  },
  "props": {
    "props": {
      "checkStrictly": false
    }
  }
}

注意这里是 props.props.checkStrictly(嵌套两层)。checkStrictly: false(默认)要求必须选到叶子节点;设为 true 允许选中任意层级的节点。

常见陷阱:parent_id 字段名必须小写,value/label 同理。外键字段是数字类型时建议加 "jdbcType": "NUMBER"

radio 组件

单选框。

基本配置

{
  "item_name": "STATUS",
  "label": "状态",
  "component": "radio",
  "classAppend": "layui-col-md12",
  "initValue": 1,
  "initData": {
    "dataJson": [
      {"value": 1, "label": "启用"},
      {"value": 0, "label": "禁用"}
    ]
  }
}

默认值两种写法

  1. 字段级 initValue(如上例)——新建表单时该字段的默认选中值。
  2. props.defaultValue(字符串形式):
{
  "item_name": "TYPE",
  "component": "radio",
  "props": {"defaultValue": "1"},
  "initData": {"dataJson": [{"value": "1", "label": "普通"}, {"value": "2", "label": "特殊"}]}
}

新文件优先用 initValue,跟同模块已有文件保持一致即可。

initData:只支持 dataJson 静态枚举,不支持 dataSql/dataUrl

checkbox 组件

复选框,支持多选。当前项目里没有找到真实用例,以下写法基于 radio 的语法类推。

基本配置(类推写法)

{
  "item_name": "TAGS",
  "label": "标签",
  "component": "checkbox",
  "classAppend": "layui-col-md12",
  "initData": {
    "dataJson": [
      {"value": "A", "label": "标签A"},
      {"value": "B", "label": "标签B"}
    ]
  }
}

数据格式

提交值预期为逗号分隔字符串(如 "A,B"),与多选的 pop-list-selectselect-multiple 提交格式一致。

建议:如果只是"是/否"类的单个布尔开关,优先用 radio 而不是单个 checkbox。真正需要多选的场景,先看是否更适合用 select-multiple

date 组件(表单字段)

表单里的日期选择字段。

基本配置

{
  "item_name": "BC_ISSUE_DATE",
  "label": "营业执照发证日期",
  "component": "date",
  "classAppend": "layui-col-md4",
  "jdbcType": "DATE",
  "props": {
    "format": "yyyy-MM-dd"
  }
}

带必填校验

{
  "item_name": "ISSUING_DATE",
  "label": "批准日期",
  "component": "date",
  "classAppend": "layui-col-md4",
  "jdbcType": "DATE",
  "props": {
    "format": "yyyy-MM-dd",
    "required": true,
    "validate": {
      "rule": {"required": true},
      "message": {"required": "请选择批准日期"}
    }
  }
}

props

配置 说明
format 日期格式,项目里统一用 yyyy-MM-dd
required + validate.rule.required 新式必填写法

表单默认当天

新增表单里的日期字段如果需要默认当天,优先在打开表单的新增 URL 里传框架内置参数:

"url": "form/page-finance/finance-arap-flow?FINANCE_BILL_ID=${ID}&TRADER_TIME=EZ_TODAY_DATE_NORMAL"

只给新增入口加默认参数;编辑入口带 ID 时不要加。

List 搜索与 Form 字段

List 搜索项使用 component: "date" 时,默认生成日期范围。Form 日期字段保持单日期,不要自动添加 range: true

upload 组件

文件/图片上传。

图片上传

{
  "item_name": "PROD_PIC_IDS",
  "label": "图片",
  "component": "upload",
  "classAppend": "layui-col-md12",
  "props": {
    "accept": "images",
    "multiple": true,
    "size": 2048,
    "number": 5,
    "description": "支持jpg、png格式,最多上传5张图片,每张大小不超过2MB"
  }
}

文件上传(带数量上限校验)

{
  "item_name": "FILE_OSS_ID",
  "label": "资质附件",
  "component": "upload",
  "classAppend": "layui-col-md12",
  "props": {
    "description": "允许 JPG、JPEG、PNG、BMP、PDF,最多上传1个文件",
    "multiple": false,
    "required": false,
    "number": 1,
    "accept": "file",
    "validate": {
      "rule": {"uploadMax": 1},
      "message": {"uploadMax": "最多上传1个文件"}
    }
  }
}

props 全量字段

配置 取值 说明
accept "images" / "file" 限制文件类型大类
multiple true / false 是否允许多文件上传
number 整数 最多上传文件数
size 整数(KB) 单文件大小上限
description 字符串 按钮下方的说明文字
validate.rule.uploadMax 整数 自定义校验:限制最多上传N个文件
常见陷阱:number 只是前端组件层面的数量提示/限制,真正阻止超量提交要配 validate.rule.uploadMax,两者建议同时配置且取值一致。

tinymce 组件

富文本编辑器,用于正文类长文本(如文章内容),保存 HTML 字符串。

基本配置

{
  "item_name": "BLOG_CONTENT",
  "label": "内容",
  "component": "tinymce",
  "classAppend": "layui-col-md12"
}
注意事项:
  • 一律用 layui-col-md12 占满整行,富文本编辑器需要足够宽度。
  • 保存到数据库的是完整 HTML 字符串,submitExpress 里作为普通文本字段处理(#{FIELD})。
  • 展示时如果要在列表里预览,注意富文本内容长度可能很长,list 的 column 一般不直接展示这个字段。

iframe 子表 Card

用于表单中内嵌子列表或子页面。父表单主键为空时,必须使用 iframe.params 做结构化参数和空值拦截。

推荐配置

{
  "type": "iframe",
  "item_name": "brandManufacturerList",
  "label": "关联生产厂家",
  "iframe": {
    "url": "list/page-product/base-brand-manufacturer-list",
    "height": "326px",
    "params": {
      "BRAND_ID": "${ID}"
    },
    "emptyText": "请先保存品牌,再维护关联生产厂家"
  },
  "buttonList": []
}

iframe 配置项

配置 必填 说明
url iframe 基础 URL
params 结构化查询参数,值支持 ${ID} 等占位符
height iframe 高度
width iframe 宽度
emptyText 任意 params 解析为空时展示的提示

行为规则

  • 渲染前先解析 iframe.params 中的占位符。
  • 任意参数解析后为空字符串、null 或缺失时,不渲染 iframe src
  • 不渲染 src 时展示 emptyText,避免新增页 ${ID} 为空导致子列表 HTTP 400。
  • item_name 用于定向刷新,例如 successUrl: "refreshcard.brandManufacturerList"

props.display 可以叠加使用

{
  "type": "iframe",
  "item_name": "suppliercert",
  "label": "资质台账",
  "iframe": {
    "url": "list/page-supplier/supplier-cert-list",
    "height": "326px",
    "params": {"SUPPLIER_ID": "${ID}"},
    "emptyText": "请先保存供应商,再维护资质台账"
  },
  "buttonList": [],
  "props": {
    "display": "{{ if(!'${ID}'){ }} none {{ } else { }} {{ } }}"
  }
}

列表组件

列表侧渲染/操作用的是另一套 component,不要跟表单字段组件混用。

列渲染组件(column)

list JSON 的 column 数组里,每个字段用 component 指定渲染方式。

tdText(默认文本)

{"item_name": "PROD_CODE", "label": "编码", "component": "tdText"}

带日期格式化:

{"item_name": "ADD_TIME", "label": "录入时间", "jdbcType": "DATETIME", "component": "tdText",
  "props": {"format": "yyyy-MM-dd HH:mm:ss", "width": 175}}

jdbcType 取值:DATEDATETIMENUMBER(数字千分位/右对齐)。

tdLink(可点击链接)

{"item_name": "PROD_CODE", "label": "编码", "component": "tdLink",
  "props": {"url": "form/page-product/base-product?ID={{= d.ID}}", "opentype": "FORM",
            "windowname": "编辑", "width": 110}}

tdSelect(枚举值显示)

// dataJson 内联
{"item_name": "QUALITY_STATUS", "label": "质量状态", "component": "tdSelect",
  "initData": {"dataJson": [{"value":"PASS","label":"通过"},{"value":"WARN","label":"预警"}]}}

// dataJson 引用外部文件
{"item_name": "CHECK_STATUS", "label": "审核状态", "component": "tdSelect",
  "initData": {"dataJson": "@import(checkstatus.json)"}}

// dataSql 动态查询
{"item_name": "ADD_ID", "label": "添加人", "component": "tdSelect",
  "initData": {"dataSql": "SELECT USER_ID value, USER_NAME label FROM T_SYS_USER WHERE COMPANY_ID=${COMPANY_ID}",
               "dataSource": "dataSource"}, "props": {"width": 100}}

tdSelectMultiple(多选值显示)

用法同 tdSelect,值为逗号分隔的多个 value,按 initData 映射逐个展示成多个标签。

tdCascader(级联路径显示)

{"item_name": "CATEGORY_ID", "label": "分类", "component": "tdCascader",
  "initData": {
    "dataSql": "select PARENT_ID parent_id,category_id value,category_id id,category_name label from T_BASE_CATEGORY WHERE COMPANY_ID=${COMPANY_ID} and DELETE_FLAG=0",
    "dataSource": "datasource"}}

tdPic(图片)

{"item_name": "LOGO", "label": "公司logo", "component": "tdPic"}

固定尺寸:

{"item_name": "PROD_PIC_IDS", "label": "图片", "component": "tdPic",
  "props": {"width": 85, "minWidth": 85}}
项目自定义列组件:出库模块里出现过 prodInfonumberInfo 等自定义 component,这些是项目自行扩展的前端渲染器,不是框架内置组件。新模块生成 DSL 时不要照抄这些名字。

列表按钮组件(tableButton / rowButton)

component 取值

component 用于 说明
button-table tableButton 表格上方工具栏普通按钮,如"新增"
button-toolbar tableButton 工具栏按钮,常用于子列表场景
button-normal rowButton 行操作普通按钮
button-single rowButton 行操作单按钮(常配 classAppend 着色)
button-bread rowButton 行操作面包屑型按钮
button-span rowButton 仅展示文本,不触发任何操作
button-dropdown tableButton/rowButton 下拉按钮,收纳多个次要操作

opentype 全量对照表

说明
FORM 全屏表单(复杂表单用)
MODAL 模态框(简单表单用)
MODEL MODAL 的旧拼写,效果等价
CONFIRM_AJAX 确认对话框后发 Ajax,不刷新列表
CONFIRM_AJAX_LIST 确认对话框后发 Ajax,成功后自动刷新当前列表
AJAX_LIST 不弹确认框,直接发 Ajax,成功后刷新当前列表
AJAX 直接 Ajax 请求,不确认、不强制刷新
PARENT 在父窗口打开
_BLANK 新标签页打开
script 触发 appendFoot 中同名 JS 函数
custom 纯前端自定义行为,不发请求
parent.MODAL iframe 内的子列表专用:在父窗口弹出模态框
parent.MODEL parent.MODAL 的旧拼写
parent.CONFIRM_AJAX iframe 内的子列表触发确认后,向父窗口发 Ajax
parent.CONFIRM_AJAX_LIST iframe 内的子列表触发确认后,向父窗口发 Ajax 并刷新列表
常见陷阱:iframe 内的按钮永远不要用裸的 MODAL,必须用 parent.MODAL 系列。新文件遇到旧写法 MODEL/parent.MODEL 不用主动改成 MODAL/parent.MODAL

父子关系(父表单 iframe 嵌套子列表)

适用场景

父记录与子记录是一对多关系,子记录需要独立新增/编辑/删除(不随父表单一次性提交)。典型例子:供应商-资质台账、客户资质-经营范围、注册证-关联厂家、订单-商品。

总体文件结构

form/{模块}/
  {父功能}.json                    ← 父表单,含 iframe card
  {子功能}.json                    ← 子表单(新增/编辑子记录)
  {子功能}_initExpress.ql
  {子功能}_submitExpress.ql
  {子功能}_deleteExpress.ql

list/{模块}/
  {子功能}-list.json               ← 子列表(嵌在 iframe 里)
  {子功能}-list_express_main.ql

1. 父表单 — iframe card

{
  "type": "iframe",
  "item_name": "suppliercert",
  "label": "资质台账",
  "iframe": {
    "url": "list/page-supplier/supplier-cert-list",
    "height": "326px",
    "params": {"SUPPLIER_ID": "${ID}"},
    "emptyText": "请先保存供应商,再维护资质台账"
  },
  "buttonList": [],
  "props": {
    "display": "{{ if(!'${ID}'){ }} none {{ } else { }} {{ } }}"
  }
}

2. 子列表 DSL

{
  "id": "registration-manufacturer-list",
  "name": "关联厂家列表",
  "dataSource": "dataSource",
  "hideSearch": true,
  "body": { "emptyShow": "-", "showIndex": true, "selectable": false, "rowActionWidth": 175 },
  "tabList": [], "search": [],
  "column": [
    {"item_name": "MANUFACTURER_NAME", "label": "厂家名称", "component": "tdText"},
    {"item_name": "PRODUCTION_TYPE", "label": "生产类型", "component": "tdSelect",
      "initData": {"dataJson": [{"value": 0, "label": "自行生产"}, {"value": 1, "label": "委托生产"}]}}
  ],
  "tableButton": [
    {"item_name": "新增", "label": "新增", "component": "button-table",
      "props": {
        "url": "form/page-med/registration-manufacturer?REGISTRATION_ID=${REGISTRATION_ID}",
        "opentype": "parent.MODAL",
        "windowname": "新增关联厂家"
      }}
  ],
  "rowButton": [
    {"item_name": "编辑", "label": "编辑", "component": "button-single",
      "classAppend": "layui-border-blue",
      "props": {
        "url": "form/page-med/registration-manufacturer?ID={{= d.ID}}®ISTRATION_ID={{= d.REGISTRATION_ID}}",
        "opentype": "parent.MODAL",
        "windowname": "编辑关联厂家"
      }},
    {"item_name": "删除", "label": "删除", "component": "button-single",
      "classAppend": "layui-border-red",
      "props": {
        "url": "form/delete-med/registration-manufacturer?ID={{= d.ID}}",
        "opentype": "script",
        "windowname": "删除"
      }}
  ],
  "express": {
    "main": "@import(registration-manufacturer-list_express_main.ql)",
    "orderBy": [], "groupBy": []
  },
  "appendHead": "", "appendFoot": ""
}

3. 子列表 express

companyId = $$("COMPANY_ID");
StringBuilder sql = new StringBuilder();
sql.append("SELECT R.ID, R.REGISTRATION_ID, R.MANUFACTURER_ID, R.PRODUCTION_TYPE, M.MANUFACTURER_NAME ");
sql.append("FROM T_MED_REGISTRATION_MANUFACTURER R ");
sql.append("LEFT JOIN T_MED_MANUFACTURER M ON R.MANUFACTURER_ID = M.MANUFACTURER_ID ");
sql.append("WHERE R.DELETE_FLAG=0 AND R.REGISTRATION_ID=${REGISTRATION_ID} ");
sql.append("AND R.COMPANY_ID=").append(companyId);
return search(sql);

4. 子表单 form DSL

{
  "id": "registration-manufacturer",
  "name": "关联厂家",
  "dataSource": "dataSource",
  "successUrl": "parent.refreshcard.registrationManufacturerList",
  "cardList": [{
    "type": "card", "label": "关联厂家信息",
    "fieldList": [
      {"row": [
        {"item_name": "ID", "label": "ID", "component": "hidden", "classAppend": "layui-col-md0"},
        {"item_name": "REGISTRATION_ID", "label": "注册证ID", "component": "hidden", "classAppend": "layui-col-md0"}
      ]},
      {"row": [
        {"item_name": "MANUFACTURER_ID", "label": "厂家", "component": "xmselect",
          "classAppend": "layui-col-md12",
          "props": {"max": 1, "remoteSearch": true, "required": true,
            "validate": {"rule": {"required": true}, "message": {"required": "请选择厂家"}}},
          "initData": {"dataUrl": "/jxc/manufacturer/search.html"}}
      ]},
      {"row": [
        {"item_name": "PRODUCTION_TYPE", "label": "生产类型", "component": "select",
          "classAppend": "layui-col-md12",
          "initData": {"dataJson": [{"value": "0", "label": "自行生产"}, {"value": "1", "label": "委托生产"}]},
          "props": {"lay-verify": "required"}}
      ]}
    ],
    "buttonList": []
  }],
  "buttonList": [],
  "initExpress": "@import(registration-manufacturer_initExpress.ql)",
  "submitExpress": "@import(registration-manufacturer_submitExpress.ql)",
  "deleteExpress": "@import(registration-manufacturer_deleteExpress.ql)",
  "statusExpress": [], "appendHead": "", "appendFoot": ""
}

5. 子表单 initExpress

companyId = $$("COMPANY_ID");

if (!isNotBlank("ID")) {
    // 新增:从 URL 参数取父 ID 回填 hidden 域
    resp = new HashMap();
    resp.put("REGISTRATION_ID", $("REGISTRATION_ID"));
    resp.put("PRODUCTION_TYPE", "0");   // 默认值
    return resp;
}

// 编辑:按子表主键查询
resp = selectOne("SELECT R.ID, R.REGISTRATION_ID, R.MANUFACTURER_ID, R.PRODUCTION_TYPE "
    + "FROM T_MED_REGISTRATION_MANUFACTURER R "
    + "WHERE R.DELETE_FLAG=0 AND R.ID=#{ID} AND R.COMPANY_ID=" + companyId);
return resp;