在做一些常规应用的时候,我们往往需要确定条件的内容,以便在后台进行区分的进行精确查询,在移动端,由于受限于屏幕界面的情况,一般会对多个指定的条件进行模糊的搜索,而这个搜索的处理,也是和前者强类型的条件查询处理类似的处理过程,因此本篇随笔探讨两种不同查询在前端界面上的展示效果,以及后端基于.netCore的Web API端的基类进行的统一封装处理。
1、前端精确条件的查询处理
在基于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;
}
通过逻辑和具体对象的实现分离,从而构建了很多通用的基类函数,这些函数只需要在子类重写一些规则即可实现更加详细的处理,也称为模板方法的设计模式,这种方式广泛应用于基类函数的抽象处理。
2、前端模糊查询及自定义查询的处理
介绍了前面基于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 = startDateparams.TimeEnd = endDateparams.DeliveryAreas = deliveryAreaparams.Lines = lineparams.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 = falseuni.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, 如需要精确匹配则用Equalst.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的移动端界面,实现一些常见查询的处理,前端和后端的配合处理逻辑。