任欢
Published on 2026-09-16 / 29 Visits
0
0

前端组件库查询条件queryWrapper构造器用法

前情提要:昨天我试验构造queryWrapper对象传入接口成功后,决定在前端提供一个通用的条件构造工具。

早期开发前端时发现不会构造复杂的查询条件,于是大部分情况都委托后端开发对ts标准查询接口进行了深度的魔改,与最初的ts设计目的相悖了。

本次构造器的出现就是为了解决这个问题,旨在由前端自行构造较为复杂的查询条件,它也可以用于取代早期的searchWhereFromForm,因为后者只构造whereQueryCondition,而queryWrapper构造器可以构造整个payload。

在浏览器侧用链式 API 组装分页查询 JSON,作为 JSAPI page / query 的请求 body。

模块:web-designer-comps-shared(src/utils/query-wrapper/)


1. 用途

默认做条件 + 分页 + 排序。需要按关联表字段过滤主表时,再加 JOIN。

不提供 from / select:主表由原子服务实体决定。JOIN 只过滤主表行,结果集仍是主表记录,不会带出外表字段。

不提供裸 SQL where(...)、单参数 Raw on(...)、子查询 JOIN。


2. 导入

import { QueryWrapper } from "web-designer-comps-shared";

分包:

import { QueryWrapper } from "web-designer-comps-shared/utils/query-wrapper";

类型:

import type { QueryWrapperPayload } from "web-designer-comps-shared";

请求 body 一律使用 toPayload(),不要手写 whereQueryCondition / joins 结构。


3. 单表查询

不调用 innerJoin / leftJoin / rightJoin。payload 仅含已赋值字段:limitOffset、limitRows、whereQueryCondition、orderBys。

3.1 分页列表

import { QueryWrapper } from "web-designer-comps-shared";
import request from "@/api";
import { normalizePageRequest } from "@/common/queryBuilders";

export function pageExample(params: {
    pagination?: { current?: unknown; pageSize?: unknown };
    code?: string;
    name?: string;
    isEnable?: string;
}) {
    const { limitOffset, limitRows } = normalizePageRequest(params.pagination, 20);
    const qw = QueryWrapper.create().limit(limitOffset, limitRows);

    if (params.code?.trim()) {
        qw.andColumn("code").like(params.code.trim());
    }
    if (params.name?.trim()) {
        qw.andColumn("name").like(params.name.trim());
    }
    if (params.isEnable) {
        qw.andColumn("isEnable").eq(params.isEnable);
    }

    return request({
        url: "/mdm/v1/example/page",
        method: "post",
        data: qw.toPayload(),
    });
}

仅分页:

QueryWrapper.create().limit(0, 20).toPayload();

产出:{ "limitOffset": 0, "limitRows": 20 }

limit(offset, rows) 不换算页码。页码需先转为 offset / rows 再传入。

3.2 条件与排序

const qw = QueryWrapper.create();

qw.andColumn("code").like("A01");
qw.andColumn("code").likeLeft("A01");
qw.andColumn("isEnable").eq("1");
qw.andColumn("qty").ge(1);
qw.andColumn("id").in(ids);
qw.andColumn("remark").isNull();
qw.orColumn("status").eq("DRAFT");

qw.orderByDesc("updateDate").orderByAsc("code");
qw.limit(limitOffset, limitRows);

单表列只写字段名:andColumn("code") → { name: "code" }。不要加表前缀。

方法 logic value
eq(v) = 标量转字符串;布尔值为 "1" / "0"
ne(v) != 同上
gt / ge / lt / le > / >= / < / <= 字符串
like(v) LIKE 自动包成 %v%,调用方不要再加 %
likeLeft(v) LIKE v%
likeRight(v) LIKE %v
in(arr) / notIn(arr) IN / NOT IN 数组
isNull() / isNotNull() IS NULL / IS NOT NULL null

andColumn 节点的 connector 为 AND,orColumn 为 OR,多条件以 next 串联。

3.3 单表 payload

QueryWrapper.create()
    .andColumn("code")
    .like("T01")
    .andColumn("isEnable")
    .eq("1")
    .orderByDesc("updateDate")
    .limit(0, 10)
    .toPayload();
{
  "limitOffset": 0,
  "limitRows": 10,
  "whereQueryCondition": {
    "effective": true,
    "connector": "AND",
    "column": { "name": "code" },
    "logic": " LIKE ",
    "value": "%T01%",
    "next": {
      "effective": true,
      "connector": "AND",
      "column": { "name": "isEnable" },
      "logic": " = ",
      "value": "1"
    }
  },
  "orderBys": [
    {
      "queryColumn": {
        "column": { "name": "updateDate" },
        "orderType": "DESC"
      }
    }
  ]
}

4. 链式规则

QueryWrapper.create() 为入口。

andColumn / orColumn / innerJoin / leftJoin / rightJoin 返回中间对象,必须立刻调用 .eq / .like / .on 等,不能把中间对象赋给 QueryWrapper 变量。

可选条件写在已创建的实例上:

const qw = QueryWrapper.create().limit(0, 20);
if (code) {
    qw.andColumn("code").like(code);
}

5. JOIN 查询

筛选字段不在主表时使用。JOIN 的表名为 数据库表名。

QueryWrapper.create()
    .innerJoin("mdm_tooling_model")
    .on("mdm_tooling.tooling_model_id", "mdm_tooling_model.id")
    .andColumn("mdm_tooling_model", "is_barcode_control")
    .eq("Y")
    .andColumn("code")
    .like(code)
    .limit(limitOffset, limitRows)
    .toPayload();
方法 写入的 JOIN 类型(含两侧空格,由构造器生成)
innerJoin(table) " INNER JOIN "
leftJoin(table) " LEFT JOIN "
rightJoin(table) " RIGHT JOIN "
  • as(alias):外表别名。
  • on(leftColumn, rightColumn):两列等值,生成 left = right。
  • 表名、列名、别名:[A-Za-z_][A-Za-z0-9_]*;列引用可以是 table.column。非法标识符会抛错。
  • 每次 on() 同时写入 joins 与 joinTables,二者必须成对。

外表条件必须限定表:

写法 含义
andColumn("code") 主表列
andColumn("mdm_tooling_model.is_barcode_control") 外表列
andColumn("mdm_tooling_model", "is_barcode_control") 外表列(同上)
const qw = QueryWrapper.create()
    .innerJoin("mdm_tooling_model")
    .on("mdm_tooling.tooling_model_id", "mdm_tooling_model.id")
    .andColumn("mdm_tooling_model", "is_barcode_control")
    .eq("Y")
    .limit(limitOffset, limitRows);

if (code) {
    qw.andColumn("code").like(code);
}

return request({
    url: "/mdm/v1/tooling/page",
    method: "post",
    data: qw.toPayload(),
});
{
  "limitOffset": 0,
  "limitRows": 10,
  "whereQueryCondition": {
    "effective": true,
    "connector": "AND",
    "column": {
      "name": "is_barcode_control",
      "table": { "name": "mdm_tooling_model" }
    },
    "logic": " = ",
    "value": "Y"
  },
  "joins": [
    {
      "type": " INNER JOIN ",
      "queryTable": { "name": "mdm_tooling_model" },
      "on": {
        "content": "mdm_tooling.tooling_model_id = mdm_tooling_model.id"
      }
    }
  ],
  "joinTables": [{ "name": "mdm_tooling_model" }]
}

6. 约束与错误用法

错误 说明
like 自行再包 % 会变成 %%值%%
把 andColumn(...) 的返回值当作 QueryWrapper 后续无法继续链式调用
单表使用 andColumn("表名.列名") 单表只写字段名
JOIN 后外表条件不带表名 列会落到主表
on 只传一段 SQL 字符串 必须是两个列名参数
用户输入写入表名或 on 的列名 只允许把用户输入放在比较值里
手写 joins / joinTables / logic 一律由 toPayload() 生成

7. 导出

名称 说明
QueryWrapper 构造器入口
QueryWrapperPayload toPayload() 返回类型
JoinType JOIN 类型常量
JoinBuilder / QueryConditionBuilder 链式中间类型

Comment