2023年2月

Winform界面中,有时候,需要对界面进行一些多语言的处理,以适应客户场景的使用,如有时候需要考虑英文、日文、韩文等客户的使用,那么在Winform界面如何实现这种多语言的正常处理呢,本篇基于Winform开发框架的基础上介绍使用多语言的处理过程。

1、多语言的处理思路

在国际化环境下,越来越多的程序需要做多语言版本,以适应各种业务需求的变化。在Winform应用程序中实现多语言也有常规的处理方式处理,不过需要针对每个语言版本,重新修改Winform界面的显示,对一些常规的辅助类,也需要引入一个统一的资源管理类来处理多语言的问题,相对比较繁琐。

我们的Winform开发框架,主要为了简化多语言的处理,编写了一些辅助的处理类库,读取对应的JSON多语言对键值对应文件,实现多语言的切换处理。

我们在开始编写程序的时候,使用我们熟悉的中文,然后在百度翻译(
https://fanyi.baidu.com/
)或者Google翻译对应的内容,存储在对应的目录中。

程序运行的时候,加载对应目录json文件即可实现多语言的处理。

首先我们开发系统的时候,需要创建一些多语言的对应目录,具体的目录我们遵循约定规则即可。

为了便于我们的处理,我们以中文为键,其中JSON里面对应的中文和其他语言的翻译结果,如下面是日文的参考内容。

我们记得把JSON文件,始终复制到目录上或者如果较新则复制,

这个目录就是会输出到debug或者Release的运行目录中,我们就是根据相对于运行目录进行资源读取即可,所有模块共用同一的多语言文件,我们可以把各个模块基础通用的多语言文件放在Basic.json文件中,也可以根据模块独立起名。

实际上目录名称是为了区分而已,程序加载的时候,会把目录下面所有的JSON文件进行加载,读取里面的键值作为资源的字典参照。

我们以我们常规的母语开发,即使我们不做多语言,也不影响代码的正常处理,我们只需要把窗体上和代码里面的中文提取出来,然后进行多语言处理(如变为英文/韩文/日文)即可。

2、多语言的处理效果

为了便于体验多语言的切换实际效果,我这里编写了一些测试的案例,并准备了中文、英文、日文、韩文的对照JSON文件,默认界面效果如下。

切换到英文的时候,界面如下所示。

控件英文界面效果

其他英文测试界面

切换韩文界面效果如下所示

其他视图界面效果

日文界面效果如下。

为了更好的测试其他控件的多语言处理,我们编写了一些控件的展示界面如下GridControl,TreeList等控件。

效果都没有问题,来回测试测试也是正常,符合要求。

3、多语言的代码处理

看完实际的效果,那么我们需要如何在代码中处理,才能实现多余的正常切换呢。

首先我们为了记住用户选择的语言区域信息,我们在配置文件中增加一个键值用来保存区域代码。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <addkey="CultureInfo"value="en-US" />
  </appSettings>

然后切换语言的时候,编写处理语言的切换和区域的存储即可,如下所示。

        /// <summary>
        ///改变界面的语言/// </summary>
        /// <param name="lang"></param>
        private void ChangeLang(stringlang)
{
if (lang == "cn")
{
System.Threading.Thread.CurrentThread.CurrentUICulture
= new System.Globalization.CultureInfo("zh-Hans");
config.AppConfigSet(
"CultureInfo", "zh-Hans");
}
else if (lang == "kr")
{
System.Threading.Thread.CurrentThread.CurrentUICulture
= new System.Globalization.CultureInfo("ko-KR");//韩文界面 config.AppConfigSet("CultureInfo", "ko-KR");
}
else if (lang == "en")
{
System.Threading.Thread.CurrentThread.CurrentUICulture
= new System.Globalization.CultureInfo("en-US");//英文界面 config.AppConfigSet("CultureInfo", "en-US");
}
else if (lang == "jp")
{
System.Threading.Thread.CurrentThread.CurrentUICulture
= new System.Globalization.CultureInfo("ja-JP");//日文界面 config.AppConfigSet("CultureInfo", "ja-JP");
}

LanguageHelper.Reload();
//重新初始化JSON资源 LanguageHelper.InitLanguage(this, true);//刷新界面控件 }

上面的
LanguageHelper
辅助函数,主要就是用来对界面进行多语言处理的。

为了方便,我们的封装的基类窗体BaseForm、BaseEditForm、BaseDock等基础窗体,我们一般在界面初始化的时候,都会调用
LanguageHelper
辅助函数来进行界面的多语言处理。

因此,如果是采用我们的基类窗口,那么默认就带有多语言的处理功能了,我们按照约定规则,处理好对应的多语言翻译文件JSON文件即可。

如果是对于一些自定义的窗体,或者继承默认的窗体类,那么在构造函数完成处理的时候,可以增加一行代码来处理多语言,如上面的规则所示即可。

这样也就能实现多语言的处理了

在一个应用系统的开发框架中,往往很多地方需要用到缓存的处理,有些地方是为了便于记录用户的数据,有些地方是为了提高系统的响应速度,如有时候我们在发送一个短信验证码的时候,可以在缓存中设置几分钟的过期时间,这样验证短信验证码的时候,就会自动判断是否过期了。本篇随笔结合CSRedis的使用,介绍如何实现缓存的初始化及使用的处理。

1、在基于.netCore的Web API后端使用CSRedis

关于CSRedis的使用,我们可以参考Github网站:
https://github.com/2881099/csredis
进行了解。

首先我们在使用前,需要添加对应的程序集应用。

Package Name NuGet Downloads
CSRedisCore nuget stats
Caching.CSRedis nuget stats IDistributedCache

CSRedisCore是必须的,而Caching.CSRedis则是在用到分布式缓存的时候需要用到。

初始化CSRedis也比较简单,如代码所示。

var csredis = new CSRedis.CSRedisClient("127.0.0.1:6379,password=123,defaultDatabase=13,prefix=my_");

不过我们的Redis配置一般放在appSettings.json文件中,不是直接硬编码的,所以需要调整一下。

//初始化Redis及分布式缓存
var redisConnectionString = builder.Configuration["CSRedis:ConnectString"];
RedisHelper.Initialization(
newCSRedisClient(redisConnectionString));
builder.Services.AddSingleton
<IDistributedCache>(new CSRedisCache(RedisHelper.Instance));

常规的缓存设置,通过键、值、时间设置等几个内容进行处理,如下代码所示。

RedisHelper.Set("test1", "123123", 60);

RedisHelper.Get(
"test1");

如果我们要清空所有的缓存键值,那么对键进行模式匹配进行处理即可。

/// <summary>
///清空Redis缓存/// </summary>
protected voidClearRedisCache()
{
//查找所有分区节点中符合给定模式(pattern)的 key var cacheKeys = RedisHelper.Keys("*");
RedisHelper.Del(cacheKeys);
}

除了常规的缓存处理,redis也支持消息队列的处理,消息队列最熟悉无疑是 rabbitmq,它基本是业界标准的解决方案。另外 redis 也提供了多种实现轻订阅方法。如下面是一案例代码。

//程序1:使用代码实现订阅端
var sub = RedisHelper.Subscribe(("chan1", msg =>Console.WriteLine(msg.Body)));//sub.Disponse();//停止订阅//程序2:使用代码实现发布端
RedisHelper.Publish("chan1", "666666");

我们这里不深究消息队列的处理,有兴趣的可以参考文章《
【由浅至深】redis 实现发布订阅的几种方式
》进行了解即可。

2、前端发送短信验证码及后端判断

我们这里以一个短信验证码登录的前端来介绍CSRedis缓存的设置、获取、移除等操作过程。

例如,我们的移动前端,需要验证码登录系统的时候,需要发送验证码的操作,如下所示。

前端通过初步判断手机号码正确后,可以向后端请求发送验证码,如下逻辑代码所示(vue)

//获取验证码
getCode() {if (this.model.mobile.length < 11 && !uni.$u.test.mobile(this.model.mobile)) {
uni.$u.toast(
'手机号码不正确')return;
}
//发送短信验证码 var params ={
PhoneNumber:
this.model.mobile
}
user.SendPhoneLoginSmsCode(
params).then(res =>{if(res.success) {this.show = false;
uni.$u.toast(`验证码已发送至手机 ${
this.model.mobile},请注意查收!`)

let interval
= setInterval(() =>{this.second--;if (this.second <= 0) {this.show = true;
clearInterval(interval);
}
},
1000);
}
else{
uni.$u.toast(
'发送出现错误:' +res.errorMessage)
}
})
}

WebAPI后端,处理逻辑是构建随机的验证码并通过短信发送到手机上,并缓存好对应的验证码,后端的处理代码如下所示

/// <summary>
///发送登录动态码/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
[Route(
"send-login-smscode")]public async Task<CommonResult>SendPhoneLoginSmsCode(PhoneCaptchaModel model)
{
//获取随机6位数字动态验证码 var code = RandomChinese.GetRandomNumber(6);//使用自定义模板处理短信发送 string message = string.Format(ConfigData.MySmsCodeTemplate, code);var result = await_smsSender.SendAsync(model.PhoneNumber, message);if(result.Success)
{
var cacheKey = model.PhoneNumber;//以手机号码作为键存储验证码缓存 var cacheItem = new SmsLoginCodeCacheItem { Code = code, PhoneNumber =model.PhoneNumber };

RedisHelper.Set(cacheKey, cacheItem, TimeSpan.FromMinutes(ConfigData.SmsCodeExpiredMinutes));
//获取的时候//var tmp = RedisHelper.Get<SmsLoginCodeCacheItem>(cacheKey); }returnresult;
}

顺利发送短信验证码后,前端会提示用户验证码发送情况,并要求输入验证码进行登录,前端登录的代码如下所示。

//短信验证码登录
loginByCode() {var params ={
mobile:
this.model.mobile,
smscode:
this.model.code
};
console.log(params);
user.dynamiclogin(params)
.then(res
=>{
uni.$u.toast(
'验证成功');this.gotoPage();
})
.
catch(error =>{
console.log(
'验证失败' +error);
uni.$u.toast(error);
});
},

后端的登录处理,主要就是通过在Redis中读取对应的手机验证码,如果匹配进行令牌的生成处理,否则提示错误信息。

/// <summary>/// 登录授权处理/// </summary>/// <returns></returns>
[AllowAnonymous]
[HttpPost]
[Route(
"authenticate-byphone")]
public async Task
<AuthenticateResultDto>AuthenticateByPhoneCaptcha(PhoneCaptchaModel model)
{
var authResult = newAuthenticateResultDto();
#region 条件检查
if(string.IsNullOrEmpty(model.PhoneNumber))
{
throw new MyApiException("手机号不能为空");
}
if(string.IsNullOrEmpty(model.SmsCode))
{
throw new MyApiException("验证码不能为空");
}
var userInfo = await _userService.GetFirstAsync(s => s.MobilePhone ==model.PhoneNumber);if (userInfo == null)
{
throw new MyApiException("用户手机不存在");
}
#endregion
var cacheKey = model.PhoneNumber;//以手机号码作为键存储验证码缓存 var item = RedisHelper.Get<SmsLoginCodeCacheItem>(cacheKey);if (item != null && item.Code ==model.SmsCode)
{
//根据用户身份生成tokenresult authResult.AccessToken = GenerateToken(userInfo); //令牌 authResult.Expires = expiredDays * 24 * 3600; //失效秒数 authResult.Success = true;//成功 authResult.UserId = userInfo.Id;//当前用户Id //移除缓存短信键值 RedisHelper.Del(cacheKey);
}
else{
authResult.Error
= "登录失败,无法生成令牌";
}
returnauthResult;
}

如果顺利生成令牌,则从redis中移除对应的缓存键值即可。

如果我们需要查看Redis的缓存内容,Windows端可以安装 RedisDesktopManager 进行查看管理Redis的内容。

发送短信后,键值会保存在Redis缓存中,可以通过RedisDesktopManager  进行查看。

手机端顺利收到短信提示。

以上就是关于在SqlSugar的开发框架,通过介绍短信验证码的前后端协作方式,介绍使用CSRedis实现缓存的处理过程。

系列文章:


基于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端界面

我们开发一个系统,在保证风格统一、代码强壮、可读性强等基础上,还能够结合代码生成工具快速开发相关后端,以及各种前端界面的,无疑是非常好的,既保证了项目的代码质量,又能够极大的提高开发效率。代码生成工具Database2Sharp是在完善的开发项目上,抽取出数据变化的部分,通过演绎、归纳、反复演绎和归纳等提炼方式抽取出相关的规则,以工具的方式来快速提高生产率,使得我们在开发各种不同的项目上的时候,能够事半功倍,本篇随笔介绍基于代码生成工具Database2Sharp,快速生成Vue3+TypeScript的前端界面和Winform端界面。

1、代码生成工具Database2Sharp的使用

对于SQLSugar的项目框架,我们为了方便,分别单独提供后端代码和Web API代码的生成、Winform界面代码的生成,以及前面介绍到的Vue3+TypeScript+ElementPlus的代码生成操作。

代码生成工具的界面效果如下所示,通过入口菜单,可以实现不同部分的代码快速生成。

2、Vue3+TypeScript的前端界面代码生成

Vue3+TypeScript+ElementPlus前端界面,常规页面的内容的整体界面布局,它包含常规的列表界面,新增、编辑、查看、导入等界面,除了列表页面,其他内容以弹出层对话框的方式进行处理,如下界面示意图所示。

Vue3+TypeScrip最大的特点就是组件模块的便利性。

我们通过抽取共共性的内容组成组件,从而不同的页面内容,只需要维护不同的文件即可,从而隔离变化,提高代码的可读性和可维护性。

根据以上的页面划分,我们把一个页面分为search.vue、edit.vue、import.vue、view.vue、index.vue,其中index.vue为整合各个组件的主页面,在视图中如下所示。我们每个业务模块都是如此统一划分,因此比较统一,同时也是为后续的代码生成工具批量生成做好准备。

因此在index.vue页面中,我们整合了几个组件页面即可,如下所示。

<template>
  <divclass="main">
    <!--条件及列表展示-->
    <Searchref="searchRef"@show-import="showImport"@show-add="showAdd"@show-view="showView"@show-edit="showEdit" />

    <!--查看详细组件界面-->
    <view-dataref="viewRef" />
    <!--新增、编辑组件界面-->
    <edit-dataref="editRef"@submit="refreshData" />
    <!--模板导入信息-->
    <import-dataref="importRef"@finish="finishImport" />
  </div>
</template>

<scriptsetup lang="ts">import { reactive, ref, onMounted } from'vue';

import Search from
'./search.vue';
import ViewData from
'./view.vue';
import EditData from
'./edit.vue';
import ImportData from
'./import.vue';

这样我们只需要维护查询页面、编辑页面、查看页面、导入页面的内容就可以了,而这些主要反映在相关的字段信息上,大的方向上我们已经根据页面的布局设置好了。

常规的列表页面内容,包含一些查询条件,以及相关的入口按钮事件的处理,如下界面所示

而如果是需要显示树列表的内容,我们在代码生成的时候,选择树列表界面生成即可,大致效果如下所示。

最后页面效果如下所示。

查看详细的页面内容效果如下所示。

视图页面效果如下所示。

编辑界面的效果如下所示。

导入界面,一般我们分为几个步骤,一个是提供导入模板下载,然后上传文件并显示数据,然后确认提交即可。

由于导入数据的逻辑上大致类似,不同的是他们的业务数据和验证规则,因此我们通过自定义组件的方式,来简化

相关的处理。

我通过改造ele-import 的第三方组件,让它支持Vue3+Typescript语法,实现对业务数据的上传操作。】

通过隔离页面组件的内容变化,实现变化不同通过数据库表关系生成,固定部分采用规定模板预置内容,实现了代码的快速生成操作。

3、Winform端界面代码生成

和我们其他Winform界面一样,Winform项目的界面代码生成,生成包括普通的列表界面,以及主从表Winform界面代码生成。Winform界面的生成已经非常成熟,我们只需要替换一下数据请求的处理即可,常规的实体类,接口等内容,通过SqlSugar开发框架生成后,引入相关的程序集使用即可。

Winform项目大概情况如下所示。

Winform端本身已经包含了完善的权限管理、字典管理、附件管理、综合案例,以及工作流管理等内容。

用户操作日志信息如下所示。

工作相关界面如下所示。

而我们基于代码生成工具的Winform界面快速生成,也是基于成熟的Winform开发经验和界面效果来规范化的。

SqlSugar的项目基础核心数据模块SugarProjectCore,主要就是开发业务所需的数据处理和业务逻辑的项目,为了方便,我们区分Interface、Modal、Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface是定义访问接口,Service是提供具体的数据操作实现。

WInform界面生成的界面效果如下所示,通过选定不同的字段,定制化界面效果。

生成的简单业务表界面,包括分页列表展示界面,在列表界面中整合查看、编辑、新增、删除、导入、导出、查询/高级查询等功能,整合的编辑界面也是依据数据库表的信息进行生成的。

列表界面和编辑界面效果如下所示。

而主从表界面生成的效果如下所示。

我们看看生成的Winform列表界面代码,如下所示。

另外我们把一些常用的处理逻辑放在函数中统一处理,如AddData、EditData、DeleteData、BindData、GetData、ImportData、ExportData等等,如下所示。

如需要下载测试代码生成工具
Database2sharp
,请到官网
https://www.iqidi.com/database2sharp.htm
下载试用。

在之前的SqlSugar系列随笔中,介绍了很多我们关于SqlSugar的开发框架的内容,SqlSugar的开发框架的目的是多前端应用场景,因此其中会包含各种不同的前端应用,前面介绍了基于DevExpress的Winform的前端应用,以及基于Vue3+TypeScript+ElementPlus的BS前端应用,本篇随笔继续介绍SqlSugar的开发框架的另一个前端应用,基于UniApp+Vue+ThorUI的移动前端。

1、基于UniApp+Vue+ThorUI的移动前端

前端开发,可以是基于Vue&Element的管理后台的前端开发,也可以是Vue + UniApp+手机端组件库的开发H5或者App应用,技术路线都是基于Vue的,我们这里主要介绍UniApp+HBuliderX+Vue+ThorUI来开发H5端的应用。

UniApp
是一个使用
Vue.js
开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。

使用UniApp的主要原因是它的生态比较完善,而且提供了不同平台的统一的接口调用方法,因此非常方便使用它来统一构建多端应用。

使用UniApp,为了集成方便,一般也会使用HBuilderX开发工具来进行前端的开发。
HBuilderX
编辑器是DCloud全新推出的一款HTML5的Web开发工具。HBuilder目前有两个版本,一个是windows版,一个是mac版。下载的时候根据自己的电脑选择适合自己的版本即可。如果不考虑深度集成的原因,还可以考虑使用更加广泛的VS Code编辑器,毕竟VS Code可是前端界的开发标准工具了。

另外UniApp也有自己的组件库,同时也做的很不错,不过由于参照模板的问题,我们这里使用了会员版的
Thor UI
,可以参考的案例更多,方便做出更好的界面效果。

Thor UI
是一款开发移动端网页应用、小程序的前端 UI 组件库,uni-app 版本还可以编译成为安卓/ iOS APP 应用,特点是轻量、简洁,组件覆盖比较全面,使开发更高效。Thor UI目前有微信小程序原生版本 (opens new window)和uni-app版本。

SqlSugar开发框架主要的设计模块场景如下所示。

1)其中一些常用的类库,以及SqlSugar框架的基类放在框架公用模块里面。

2)Winform开发相关的基础界面以及通用组件内容,放在基础Winform界面库BaseUIDx项目中。

3)基础核心数据模块SugarProjectCore,主要就是开发业务所需的数据处理和业务逻辑的项目,为了方便,我们区分Interface、Modal、Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface是定义访问接口,Service是提供具体的数据操作实现。其中Service里面一些框架基类和接口定义,统一也放在公用类库里面。

4)Winform应用模块,主要就是针对业务开发的Winform界面应用,而Winform开发为了方便,也会将一些基础组件和基类放在了BaseUIDx的Winform专用的界面库里面。

5)WebAPI项目采用基于.net Core 的项目开发,通过调用SugarProjectCore实现相关控制器API的发布,并整合Swagger发布接口,供其他前端界面应用进行调用。

6)纯前端通过API进行调用Web API的接口,纯前端模块可以包含Vue3&Element项目,以及基于EelectronJS应用,发布跨平台的基于浏览器的应用界面,以及其他H5应用、App应用或者小程序应用模块,整合Web API进行业务数据的处理或者展示。

其中Web API的Swagger接口管理界面如下。

Winform端的前端界面如下所示。

Vue3+Typescript+ElementPlus的前端界面如下所示。

而基于Web API的移动端UniApp应用(HbuilderX开发工具)的开发项目界面效果如下所示。

2、UniApp+Vue+ThorUI的移动前端功能介绍

了解了大概的项目架构和技术路线后,我们来看看移动端的功能介绍。

和其他前端的功能配套,那么我们移动前端也可以管理一些系统的数据,如用户、机构、角色、字典等等信息,以及一些额外的业务数据处理,如一些业务基础数据的录入,业务的审核,业务统计报表等功能。

1)用户登录

移动前端用户通过账号密码进行登录(也可以选择短信验证码方式登录)后台管理系统,登录界面如下所示。

用户输入系统正确的账号、密码,即可顺利登录系统,系统根据用户所属角色和分组信息,展示用户能够管理的功能页面。系统默认展示首页视图。

2)忘记密码

如果用户忘记自己的账号密码,可以通过手机验证码方式进行重置密码操作。

3)重设密码、修改用户资料

登录系统成功后,在系统右上角的图像按钮中,可以进行用户密码修改,也可以在编辑按钮或者图标中进入用户信息编辑处理界面,界面效果如下所示。

如需修改用户头像,单击头像图片,图片选择确认后上传图片进行头像修改。

4)修改手机号码

如需修改手机号码,单击手机号栏目,可以进行手机号码的修改调整,手机修改需要接受短信验证码,输入成功后才能更新该用户的手机号码。

5)注册账号

由使用人员使用手机和手机验证码自己申请注册用户,由系统管理员审核同意后,创建并分配系统用户,一个手机号码只能申请一次。

注册账号的界面如下所示。

6)注册用户审核

系统管理员登录后,可以在管理功能中进行注册用户的管理操作,如下界面所示。

单击用户列表,弹出审核对话框,审核通过后,系统将创建指定相关信息的用户账号,可以使用该账号和初始密码进行登录。拒绝申请则不再接收该用户的注册,系统不创建账号。

7)字典管理

字典管理是一个通用的字典大项、字典项目的维护管理,便于系统下拉列表以及相关参考值的维护,字典管理界面如下所示。

这样在具体页面中就可以直接使用字典项目信息了,如下树形列表和下拉项目所示。

为了方便,在手机前端为管理员提供一个维护常规字典类型和字典项目的入口,在【管理工具】【字典维护】入口进入,如下所示。

单击其中字典大类,可以进行查看或者编辑对应字典大类下的项目信息,如下界面所示。

如需要对字典大类的信息进行维护,可以单击顶部修改的按钮,对字典大类的信息进行编辑处理,如下编辑界面所示。

8)我的地址簿

为了方便管理一些地址信息,系统提供了一个通用的地址簿进行管理,可以录入常见的地址信息,如下界面所示。

新增或者对已有记录进行编辑,可以进入界面如下所示。

9)新闻资讯

在个人信息页面中,里面有一个资讯中心,展示一些新闻资讯,如下所示。

单击可以或查看更多的列表,可以查看详细的新闻咨询信息,详细信息页面如下所示。

10)系统用户维护

系统管理员用户可以登录后,进行系统用户的维护,包括对用户进行查询,以及用户密码重置、用户过期/用户恢复的设置处理。

在管理工具入口,单击系统用户即可进行用户的搜索处理。

滑动用户列表,可以对用户进行密码重置、设置过期、用户恢复操作。

或者单击用户记录,可以对指定用户进行相关的
用户密码重置、用户过期/用户恢复
的设置处理。


11)组织机构管理

在管理面板中找到【组织机构】入口,如下图所示。

页面分层列出整个公司部门的组织机构,示例组织机构如下所示,实际根据自己的系统进行创建。

单击特定的机构节点,可以展开详细的机构信息,如下界面所示,管理员可以删除机构处理。

另外在底部有【新增机构】功能,单击可以进行创建机构信息。

在弹出的界面中,单击【父级机构】,可以展示当前机构的节点,选择机构作为父级,然后录入其他信息即可。

12)角色管理

角色管理包括角色查看、角色创建、以及分配角色用户几个功能。

在管理面板中找到【角色管理】入口,如下图所示。

角色是以公司进行划分的,因此查看角色需要选择特定的公司节点,如下所示。

单击公司节点,可以查看任一公司的角色列表,如下界面所示。

单击角色节点,可以查看角色的详细信息,或者删除角色,如下所示。

也可以在底部【新增角色】,弹出如下界面,录入提交即可创建新角色。

或者在角色包含的用户列表中,可以选择【移除角色用户】,或者【添加新用户】到角色中。

选择【添加】按钮,会弹出新的选择用户列表界面,选择加入用户即可完成角色用户的添加。

13)业务数据管理

不同的业务系统,我们需要创建一些不同的业务表单数据进行录入、查询等操作。

业务数据,可以在管理列表中根据关键字进行查询,列表界面下所示。

以上就是一个移动端的业务应用的系统界面,主要的目的是能够快捷的收集或者处理常规的业务数据,也便于后台管理系统的数据维护。

系列文章:


基于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的移动前端的功能介绍

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

1、前端精确条件的查询处理

在基于Vue3+Typescript+ElementPlus的前端界面中,查询是很多界面需要拥有的功能,如下所示。

展开后的全部查询条件

以上的查询部分是一个查询函数的处理,如下代码所示。

//查询列表处理
async functionsearch() {
pageInfo.pageIndex
= 1; //重置为第一页 //默认使用当前用户公司 const userInfo = $u.util.storageSession.getItem('user_info');
searchForm.company_ID
= userInfo?.company_ID; //所属公司 await getlist(); //获取列表 }//列表数据获取 async functiongetlist() {
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 = awaitquery.CountAsync();

query
=ApplySorting(query, input);
query
=ApplyPaging(query, input);var list = awaitquery.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)****//此处省略更多其他条件 ;returnquery;
}

通过逻辑和具体对象的实现分离,从而构建了很多通用的基类函数,这些函数只需要在子类重写一些规则即可实现更加详细的处理,也称为模板方法的设计模式,这种方式广泛应用于基类函数的抽象处理。

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
=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 = 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(PagedSortedAndFilteredInputDtoinput)
{
var query =CreateFilteredQueryAsync(input.Filter);var totalCount = awaitquery.CountAsync();//排序处理 if (!input.Sorting.IsNullOrWhiteSpace())
{
query
=query.OrderBy(input.Sorting);
}
else{
query
=ApplyDefaultSorting(query);
}
//分页处理 query =query.Skip(input.SkipCount).Take(input.MaxResultCount);var list = awaitquery.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(stringfilter)
{
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)
);
returnquery;
}

有了这些条件的定义,我们就可以在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的移动端实现多条件查询的处理