一二三应用开发平台自定义查询设计与实现系列2——查询方案功能实现

查询方案功能实现

上面实现了自定义查询功能框架,从用户角度出发,有些条件组合可以形成特定的查询方案,对应着业务查询场景。诸多查询条件的组合,不能每次都让用户来设置,而是应该保存下来,下次可以直接使用或者在其基础上按需调整,也就是将查询方案持久化。

实体配置

使用平台的低代码配置功能来实现查询方案的实体配置。

考虑到自定义查询属于通用性功能,因此放到了平台架构体系中位于底层的系统管理模块下。

实体配置如下:

实体模型配置如下:

查询方案内置于自定义查询功能内部的,因此无需配置相关视图。

使用平台功能生成库表和代码、拷贝代码、编译、配置权限项。

功能整合

首先上整体效果图,有个直观的了解,如下图:

在原有基础上,顶部增加了查询方案的下拉列表,用户可以通过下拉选择快速加载先前保存的查询方案,并可以通过修改按钮来修改名称、通过删除按钮来删除方案。

查询方案的主体,仍是由筛选器组件来承担,集成整合过程中增加了交互。

底部通过保存按钮来保存查询方案;通过另存为按钮来拷贝新增方案,点击查询按钮后,关闭对话框,将查询方案对应查询条件组合,传递给父窗口(自定义查询功能页面),发起查询操作。

查询方案下拉列表实现

在查询方案目录下,新增一个select.vue的页面,源码如下:

<template><el-selectv-model="selectedValue":size="size"clearable:disabled="readonly"style="width: 200px; margin: 20 auto"@change="change"><el-optionv-for="item in dictionaryItemList":key="item.id":value="item.id":label="item.name"/></el-select></template><script>
export default {name: 'QueryPlanSelect',label: '查询方案下拉',props: {modelValue: {type: String,required: false,default: ''},code: {type: String,default: ''},readonly: {type: Boolean,required: false,default: false},size: {type: String,default: ''}},data() {return {dictionaryItemList: [],selectedValue: ''}},watch: {modelValue: {immediate: true,handler: 'setSelected'}},mounted() {this.loadData()},methods: {change(value) {let rowData = nullthis.dictionaryItemList.forEach((item) => {if (item.id === value) {rowData = itemreturn}})// 更新绑定值this.$emit('update:modelValue', value)// 注意,此处若命令为change,则可能会与底层实现冲突,导致执行两次this.$emit('my-change', value, rowData)},setSelected() {this.selectedValue = this.modelValue},loadData() {this.dictionaryItemList = []this.$api.system.queryPlan.list().then((res) => {this.dictionaryItemList = res.dataif (this.dictionaryItemList.length == 1) {this.selectedValue = this.dictionaryItemList[0].idthis.$emit('my-change', this.dictionaryItemList[0].id, this.dictionaryItemList[0])} else {this.selectedValue = ''this.$emit('my-change', '', null)}})}}
}
</script>

就是把查询方案实体的列表数据,转换为下拉列表的数据格式,并对传入值以及下拉选择变更后,将数据通过事件触发返回给父页面,也就是自定义查询页面。

查询方案下拉列表选中项变更

自定义查询页面会监听查询方案下拉列表的选择的变更,可能有两种情况,一是变更选择项,二是清空选择,对应的事件处理如下:

    // 查询方案变更queryPlanChanged(value, data) {// 保存为当前查询方案this.currentQueryPlan = dataif (data) {//加载条件this.$refs.everrightFilter.setData(JSON.parse(data.content))} else {this.$refs.everrightFilter.clearData()}}

注意当查询方案内容不为空时,调用筛选器的setData方法,来填充条件组合,便于用户查看或在其基础上调整。

查询方案改名

点击下拉列表右侧的“修改”按钮,弹出输入框,这里实际是用于改名,系统会弹出对话框,默认填充当前选中的查询方案名称,用户修改后点击确定按钮后保存,如下图:

功能实现使用了element plus的MessageBox的Prompt模式,代码如下:

    // 修改方案名称modify() {if (this.currentQueryPlan == null) {this.$message.warning('请先选择要改名的查询方案')return}//获取查询方案内容,并做非空判断const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')if (this.checkContentIsNull(content)) {return}// 输入新的方案名称ElMessageBox.prompt('请输入新的方案名称', '修改查询方案', {confirmButtonText: '确定',cancelButtonText: '取消',inputValue: this.currentQueryPlan.name,inputPattern: /.+/,inputErrorMessage: '不能为空'}).then(({ value }) => {this.currentQueryPlan.name = valuethis.currentQueryPlan.content = contentthis.$api.system.queryPlan.modify(this.currentQueryPlan).then((res) => {//刷新查询方案列表this.refreshQuerPlanList()})})},

查询方案删除

点击下拉列表右侧的“删除”按钮,弹出确认框,确定后删除,如下:

源码实现如下:

 // 删除remove() {if (this.currentQueryPlan == null) {this.$message.warning('请先选择要删除的查询方案')return}this.$confirm('确定要删除该查询方案吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.$api.system.queryPlan.remove(this.currentQueryPlan.id).then((res) => {// 刷新查询方案列表this.refreshQuerPlanList()})})}

查询方案新增/修改

在查询方案下拉列表无选中项的情况下,通过筛选器配置查询条件组合后,点击保存按钮,弹出对话框,输入方案名称后,新增查询方案,如下:

在查询方案下拉列表有选中项的情况下,点击保存按钮,直接调用后端服务,保存数据,实际更新的是条件组合。

代码如下:

     // 保存查询方案save() {//获取查询方案内容,并做非空判断const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')if (this.checkContentIsNull(content)) {return}if (this.currentQueryPlan) {// 如当前查询方案不会空,更新数据this.currentQueryPlan.content = contentthis.$api.system.queryPlan.modify(this.currentQueryPlan)} else {// 如当前查询方案为空,新增数据ElMessageBox.prompt('请输入方案名称', '新增查询方案', {confirmButtonText: '确定',cancelButtonText: '取消',inputPattern: /.+/,inputErrorMessage: '不能为空'}).then(({ value }) => {const data = {name: value,entityModelCode: this.entityModelCode,content: content}this.$api.system.queryPlan.add(data).then((res) => {//刷新查询方案列表this.refreshQuerPlanList()})})}}

查询方案另存为

用户往往需要基于某个现有的查询方案调整,另存为另一个查询方案,通过功能按钮实现。

这里没有根据当前是否有选中的查询方案来控制另存为按钮的可用,而是在逻辑上进行了处理,如当前查询方案为空,新增数据,否则参照当前数据新增(即另存),实现如下:

//另存为saveAs() {//获取查询方案内容,并做非空判断const content = JSON.stringify(this.$refs.everrightFilter.getData(), null, '\t')if (this.checkContentIsNull(content)) {return}if (this.currentQueryPlan == null) {// 如当前查询方案为空,新增数据ElMessageBox.prompt('请输入方案名称', '新增查询方案', {confirmButtonText: '确定',cancelButtonText: '取消',inputPattern: /.+/,inputErrorMessage: '不能为空'}).then(({ value }) => {const data = {name: value,entityModelCode: this.entityModelCode,content: content}this.$api.system.queryPlan.add(data).then((res) => {//刷新查询方案列表this.refreshQuerPlanList()})})} else {// 如当前查询方案不为空,参照当前数据新增ElMessageBox.prompt('请输入方案名称', '查询方案另存为', {confirmButtonText: '确定',cancelButtonText: '取消',inputPattern: /.+/,inputErrorMessage: '不能为空'}).then(({ value }) => {const data = {name: value,entityModelCode: this.entityModelCode,content: content}this.$api.system.queryPlan.add(data).then((res) => {//刷新查询方案列表this.refreshQuerPlanList()})})}}

辅助方法

有几个公共辅助方法,在这里也简单列一下:

保存数据前需要判断是否已添加了条件,即进行数据验证,如下:

     // 判断方案内容是否为空checkContentIsNull(content) {if (content == '{}') {this.$message.warning('请先添加至少一个查询条件')return true}return false}

在对方案新增、修改、删除时,需要刷新下拉列表,如下:

    // 刷新查询方案列表refreshQuerPlanList() {this.$refs.queryPlanSelect.loadData()}

后端处理

先前搭建框架的时候,仅实现了单组单条件,接下来,就来完善该部分逻辑以及其他细节。

单组多条件

配置查询方案条件如下:

后端逻辑完善如下:

        // 转换数据DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);// 获取组集合List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();// 遍历组集合for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {// 获取条件集合List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();// 组内条件逻辑关系String logicalOperator = dataFilterGroup.getLogicalOperator();if (CollectionUtils.isNotEmpty(conditionList)) {// 遍历条件集合for (DataFilterConditionVO condition : conditionList) {if (logicalOperator.equals(OR)) {queryWrapper = queryWrapper.or();}// 获取字段名,命名风格驼峰转换成下划线String fieldName = condition.getProperty();Object value = condition.getValue();// 获取操作String operator = condition.getOperator();QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);addEasyQuery(queryWrapper, fieldName, queryRule, value);}}}

运行,查看生成SQL,如下:

SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ?)

将组内逻辑关系调整为或,如下:

运行,查看生成SQL,如下:

SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? OR content LIKE ?)

多组多条件

配置查询方案条件如下:

后端逻辑增加组间逻辑关系处理,调整后如下:

           // 转换数据DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);// 获取组集合List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();// 组间条件逻辑关系String logicalOperatorGroup = dataFilterRule.getLogicalOperator();// 遍历组集合for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {if (logicalOperatorGroup.equals(OR)) {queryWrapper = queryWrapper.or();}// 获取条件集合List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();// 组内条件逻辑关系String logicalOperator = dataFilterGroup.getLogicalOperator();if (CollectionUtils.isNotEmpty(conditionList)) {// 遍历条件集合for (DataFilterConditionVO condition : conditionList) {if (logicalOperator.equals(OR)) {queryWrapper = queryWrapper.or();}// 获取字段名,命名风格驼峰转换成下划线String fieldName = condition.getProperty();Object value = condition.getValue();// 获取操作String operator = condition.getOperator();QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);addEasyQuery(queryWrapper, fieldName, queryRule, value);}}}

运行,查看生成SQL,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ? AND name LIKE ? AND name LIKE ?)
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)

看上去,SQL从最终查询结果而言是正确的,但是没有分组,如果组内关系是or,推测应该会出问题,我们继续验证下。

将组间逻辑关系调整为或,如下:

运行,查看生成SQL,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND (name = ? AND content LIKE ? OR name LIKE ? AND name LIKE ?)
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)

可以看出来,果然出了问题,我们希望各条件组相对独立,用小括号包裹,然后再参与组间逻辑运算。

基于上述需求,对逻辑转换代码进行调整,如下:

private static <E, VO> void build(QueryWrapper<E> queryWrapper, Class<E> entityClass, String customQueryString, SortInfo sortInfo) {// 转换数据DataFilterRuleVO dataFilterRule = JSON.parseObject(customQueryString, DataFilterRuleVO.class);// 获取组集合List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();// 组间条件逻辑关系String logicalOperatorGroup = dataFilterRule.getLogicalOperator();// 遍历组集合for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {if (logicalOperatorGroup.equals(OR)) {queryWrapper.or(x ->generateQueryWrapper(x, dataFilterGroup));} else {queryWrapper.and(x ->generateQueryWrapper(x, dataFilterGroup));}}// 附加排序if (sortInfo != null && StringUtils.isNotBlank(sortInfo.getField())) {// 此处未使用注解,而是按照约定的规则,将驼峰命名转换为下划线,从而获取到数据库表字段名String orderField = CommonUtil.camelToUnderline(sortInfo.getField());if (sortInfo.getAscType()) {queryWrapper.orderByAsc(orderField);} else {queryWrapper.orderByDesc(orderField);}}}private static <E> QueryWrapper<E> generateQueryWrapper(QueryWrapper<E> queryWrapper, DataFilterGroupVO dataFilterGroup) {// 获取条件集合List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();// 组内条件逻辑关系String logicalOperator = dataFilterGroup.getLogicalOperator();if (CollectionUtils.isNotEmpty(conditionList)) {// 遍历条件集合for (DataFilterConditionVO condition : conditionList) {if (logicalOperator.equals(OR)) {queryWrapper = queryWrapper.or();}// 获取字段名,命名风格驼峰转换成下划线String fieldName = condition.getProperty();Object value = condition.getValue();// 获取操作String operator = condition.getOperator();QueryRuleEnum queryRule = EnumUtils.getEnum(QueryRuleEnum.class, operator, QueryRuleEnum.EQ);addEasyQuery(queryWrapper, fieldName, queryRule, value);}}return queryWrapper;}

运行,查看生成SQL,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND ((name = ? AND content LIKE ?) OR (name LIKE ? AND name LIKE ?))
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)

可以看到,条件组外层使用了小括号包裹,组间的逻辑运算也是正确的。

日期类型的处理

前面提到过,在做筛选器组件前端功能验证时,发现日期类型的属性,生成查询规则与其他类型不同,如下:

value属性不是一个值,而是对应了一个对象,并且也不是日期字符串,而是时间戳,这种情况下,需要后端另行解析处理。

设置测试条件如下:

增加逻辑处理如下:

运行,查看生成SQL,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM cfg_template WHERE delete_flag = 'NO' AND ((name = ? AND content LIKE ?) OR (name LIKE ? AND name LIKE ?))
==> Parameters: 1(String), %2%(String), 3%(String), %4(String)

开源平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:[csdn专栏]
开源地址:[Gitee]
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/458984.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文解决单调栈的应用

单调栈的定义&#xff1a; 单调栈是栈的一中特殊形式&#xff0c;在栈中的元素必须满足单调性&#xff08;一定是单调上升或单调下降等等的规律&#xff09;。 单调栈的性质&#xff1a; 单调栈解决的问题 单调栈解决的常见问题&#xff1a;给定一个序列&#xff0c;求每个位置…

css绘制s型(grid)

在之前有通过flex布局实现了s型布局&#xff0c;是通过截取数组形式循环加载数据 这次使用grid直接加载数据通过css实现 <div id"app"><template v-for"(item,inx) in items"><div class"row"><template v-for"(ite…

SpringBoot 集成RabbitMQ 实现钉钉日报定时发送功能

文章目录 一、RabbitMq 下载安装二、开发步骤&#xff1a;1.MAVEN 配置2. RabbitMqConfig 配置3. RabbitMqUtil 工具类4. DailyDelaySendConsumer 消费者监听5. 测试延迟发送 一、RabbitMq 下载安装 官网&#xff1a;https://www.rabbitmq.com/docs 二、开发步骤&#xff1a;…

微信小程序美团点餐

引言&#xff1a;外卖已经成为了都市人的必备&#xff0c;在无数个来不及&#xff08;懒得&#xff09;做饭的时刻拯救孤单寂寞的胃。美团外卖无疑是外卖届的领头羊&#xff0c;它的很多功能与设计都值得我们学习。本文将从五个方面&#xff0c;对美团外卖展开产品分析&#xf…

vue封装信号强度

图标下载链接: https://pan.baidu.com/s/1828AidkCKU1KTkw1SvBwQg?pwd4k7n 共五格信号 信号5为绿色&#xff0c;信号4为绿色&#xff0c;信号3为黄色&#xff0c;信号2为黄色&#xff0c;信号1为红色&#xff0c;信号0为灰色。 子组件 /components/SignalStrength/index.vu…

【Python爬虫实战】深入解析 Selenium:从元素定位到节点交互的完整自动化指南

#1024程序员节&#xff5c;征文# &#x1f308;个人主页&#xff1a;易辰君-CSDN博客 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html ​ 前言 Selenium 是进行网页自动化操作的强大工具&#xff0c;在测试、数据抓取、用户行…

Sqoop的安装配置及使用

Sqoop安装前需要检查之前是否安装了Tez,否则会产生版本或依赖冲突&#xff0c;我们需要移除tez-site.xml&#xff0c;并将hadoop中的mapred-site.xml配置文件中的mapreduce驱动改回成yarn&#xff0c;然后分发到其他节点&#xff0c;hive里面配置的tez也要移除&#xff0c;然后…

实战应用WPS WebOffice开放平台服务

概述 根据公司的业务需要&#xff0c;主要功能是在线编辑文档&#xff0c;前端的小伙伴进行的技术调研&#xff0c;接入的是WPS WebOffice&#xff0c;这里只阐述技术介入的步骤、流程和遇到的坑进行的一些总结。 实践 WPS WebOffice 开放平台进行认证 在开始之前&#xff…

大数据-193 Apache Tez - DAG 作业计算框架 核心解释 工作原理 配置集成

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Anki插件Export deck to html的改造

在Anki中进行复习时&#xff0c;每次只能打开一条笔记。如果积累了很多笔记&#xff0c;有时候会有将它们集中输出成一个pdf进行阅读的想法。Anki插件Export deck to html&#xff08;安装ID&#xff1a;1897277426&#xff09;就有这个功能。但是&#xff0c;这个插件目前存在…

岛津分子泵软件TMP系列分子泵EI-D系列控制电源 EI Monitor(232和485控制)

岛津分子泵软件TMP系列分子泵EI-D系列控制电源 EI Monitor(232和485控制)

探索Unity:从游戏引擎到元宇宙体验,聚焦内容创作

unity是实时3D互动内容创作和运营平台&#xff0c;包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者&#xff0c;借助Unity将创意变成现实。提供一整套完善的软件解决方案&#xff0c;可用于创作、运营和变现任何实时互动的2D和3D内容&#xff0c;支持平台包括手机、…

图为大模型一体机新探索,赋能智能家居行业

在21世纪的今天&#xff0c;科技的飞速进步正以前所未有的速度重塑着我们的生活方式。从智能手机到物联网&#xff0c;从大数据到人工智能&#xff0c;每一项技术创新都在为人类带来前所未有的便利与效率。其中&#xff0c;图为AI大模型一体机作为人工智能领域的最新成果&#…

DiskGenius一键修复磁盘损坏

下午外接磁盘和U盘都出现扇区损坏&#xff0c;估计就是在开着电脑&#xff0c;可能是电脑运行的软件还在对磁盘进行读写&#xff0c;不小心按到笔记本关机键&#xff0c;重新开机读写磁盘分区变得异常卡顿&#xff0c;估摸就是这个原因导致扇区损坏。在进行读写时&#xff0c;整…

深度学习:YOLO v1网络架构、损失值及NMS极大值抑制

引言 随着深度学习的发展&#xff0c;物体检测&#xff08;Object Detection&#xff09;成为计算机视觉领域的一项重要任务。传统的物体检测方法往往依赖于手工设计的特征和滑窗搜索策略&#xff0c;效率低下且效果有限。近年来&#xff0c;基于深度学习的方法&#xff0c;尤…

leetcode-63-不同陆路径II

题解&#xff1a; 1、设dp[i][j]为到达(i,j)点的路径。当grid[i][j]1时,dp[i][j]0;否则dp[i][j]为到达(i-1,j)的最多路径与到达(i,j-1)的最多路径之和。当(i,j)位于第一行时&#xff0c;dp[i][j]dp[i][j-1]。当(i,j)位于第一列时&#xff0c;dp[i][j]dp[i-1][j]。 2、初始化M…

MATLAB锂电概率分布模型

&#x1f3af;要点 概率分布等效电路模型结合了路径相关速率能力及状态估计中滞后效应。纠正了充电状态中时间误差累积及避免开路电压中电压滞后现象。使用电流方向和电池容量相关函数描述开路电压&#xff0c;并使用微分方程描述电压滞后现象。模型结构基于一级相变的材料机制…

新华三H3CNE网络工程师认证—OSPF路由协议

OSPF是典型的链路状态路由协议&#xff0c;是目前业内使用非常广泛的IGP协议之一。本博客将对OSPF路由协议进行总结。 OSPF目前针对IPv4协议使用的是OSPFVersion2(RFC2328)&#xff1b; 针对IPv6协议使用OSPFVersion3(RFC2740)。如无特殊说明本章后续所指的OSPF均为OSPF Versi…

监督学习之逻辑回归

逻辑回归&#xff08;Logistic Regression&#xff09; 逻辑回归是一种用于二分类&#xff08;binary classification&#xff09;问题的统计模型。尽管其名称中有“回归”二字&#xff0c;但逻辑回归实际上用于分类任务。它的核心思想是通过将线性回归的输出映射到一个概率值…

【MATLAB源码-第193期】基于matlab的网络覆盖率NOA优化算法仿真对比VFINOA,VFPSO,VFNGO,VFWOA等算法。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 NOA&#xff08;Network Optimization Algorithm&#xff0c;网络优化算法&#xff09;是一个针对网络覆盖率优化的算法&#xff0c;它主要通过优化网络中节点的分布和配置来提高网络的整体覆盖性能。网络覆盖率是衡量一个无…