在做一些常规应用的时候,我们往往需要确定条件的内容,以便在后台进行区分的进行精确查询,在移动端,由于受限于屏幕界面的情况,一般会对多个指定的条件进行模糊的搜索,而这个搜索的处理,也是和前者强类型的条件查询处理类似的处理过程,因此本篇随笔探讨两种不同查询在前端界面上的展示效果,以及后端基于.netCore的Web API端的基类进行的统一封装处理。
在基于Vue3+Typescript+ElementPlus的前端界面中,查询是很多界面需要拥有的功能,如下所示。

展开后的全部查询条件

以上的查询部分是一个查询函数的处理,如下代码所示。
// 查询列表处理
async function search() {
pageInfo.pageIndex = 1; // 重置为第一页
//默认使用当前用户公司
const userInfo = $u.util.storageSession.getItem('user_info');
searchForm.company_ID = userInfo?.company_ID; //所属公司
await getlist(); //获取列表
}
//列表数据获取
async function getlist() {
loading.value = true;
var param = {
// 分页条件
SkipCount: (pageInfo.pageIndex - 1) * pageInfo.pageSize,
MaxResultCount: pageInfo.pageSize,
Sorting: sorting.value,
// 查询过滤条件
Name: searchForm.name,
MobilePhone: searchForm.mobilePhone,
Email: searchForm.email,
QQ: searchForm.qq,
Nickname: searchForm.nickname,
HandNo: searchForm.handNo,
IsExpire: searchForm.isExpire,
Title: searchForm.title,
dept_ID: searchForm.dept_id,
company_ID: searchForm.company_ID
};
//日期条件处理
addDateRange(param, searchForm.creationTime);
let result = await user.GetList(param);
if (result) {
list.value = result.items;
pageInfo.totalCount = result.totalCount;
}
setTimeout(() => {
loading.value = false;
}, 500);
}
我们看到,这些条件都是由特定的参数组成的,因此他们是精确性的属性查询。
前端根据框架后端的接口进行前端JS端的类的封装处理,引入了ES6类的概念实现业务基类接口的统一封装,简化代码。
权限模块我们涉及到的用户管理、机构管理、角色管理、菜单管理、功能管理、操作日志、登录日志等业务类,那么这些类继承BaseApi,就会具有相关的接口了,如下所示继承关系。

按照这个思路,我们在BaseApi的ES6类里面定义了对应Web API基类里面的操作方法,如下所示。

这样,我们在创建一个业务类的时候,如果没有特殊的自定义接口,只需要继承基类BaseApi即可具有所有的常规基类方法了。

我们再来后端看看具体的查询逻辑实现,首先需要了解各个控制器之间的继承关系,如下图所示 。

同样,我们基础的查询处理逻辑,主要也是放在BusinessController里面实现,毕竟是通用的逻辑,变化的只是一些实体信息,因此可以通过泛型的模板方法设计模式处理变化的部分。

我们可以看到,在BusinessController控制器部分,它也只是对Service层逻辑的简单封装一下,核心的处理逻辑部分,在下面的基类Service层的代码中。
/// <summary>
/// 根据条件获取列表
/// </summary>
/// <param name="input">分页查询条件</param>
/// <returns></returns>
public virtual async Task<PagedResultDto<TEntity>> GetListAsync(TGetListInput input)
{
var query = CreateFilteredQueryAsync(input);
var totalCount = await query.CountAsync();
query = ApplySorting(query, input);
query = ApplyPaging(query, input);
var list = await query.ToListAsync();
return new PagedResultDto<TEntity>(
totalCount,
list
);
}
这里基类Service层主要处理逻辑部分,而具体的构建精确的查询处理条件,下放在了每个具体业务Service类中进行处理了。
UserService是具体对应的业务类的逻辑处理层,该类的定义方法如下所示。
/// <summary>
/// 应用层服务接口实现
/// </summary>
public class UserService : MyCrudService<UserInfo, int, UserPagedDto>, IUserService
下放在UserService这个具体业务的Service类中的查询处理逻辑,这部分通过代码生成工具生成即可。
/// <summary>
/// 自定义条件处理
/// </summary>
/// <param name="input">查询条件Dto</param>
/// <returns></returns>
protected override ISugarQueryable<UserInfo> CreateFilteredQueryAsync(UserPagedDto input)
{
var query = base.CreateFilteredQueryAsync(input);
query = query
.WhereIF(input.ExcludeId.HasValue, t => t.Id != input.ExcludeId) //不包含排除ID
.WhereIF(input.PID.HasValue, s => s.PID == input.PID)
.WhereIF(!input.HandNo.IsNullOrWhiteSpace(), t => t.HandNo.Contains(input.HandNo)) //如需要精确匹配则用Equals
.WhereIF(!input.Name.IsNullOrWhiteSpace(), t => t.Name.Contains(input.Name)) //如需要精确匹配则用Equals
.WhereIF(!input.FullName.IsNullOrWhiteSpace(), t => t.FullName.Contains(input.FullName)) //如需要精确匹配则用Equals
.WhereIF(!input.Nickname.IsNullOrWhiteSpace(), t => t.Nickname.Contains(input.Nickname)) //如需要精确匹配则用Equals
.WhereIF(input.IsExpire.HasValue, t => t.IsExpire == input.IsExpire) //如需要精确匹配则用Equals
//过期时间区间查询
.WhereIF(input.ExpireDateStart.HasValue, s => s.ExpireDate >= input.ExpireDateStart.Value)
.WhereIF(input.ExpireDateEnd.HasValue, s => s.ExpireDate <= input.ExpireDateEnd.Value)
****//此处省略更多其他条件
;
return query;
}
通过逻辑和具体对象的实现分离,从而构建了很多通用的基类函数,这些函数只需要在子类重写一些规则即可实现更加详细的处理,也称为模板方法的设计模式,这种方式广泛应用于基类函数的抽象处理。
介绍了前面基于Vue3+Typescript+ElementPlus的前端界面的内容,主要还是用来引出基于UniApp+Vue的移动端实现多条件查询的处理,一般移动端的界面空间比较宝贵,所以往往查询通过组合条件的方式进行模糊查询处理,如下界面所示。

在查询框中输入一些条件,会在后端对多个条件进行模糊匹配,并返回相应的结果列表进行展示,如下所示。

查询界面只是接受了一个输入值,通过传递该值,在后端进行多字段的匹配查询处理。前端界面如下所示。
async getlist() {
var params = {
MaxResultCount: this.pageSize,
SkipCount: (this.pageIndex - 1) * this.pageSize,
Sorting: '',
}
if (this.isAdvance) {
let {
startDate,
endDate,
filter,
deliveryArea,
line
} = this.advanceData;
params.TimeStart = startDate
params.TimeEnd = endDate
params.DeliveryAreas = deliveryArea
params.Lines = line
params.Filter = filter
} else {
params.Filter = this.searchValue;
}
console.log(params)
this.loadding = true;
this.pullUpOn = true;
let res = {};
if (this.isAdvance) {
res = await sign.GetAllByFilter2(params);
} else {
res = await sign.GetAllByFilter(params);
}
console.log(res)
if (this.totalCount == res.totalCount) {
this.hasmore = false; //没有了
this.loadding = false;
this.pullUpOn = false;
uni.stopPullDownRefresh();
return;
}
this.totalCount = res.totalCount;
let items = res.items;
for (var i = 0; i < items.length; i++) {
this.list.push(items[i]);
}
if (this.list.length < res.totalCount) {
this.pageIndex += 1;
console.log(res.totalCount)
} else {
this.hasmore = false; //没有了
}
this.loadding = false;
this.pullUpOn = false
uni.stopPullDownRefresh();
let count = items.length;
let options = {
msg: `刷新成功,为你更新了${count}条数据`,
duration: 2000,
type: "translucent"
};
if (this.pageIndex > 1) {
setTimeout(() => {
this.$refs.toast.showTips(options);
}, 300);
}
},
有时候,如需要精确一些的条件处理,也可以以自定义条件的方式进行查询处理的界面。

单击【筛选】按钮进入抽屉式的展示页面,弹出高级查询的相关字段属性,可以进行一定的条件设置处理。

同样我们在UniApp+Vue的移动前端项目上,也需要设置BaseApi的基础接口,如下所示。

而我们的业务类 Sign-Receipt(Sign-Receipt.js )只需要继承BaseApi(base-api.js)类即可,如下所示。
import BaseApi from '@/api/base-api'
// 业务类自定义接口实现, 通用的接口已经在BaseApi中定义
class Api extends BaseApi {
FindByCode(shopCode) { // GET 根据客户代码获取记录
var params = {
shopCode
};
return this.httpget(this.baseurl + 'Find-ByCode', params)
}
GetAllByFilter2(params) { // 根据条件获取所有记录
var url = this.baseurl + 'list-filter2';
return this.httpget(url, params)
}
}
// 构造接口对象信息 Api实例,并传递业务类接口地址
export default new Api('/api/SignReceipt/')
对应的后端接口,同样也是使用前面介绍精确查询的方式进行处理,在基类Service层里面,有对应通用的模糊查询方法定义。
/// <summary>
/// 根据指定的Filter值分页获取列表
/// </summary>
/// <param name="input">分页查询条件</param>
/// <returns></returns>
public virtual async Task<PagedResultDto<TEntity>> GetListByFilterAsync(PagedSortedAndFilteredInputDto input)
{
var query = CreateFilteredQueryAsync(input.Filter);
var totalCount = await query.CountAsync();
//排序处理
if (!input.Sorting.IsNullOrWhiteSpace())
{
query = query.OrderBy(input.Sorting);
}
else
{
query = ApplyDefaultSorting(query);
}
//分页处理
query = query.Skip(input.SkipCount).Take(input.MaxResultCount);
var list = await query.ToListAsync();
return new PagedResultDto<TEntity>(
totalCount,
list
);
}
那么下放给子类的CreateFilteredQueryAsync 函数就是实现逻辑的关键,毕竟基类是无法构建正确的条件的。
而对应的SignReceiptService类中,是业务类的逻辑处理方法,该类的定义方法如下所示
/// <summary>
/// 拍照签收 应用层服务接口实现
/// </summary>
public class SignReceiptService : MyCrudService<SignReceiptInfo,string, SignReceiptPagedDto>, ISignReceiptService
因此它里面可以重写模糊查询条件的逻辑,如下代码所示。
/// <summary>
/// 自定义条件处理(根据Filter进行的过滤处理)
/// </summary>
/// <param name="filter">查询条件Dto</param>
/// <returns></returns>
protected override ISugarQueryable<SignReceiptInfo> CreateFilteredQueryAsync(string filter)
{
var query = base.CreateFilteredQueryAsync(filter);
query = query.WhereIF(!filter.IsNullOrWhiteSpace(), t => //模糊搜索用Contains, 如需要精确匹配则用Equals
t.ShopCode.Contains(filter) || t.ShopName.Contains(filter) || t.Line.Contains(filter) ||
t.DeliveryArea.Contains(filter) || t.SignMan.Contains(filter) || t.DeliverName.Contains(filter) || t.Note.Contains(filter)
);
return query;
}
有了这些条件的定义,我们就可以在Web API的后端,对前端的参数进行联合的模糊查询处理,从而为移动前端提供更好的查询接口服务。
而对于高级查询的处理,我们先要定义好对应的通用的实体类信息,如下所示。

然后在具体的进行处理查询逻辑即可,这部分和前面的逻辑处理类似,只是无法实现基类通用的处理而已,因此下发到具体的业务类进行定义。

最后在控制器部分,进行一个简单的封装处理即可。

以上就是基于不同前端,包括基于Vue3+Typescript+ElementPlus的前端界面,以及基于UniApp+Vue的移动端界面,实现一些常见查询的处理,前端和后端的配合处理逻辑。
系列文章:
《基于SqlSugar的开发框架的循序渐进介绍(1)--框架基础类的设计和使用》
《基于SqlSugar的开发框架循序渐进介绍(2)-- 基于中间表的查询处理》
《基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发》
《基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理 》
《基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转》
《基于SqlSugar的开发框架循序渐进介绍(6)-- 在基类接口中注入用户身份信息接口 》
《基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传》
《基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录》
《基于SqlSugar的开发框架循序渐进介绍(9)-- 结合Winform控件实现字段的权限控制》
《基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装,实现对后端API数据的访问和基类的统一封装处理》
《基于SqlSugar的开发框架循序渐进介绍(11)-- 使用TypeScript和Vue3的Setup语法糖编写页面和组件的总结》
《基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理》
《基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用》
《基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用》
《基于SqlSugar的开发框架循序渐进介绍(15)-- 整合代码生成工具进行前端界面的生成》
《基于SqlSugar的开发框架循序渐进介绍(16)-- 工作流模块的功能介绍》
《基于SqlSugar的开发框架循序渐进介绍(17)-- 基于CSRedis实现缓存的处理》
《基于SqlSugar的开发框架循序渐进介绍(18)-- 基于代码生成工具Database2Sharp,快速生成Vue3+TypeScript的前端界面和Winform端界面》
《基于SqlSugar的开发框架循序渐进介绍(19)-- 基于UniApp+Vue的移动前端的功能介绍》
《基于SqlSugar的开发框架循序渐进介绍(20)-- 在基于UniApp+Vue的移动端实现多条件查询的处理》
我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.
我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby数组,我们在StackOverflow上找到一
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano
当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions