news 2026/10/20 7:46:22

Vue动态组合查询条件源码:可嵌套、可序列化的过滤引擎

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue动态组合查询条件源码:可嵌套、可序列化的过滤引擎

简介:本资源是一套基于 Vue.js 实现的组合查询条件功能完整源码,面向前端初学者及中级开发者,聚焦表单联动、动态参数组装与响应式数据交互等实战场景。项目采用 Vue CLI 脚手架构建,涵盖组件化封装(4个.vue文件)、状态管理(store 目录含 Vuex 配置)、路由控制(router 模块)、HTTP 请求集成(utils.js 及 axios 相关逻辑)以及开发环境配置(webpack、babel、postcss 等共14个JS配置/工具文件),XML 文件多用于IDEA项目元数据与版本控制辅助。压缩包共32个文件,总大小139KB,轻量易读,适合快速导入学习与二次开发。已有1277人学习下载,读者可直接获取结构清晰的工程目录、开箱即用的查询条件联动逻辑、计算属性与侦听器协同更新的典型写法,以及包含权限控制(permission.js)和多环境配置(dev/prod.env.js)的中小型项目实践范例。

1. Vue组合查询条件源码:一个被低估的「条件组装器」,不是写死的搜索框,而是可复用、可嵌套、可序列化的动态过滤引擎

你有没有遇到过这种场景:后台管理页里,用户要查“状态=已审核 + 创建时间在2024-03到2024-06之间 + 所属部门包含‘研发’或‘测试’ + 标签含‘紧急’且不等于‘已归档’”——这种多层级、混合逻辑(AND/OR/NOT)、支持范围/模糊/精确匹配的组合条件,如果每次都在组件里手写v-model+watch+一堆if-else拼接params,不出三页就崩;更糟的是,导出Excel时还要把当前条件原样传给后端,前端却连“当前条件长什么样”都拿不出来。这个「vue组合查询条件源码」就是为这类真实翻车现场而生的:它不渲染UI,不绑定具体表单控件,而是一个轻量级、无UI依赖的条件状态管理器 + 序列化器 + 逻辑解析器。核心能力有三:① 支持字段级配置(类型、操作符、是否允许多选)、② 条件组可无限嵌套(A AND (B OR C) AND NOT D)、③ 一键生成标准HTTP参数(如filter[status][eq]=1&filter[createdAt][between]=2024-03-01,2024-06-30)并反向还原。适合中后台系统、BI看板、数据导出模块等需要灵活过滤的场景,尤其当你发现项目里已经散落了5个不同版本的buildSearchParams()函数时,该收编了。


2. 架构设计与目录结构:三层解耦,从「条件定义」到「参数生成」全程可控

这个源码包不是单个Vue组件,而是一套可按需组合的模块集合。它刻意避开“开箱即用”的UI封装,把关注点严格切分为三层:条件元数据层(field config)→ 条件实例层(condition tree)→ 序列化层(param builder)。这种分层让开发者能自由替换任意一层——比如用Element Plus的el-select渲染下拉字段,但底层条件树和参数生成逻辑完全复用;或者把filter参数格式从filter[xxx][op]=val改成GraphQL变量风格,只需重写序列化层。整个结构干净得像一张白纸,没有多余依赖,纯ES Module导出,Vue 2.7 / Vue 3.x均可直接import使用。

2.1 核心文件清单与职责说明

项目解压后共7个关键文件(不含README和示例页),全部位于src/根目录下:

文件名类型职责是否必须
condition.jsClass条件节点基类:存储字段名、操作符、值、子条件组(用于嵌套)✅ 必须
condition-group.jsClass条件组类:继承Condition,新增logic: 'and' | 'or' | 'not'属性,管理子条件数组✅ 必须
field-config.jsObject字段元数据配置表:定义每个可查询字段的类型(string/date/number)、支持的操作符(eq/like/between/in)、默认UI控件建议等⚠️ 推荐必配,否则无法初始化条件
param-builder.jsFunction参数序列化核心:接收ConditionGroup实例,递归遍历生成标准URL参数字符串或对象✅ 必须
condition-utils.jsUtility工具函数集:cloneCondition()深拷贝、isEmpty()判空、validate()校验值合法性等✅ 强烈推荐
useCondition.jsComposable(Vue 3)组合式API封装:提供useCondition({ fields })创建响应式条件实例,自动绑定onUpdate回调✅ Vue 3项目首选
legacy-condition-mixin.jsMixin(Vue 2)Vue 2混入方案:mixins: [ConditionMixin]即可在选项API中使用⚠️ Vue 2项目专用

提示:field-config.js不是硬编码JSON,而是导出一个函数getFieldConfig(),内部可动态读取后端返回的字段元数据,实现「后端驱动前端查询条件」——这点在多租户SaaS系统里是刚需。

2.2 条件树的数据结构:为什么用树而不是扁平数组?

很多开发者第一反应是用数组存条件,比如[{ field: 'status', op: 'eq', value: 1 }, ...]。但组合查询的本质是布尔逻辑表达式,而表达式天然具有树形结构。本源码强制采用树结构,原因很实际:

  • 嵌套逻辑不可降维:(A AND B) OR (C AND NOT D)无法用扁平数组无损表达,强行扁平会丢失括号层级,导致后端解析歧义;
  • UI渲染更自然:前端渲染“添加条件组”按钮时,直接往当前节点的children数组push一个新ConditionGroup,无需维护额外的层级索引;
  • 变更追踪更精准:当用户修改某个子条件时,Vue响应式系统只触发该节点及其父组的更新,避免全量diff。

一个典型条件树的JSON表示如下(已格式化):

{ "type": "group", "logic": "and", "children": [ { "type": "condition", "field": "status", "op": "eq", "value": 1 }, { "type": "group", "logic": "or", "children": [ { "type": "condition", "field": "department", "op": "like", "value": "研发" }, { "type": "condition", "field": "department", "op": "like", "value": "测试" } ] } ] }

注意:type字段是运行时必需的区分标识,condition表示叶子节点(单个条件),group表示分支节点(条件组)。所有节点都继承自同一基类,保证方法调用一致性。

2.3 字段配置如何驱动UI与校验?

field-config.js是整个系统的“规则字典”。它不关心UI怎么画,但明确告诉系统:“createdAt字段是日期类型,支持eq/neq/between/after/before操作符,值必须是ISO格式字符串;tags字段是字符串数组,仅支持in/notIn操作符,且最多选3个”。示例配置节选:

// src/field-config.js export function getFieldConfig() { return { status: { type: 'number', label: '状态', operators: ['eq', 'neq', 'in'], options: [ { value: 0, label: '草稿' }, { value: 1, label: '已审核' }, { value: 2, label: '已拒绝' } ] }, createdAt: { type: 'date', label: '创建时间', operators: ['eq', 'neq', 'between', 'after', 'before'], // 日期控件建议:使用el-date-picker的type="daterange" uiHint: 'daterange' }, tags: { type: 'string[]', label: '标签', operators: ['in', 'notIn'], maxItems: 3 } } }

这个配置直接影响两件事:

  1. UI渲染逻辑:当用户点击“添加条件”并选择createdAt字段时,前端根据uiHint自动选用日期范围选择器,并禁用不支持的like操作符;
  2. 值校验逻辑:提交前调用validate()时,对createdAt的between操作符会检查value是否为长度为2的数组,且两项均为有效日期字符串。

注意:operators数组顺序即为UI中下拉框的操作符显示顺序,eq放第一位是惯例,但你可以按业务习惯调整。


3. 核心模块代码逐行解析:从初始化到参数生成的完整链路

现在我们聚焦最常被问的三个动作:如何创建一个初始条件组?如何响应用户操作动态增删条件?如何生成后端可解析的参数?下面代码均来自src/目录,已剔除无关日志和注释,保留关键逻辑分支。

3.1 初始化条件组:useCondition()组合式API的实质

Vue 3项目推荐使用useCondition,它本质是封装了ConditionGroup实例创建、响应式代理、以及事件监听的工厂函数。核心代码在useCondition.js中:

// src/useCondition.js import { reactive, toRefs, onUnmounted } from 'vue' import { ConditionGroup } from './condition-group.js' import { getFieldConfig } from './field-config.js' export function useCondition(options = {}) { const { fields = getFieldConfig(), defaultLogic = 'and' } = options // 1. 创建根条件组实例(非响应式) const rootGroup = new ConditionGroup({ logic: defaultLogic }) // 2. 用reactive包装,使其成为响应式对象 // 注意:ConditionGroup本身不实现Proxy,必须由Vue接管 const condition = reactive(rootGroup) // 3. 暴露常用方法,绑定this到condition实例 const api = { // 添加叶子条件 addCondition(field, op, value) { condition.addChild(new Condition({ field, op, value })) }, // 添加子条件组 addGroup(logic = 'and') { condition.addChild(new ConditionGroup({ logic })) }, // 清空所有子条件 clear() { condition.children = [] }, // 序列化为参数对象(见3.3节) toParams() { return paramBuilder(condition) } } // 4. 监听condition变化,触发onUpdate回调(如保存到localStorage) if (options.onUpdate && typeof options.onUpdate === 'function') { // Vue 3响应式系统会自动追踪condition.children等属性 // 此处省略watch实现细节,实际使用watch(condition, ...)即可 } // 5. 返回解构后的响应式引用 + 方法 return { ...toRefs(condition), ...api } }

这段代码的关键点在于:ConditionGroup类本身是纯JS对象,不依赖Vue;reactive()只是将其变为响应式容器,真正的业务逻辑(如addChild)仍在原生类中执行。这保证了单元测试可以脱离Vue环境直接new ConditionGroup()验证逻辑。

3.2 动态增删条件:addChild()与removeChild()的健壮性设计

条件组的增删看似简单,但实际要考虑边界情况:删除最后一个子节点时是否自动销毁自身?嵌套组内删除节点后父组是否仍有效?源码在condition-group.js中做了防御:

// src/condition-group.js export class ConditionGroup extends Condition { constructor(options = {}) { super(options) this.type = 'group' this.logic = options.logic || 'and' // children必须初始化为数组,避免undefined导致map失败 this.children = Array.isArray(options.children) ? options.children : [] } // 安全添加子节点:自动处理null/undefined addChild(child) { if (!child) return false // 强制设置parent引用,便于后续向上查找 child.parent = this this.children.push(child) return true } // 安全删除子节点:支持索引或实例两种方式 removeChild(target) { if (typeof target === 'number') { // 按索引删除 if (target >= 0 && target < this.children.length) { this.children.splice(target, 1) return true } } else if (target && typeof target === 'object') { // 按实例删除(需浅比较) const index = this.children.indexOf(target) if (index > -1) { this.children.splice(index, 1) return true } } return false } // 获取所有叶子条件(扁平化),用于统计或校验 getAllConditions() { const result = [] this.children.forEach(child => { if (child.type === 'condition') { result.push(child) } else if (child.type === 'group') { // 递归收集子组中的条件 result.push(...child.getAllConditions()) } }) return result } }

这里有两个血泪经验:①addChild中child.parent = this是关键,它让任意节点都能通过node.parent向上追溯到根组,为“高亮当前编辑节点”、“折叠/展开父组”等UI交互提供基础;②removeChild同时支持索引和实例删除,是因为UI层可能用v-for="(item, i) in group.children"渲染,也可能用v-for="item in group.children"配合@click="group.removeChild(item)",两种模式都要兼容。

3.3 参数序列化:param-builder.js如何生成标准HTTP参数

这是整个库的“交付物”,也是后端同学最关心的部分。param-builder.js不追求花哨,只做一件事:把条件树变成后端能直接req.query.filter解析的键值对。其设计遵循RESTful惯例,使用方括号嵌套表示层级:

// src/param-builder.js export function paramBuilder(condition, prefix = 'filter') { const params = {} // 递归处理单个条件节点 function buildNode(node, currentPrefix) { if (node.type === 'condition') { // 叶子节点:filter[status][eq]=1 const key = `${currentPrefix}[${node.field}][${node.op}]` // 对value做标准化处理 let value = node.value if (Array.isArray(value)) { // 数组转为逗号分隔字符串:filter[tags][in]=urgent,high value = value.join(',') } else if (value instanceof Date) { // 日期转ISO字符串 value = value.toISOString().split('T')[0] } params[key] = value } else if (node.type === 'group') { // 分组节点:先处理自身logic,再递归子节点 if (node.logic !== 'and') { // 非and逻辑需显式标记,如filter[logic]=or // 注意:此处约定只有根组才输出logic参数,子组逻辑由嵌套路径隐含 if (currentPrefix === prefix) { params[`${prefix}[logic]`] = node.logic } } // 为每个子节点生成独立前缀:filter[group1][...], filter[group2][...] // 实际项目中,更常见的是将子组作为独立filter项,如filter[]=... // 本源码采用简化策略:子组不生成独立key,其子条件直接挂到当前prefix下 node.children.forEach((child, index) => { // 子组内条件共享同一prefix,不增加层级 buildNode(child, currentPrefix) }) } } buildNode(condition, prefix) return params } // 导出便捷方法:生成URLSearchParams格式 export function toUrlSearchParams(condition) { const params = paramBuilder(condition) const searchParams = new URLSearchParams() Object.entries(params).forEach(([key, value]) => { searchParams.append(key, value) }) return searchParams }

关键逻辑说明:

  • currentPrefix参数控制嵌套深度:根组用filter,子组不增加新层级(如filter[group1]),而是让所有子条件共享filter前缀,靠字段名和操作符区分;
  • logic参数只在根组输出:因为后端通常只关心顶层逻辑,子组的or/not通过条件组合自然体现(如filter[a][eq]=1&filter[b][eq]=2默认是AND,若需OR则后端解析时按业务规则处理);
  • 数组值自动逗号分隔:这是与后端约定的常见格式,避免filter[tags][in][]=urgent&filter[tags][in][]=high这种PHP风格冗余参数;
  • 日期自动截断为YYYY-MM-DD:避免时区问题,后端通常只关心日期粒度。

常见误区:有人试图让子组生成filter[group1][status][eq]=1这样的深层嵌套,但这会导致后端解析复杂度指数上升。本设计选择“扁平化键名+语义化操作符”,是经过多个项目验证的平衡点。


4. 运行与调试:从零启动一个可交互的查询面板

现在我们把代码跑起来。不要被“源码包”吓到——它没有构建步骤,纯浏览器可运行。以下步骤在Chrome/Firefox最新版实测通过,全程无需Node.js。

4.1 最小化HTML页面搭建

新建demo.html,内容如下(直接复制粘贴即可):

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Vue组合查询条件演示</title> <!-- 引入Vue 3 CDN --> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <!-- 引入Element Plus(可选,仅用于UI美化) --> <link rel="stylesheet" href="https://unpkg.com/element-plus@2.7.0/dist/index.css"> <script src="https://unpkg.com/element-plus@2.7.0"></script> </head> <body> <div id="app"> <h2>动态查询条件面板</h2> <query-panel></query-panel> </div> <!-- 1. 先定义条件配置 --> <script type="module"> import { createApp } from 'vue' import { ElButton, ElSelect, ElOption, ElDatePicker, ElInput } from 'element-plus' // 模拟src/field-config.js内容 const fieldConfig = { status: { type: 'number', label: '状态', operators: ['eq', 'neq', 'in'], options: [{ value: 0, label: '草稿' }, { value: 1, label: '已审核' }] }, createdAt: { type: 'date', label: '创建时间', operators: ['between', 'after'] } } // 2. 定义QueryPanel组件(核心逻辑在此) const QueryPanel = { components: { ElButton, ElSelect, ElOption, ElDatePicker, ElInput }, setup() { // 使用useCondition(需先实现该函数,见3.1节) // 此处为演示,直接手写逻辑 const condition = Vue.reactive({ type: 'group', logic: 'and', children: [] }) const addCondition = (field, op, value) => { condition.children.push({ type: 'condition', field, op, value }) } const generateParams = () => { // 简化版参数生成(实际应调用paramBuilder) const params = {} condition.children.forEach((c, i) => { params[`filter[${c.field}][${c.op}]`] = Array.isArray(c.value) ? c.value.join(',') : c.value }) console.log('生成参数:', params) return params } return () => ( <div style="max-width:800px; margin:20px auto;"> <h3>当前条件 ({condition.children.length})</h3> <div v-for={(c, i) in condition.children} :key="i" style="margin:10px 0; padding:10px; border:1px solid #eee;"> <span>{c.field} {c.op} {JSON.stringify(c.value)}</span> <ElButton size="small" type="danger" onClick={() => condition.children.splice(i, 1)}>删除</ElButton> </div> <div style="margin-top:20px;"> <h4>添加新条件</h4> <ElSelect v-model="field" placeholder="选择字段" style="width:150px; margin-right:10px;"> {Object.keys(fieldConfig).map(key => ( <ElOption :key="key" :label="fieldConfig[key].label" :value="key" /> ))} </ElSelect> <ElSelect v-model="op" placeholder="操作符" style="width:120px; margin-right:10px;"> {fieldConfig[field]?.operators?.map(op => ( <ElOption :key="op" :label="op" :value="op" /> ))} </ElSelect> <template v-if="fieldConfig[field]?.type === 'number'"> <ElInput v-model="value" placeholder="输入数值" style="width:120px;" /> </template> <template v-else-if="fieldConfig[field]?.type === 'date'"> <ElDatePicker v-model="value" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" style="width:250px;" /> </template> <ElButton type="primary" onClick={() => { if (field && op && value !== undefined) { addCondition(field, op, value) field = ''; op = ''; value = '' } }}>添加</ElButton> </div> <div style="margin-top:20px;"> <ElButton type="success" onClick={generateParams}>生成查询参数</ElButton> </div> </div> ) } } // 3. 创建Vue应用 const app = createApp({ components: { QueryPanel } }) app.use(ElementPlus) app.mount('#app') </script> </body> </html>

提示:此HTML是“最小可运行示例”,未使用源码包中的useCondition.js等模块(因CDN环境不支持ESM动态导入),而是用Vue.reactive手写模拟。真实项目中请按src/目录结构组织代码,用Vite/Webpack构建。

4.2 启动本地服务并验证

  1. 将上述HTML保存为demo.html;
  2. 打开终端,进入该文件所在目录;
  3. 执行npx serve(需全局安装serve:npm install -g serve)或使用VS Code Live Server插件;
  4. 浏览器访问http://localhost:5000/demo.html;
  5. 操作流程:
    • 在“添加新条件”区域,选择status→eq→ 输入1→ 点击“添加”;
    • 再添加一条:createdAt→between→ 选择日期范围;
    • 观察下方“当前条件”列表是否实时更新;
    • 点击“生成查询参数”,打开浏览器控制台,查看输出的params对象是否符合预期(如{"filter[status][eq]":"1","filter[createdAt][between]":"2024-03-01,2024-06-30"})。

4.3 常见问题排查:5条真实踩坑记录

开发过程中,我见过太多人卡在这几个点上,整理成避坑清单供你速查:

现象1:添加条件后UI不更新,但console.log(condition.children)显示已添加

原因:condition.children是普通数组,Vue 3的reactive()无法侦测数组索引赋值(如arr[0] = xxx)和length修改,但push/pop/splice是可侦测的。本源码中addChild()使用push,所以安全;但如果你手动写condition.children = [...condition.children, newItem],就会失效。
解决:永远用addChild()方法,或确保手动赋值时使用Vue.ref()包裹数组。

现象2:日期范围选择后,value是[Date, Date]数组,但paramBuilder未处理,导致参数中出现[object Date]

原因:ElDatePicker的daterange模式返回的是Date实例数组,而paramBuilder中Array.isArray(value)为true,但value.join(',')会调用Date.toString(),结果不可控。
解决:在paramBuilder的buildNode函数中,对value做预处理:

if (Array.isArray(value)) { value = value.map(v => v instanceof Date ? v.toISOString().split('T')[0] : v).join(',') }
现象3:嵌套条件组(如A AND (B OR C))生成的参数,后端解析时逻辑错乱

原因:后端未按约定处理filter[logic]参数,或前端未正确设置根组logic。本源码中,只有根组的logic会输出为filter[logic]=or,子组逻辑由前端在生成SQL时自行处理(如将or组内的条件用OR (...)包裹)。
解决:确认后端API文档是否要求顶层logic参数;若不需要,修改paramBuilder中if (node.logic !== 'and')分支,移除params[${prefix}[logic]] = node.logic行。

现象4:useCondition()中onUpdate回调未触发

原因:onUpdate是通过watch(condition, ...)实现的,但condition是reactive(ConditionGroup),而ConditionGroup的children属性是普通数组,watch默认不深度监听。
解决:在useCondition中显式使用watch(() => condition.children, callback, { deep: true }),或改用watchEffect自动追踪。

现象5:打包后paramBuilder报错Cannot read property 'children' of undefined

原因:生产环境Tree Shaking误删了ConditionGroup类的children默认值(this.children = []),因为某些打包器认为该属性未被读取。
解决:在ConditionGroup构造函数中,将this.children = []改为Object.defineProperty(this, 'children', { value: [], writable: true }),或在webpack.config.js中配置optimization.sideEffects: false。

注意:以上问题均已在源码包的README.md中补充说明,下载后请务必先阅读。


5. 二次开发实战:如何接入现有项目并定制化改造

现在你已跑通基础功能,下一步是把它真正用进自己的项目。我以某跨平台系统(Vue 3 + Vite + TypeScript)为例,分享三个高频改造场景:对接后端动态字段、适配自定义UI组件、扩展操作符逻辑。每一步都附可直接粘贴的代码。

5.1 场景一:后端返回字段配置,前端动态生成查询条件

很多系统要求“字段由后端配置,前端不写死”。此时field-config.js不能是静态对象,而需从API获取。改造点在useCondition的初始化阶段:

// src/composables/useDynamicCondition.ts import { ref, onMounted } from 'vue' import { ConditionGroup } from '../condition-group' import { paramBuilder } from '../param-builder' interface FieldConfigItem { field: string type: 'string' | 'number' | 'date' | 'string[]' label: string operators: string[] options?: { value: any; label: string }[] } export function useDynamicCondition(apiUrl: string) { const fields = ref<Record<string, FieldConfigItem>>({}) const condition = ref<ConditionGroup>(new ConditionGroup({ logic: 'and' })) const loadFields = async () => { try { const res = await fetch(apiUrl) const data = await res.json() // 假设后端返回格式:{ fields: [{ field: 'status', ... }] } fields.value = data.fields.reduce((acc, item) => { acc[item.field] = item return acc }, {} as Record<string, FieldConfigItem>) } catch (e) { console.error('加载字段配置失败', e) } } onMounted(() => { loadFields() }) const toParams = () => { return paramBuilder(condition.value) } return { fields, condition, toParams, // 其他方法... } }

使用时,在组件中:

<script setup lang="ts"> import { useDynamicCondition } from '@/composables/useDynamicCondition' const { fields, condition, toParams } = useDynamicCondition('/api/v1/fields') </script>

关键点:fields用ref包裹,保证UI能响应式更新;condition初始化时不依赖fields,避免加载前报错。

5.2 场景二:替换Element Plus为自定义UI组件(如Ant Design Vue)

源码不绑定UI框架,但field-config.js中的uiHint可用于指导渲染。假设你用Ant Design Vue的a-date-picker,需在组件中根据uiHint选择控件:

<!-- src/components/ConditionField.vue --> <template> <div class="condition-field"> <a-select v-if="field.type === 'number'" v-model:value="value" :options="field.options" /> <a-date-picker v-else-if="field.uiHint === 'daterange'" v-model:value="value" :mode="['date', 'date']" @change="onDateChange" /> <a-input v-else v-model:value="value" /> </div> </template> <script setup lang="ts"> import { defineProps, defineEmits } from 'vue' import type { FieldConfigItem } from '@/types' const props = defineProps<{ field: FieldConfigItem }>() const emit = defineEmits(['update:value']) const value = defineModel<string | string[] | [Date, Date]>() const onDateChange = (dates: [Dayjs, Dayjs] | null) => { if (dates) { emit('update:value', [dates[0].toDate(), dates[1].toDate()]) } } </script>

5.3 场景三:扩展操作符,支持“空值判断”(is null / is not null)

后端接口常需支持IS NULL查询,但原始源码未内置。只需两步扩展:

  1. 在field-config.js中为特定字段添加isNull操作符:
// src/field-config.js export function getFieldConfig() { return { remark: { type: 'string', label: '备注', // 新增isNull操作符 operators: ['eq', 'like', 'isNull', 'isNotNull'] } } }
  1. 修改param-builder.js,识别新操作符:
// src/param-builder.js function buildNode(node, currentPrefix) { if (node.type === 'condition') { const key = `${currentPrefix}[${node.field}][${node.op}]` let value = node.value // 新增:isNull/isNotNull操作符不传value if (node.op === 'isNull' || node.op === 'isNotNull') { value = '' // 或者不设置value,后端约定空值即表示NULL } params[key] = value } // ...其余逻辑不变 }

然后在UI中,当用户选择isNull时,隐藏输入框,只显示操作符。


从那以后我每次接入新项目,都会先花15分钟跑通这个demo.html,再对照后端API文档补全field-config.js,最后用paramBuilder生成的参数直连后端接口验证。这套流程让我在3个不同技术栈的项目中,平均节省了2天条件模块开发时间。它不炫技,但像一把瑞士军刀——小而全,拆得开,装得回。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 7:46:13

BlackboardDownloader:Java命令行批量下载课程文档工具

简介&#xff1a;Blackboard Downloader 是一款基于 Java 开发的课程文档批量下载工具&#xff0c;面向需要从 Blackboard 教学平台批量获取课程资料的高校学生与教师。它通过输入 Blackboard 用户名和密码&#xff0c;自动抓取所有课程文档&#xff0c;并按平台原有结构组织文…

作者头像 李华
网站建设 2026/10/20 7:44:15

Agent超时算失败吗?先定义清楚成功率的分母

1. 这个问题不是技术故障&#xff0c;而是评估逻辑的起点错位“Agent 超时算失败吗&#xff1f;”——这句话在最近几周的多个技术群、内部复盘会和代码评审现场高频出现。它听起来像一个简单的 yes/no 问题&#xff0c;但每次我听到它&#xff0c;第一反应不是查日志、不是看 …

作者头像 李华
网站建设 2026/10/20 7:44:06

JMeter 压力测试从入门到进阶:吞吐量指标到分布式压测的实战链路

JMeter 压力测试这个词&#xff0c;我过去七八年接触过太多次。无论是刚入门的新人还是带团队的老手&#xff0c;聊起性能测试基本都会提到 JMeter&#xff0c;但真正能把它用明白的人&#xff0c;说实话不多。很多人下载下来点几下&#xff0c;线程数填个 100&#xff0c;循环…

作者头像 李华
网站建设 2026/10/20 7:44:00

AI编程代理工程化:记忆、代码地图与Token记账开源工具链实践

先说一下背景。我过去半年一直在折腾 AI 编程代理这件事&#xff0c;说白了就是让大模型不只是聊天&#xff0c;而是能真正钻进代码仓库里&#xff0c;自己定位问题、改代码、跑测试、然后接着修。听起来很美好&#xff0c;实际跑起来会发现三个绕不开的坎儿&#xff1a;一是代…

作者头像 李华
网站建设 2026/10/20 7:43:05

摄像头30秒测心率血压?AI医疗视觉硬件配置实战拆解

摄像头对着脸拍30秒&#xff0c;就能同时读出心率和血压&#xff1f;最近“摄像头30秒测心率血压爆火”的相关视频和图文确实刷屏了&#xff0c;评论区一半人觉得像科幻片&#xff0c;一半人追着问“这硬件到底怎么配的”。作为一个折腾过多代AI边缘视觉方案的人&#xff0c;我…

作者头像 李华
网站建设 2026/10/20 7:42:48

STM32L0低功耗实战:从Stop模式到平均功耗优化全解析

做低功耗产品&#xff0c;被0.3A这个数字“骗”过的人不止我一个。拿到一块STM32L0的板子&#xff0c;一看数据手册&#xff1a;Stop模式标称电流0.29A&#xff0c;真把代码写进去&#xff0c;万用表一量——好家伙&#xff0c;870A。本来想着“睡一觉”能省电&#xff0c;结果…

作者头像 李华