wenmo8 发布的文章

在我们很多模块里面,都需要使用到一些诸如图片、Excel文件、PDF文件等附件的管理,一般我们倾向于把它独立为一个公用的附件管理模块,这样可以有效的统一管理附件的信息。本篇随笔介绍附件内容的管理,包括可以对图片进行预览,对其他文件实现信息的查看和下载等操作,以及后端提供对应的附件上传等处理。

1、附件管理的界面

和其他模块一样,我们可以对附件记录表里面的信息进行管理,一般情况下,我们管理的附件都是诸如图片、Excel文件、PDF文件等附件的管理。

附件表是一个综合管理这些文件的记录表,虽然附件一般是独立上传到服务器端的文件系统里面,不过也需要记录这些文件的名称、类别名称、大小、后缀名、创建时间、创建人等信息。

数据库设计表如下所示。

记录明细大概如下所示。

为了管理好这些文件信息,我们在界面提供一些条件供查询,如下是管理界面。

为了快速的进行检索,我们提供了两个树形列表进行查询,可以按照文件类型,以及按照类别名称查询,类别是我们在上传的时候指定的一个附件的类别名称。

按文件类型分类如下所示

按类别名称分类如下所示。

而树形列表的信息展示,我们使用了自定义的树列表控件,非常方便,并极大减少了界面代码,界面代码如下所示。

<el-tabsvalue="treeType"type="border-card">
  <el-tab-panename="treeType"label="按文件类型">
    <myTree:data="treeType"icon-class="el-icon-price-tag"@nodeClick="nodeTypeClick" />
  </el-tab-pane>
  <el-tab-panename="treeCategory"label="按类别名称">
    <myTree:data="treeCategory"icon-class="el-icon-price-tag"@nodeClick="nodeCategoryClick" />
  </el-tab-pane>
</el-tabs>

而树形列表的类别名称,我们是从数据库中动态获取的,因此需要特殊的API封装调用。

在ABP框架的后端,应用服务类FileUploadAppService中定义一个获取类别的列表接口

        /// <summary>
        ///获取所有类别(Distinct)/// </summary>
        /// <returns></returns>
        public virtual async Task<List<string>>GetAllCategory()
{
var query = Repository.GetAll().Where(s=> s.Category != null).OrderBy(s =>s.Category);var list = query.Select(s =>s.Category).Distinct().ToList();return awaitTask.FromResult(list);
}

在客户端的API调用类中同时增加一个API处理接口,如下所示。

而Element的前端调用后端的ABP接口,前面很多博客也介绍的很多了,如下是它们的处理过程图示。

前端根据ABP后端的接口进行前端JS端的类的封装处理,引入了ES6类的概念实现业务基类接口的统一封装,简化代码。

大多数模块我们涉及到常规增删改查等业务接口,那么这些类继承BaseApi,就会具有相关的接口了,如下所示继承关系。

其中JS类的BaseApi具有常规的增删改查接口,如下所示。

在整合ABP后端接口的时候,我们为了方便,一般使用ES6的方式定义一个客户端的Api调用类,基础接口封装在BaseApi类里面,扩展自定义接口放在子类定义,因此前端API封装类fileupload.js的类关系如下所示。

我们再次回到管理界面,在列表中展示附件信息外,如果是图片提供预览,如果是文件则提供下载链接,方便处理。

或者

预览查看图片文件的时候,我们也需要在明细中列出附件的一些信息,如下界面所示。

以上就是附件管理的设计表,以及管理界面,其中前端主要使用了Vue + Element进行开发,后端还是用ABP的框架提供相关的API接口。

2、附件上传的处理

在之前随笔《
循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理
》中已经比较详细的介绍了对附件的上传处理,我们ABP后端提供了一些API接口给前端界面控件进行调用即可上传对应的附件

在附件上传处理的时候,我们就可以通过这样获得请求的文件对象了,如下代码所示。

我们上传到后端ABP应用服务器的文件,一般情况是不能访问目录的,如果需要特别放行,那么需要在ABP服务的Host应用里面,设置静态文件,允许前端访问我们的文件路径。

一般在Host项目的启动入口设置即可。

        public voidConfigure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
............

app.UseStaticFiles();
//指定特定的目录作为静态文件目录,如UploadFiles//是否可以访问静态文件 app.UseStaticFiles(newStaticFileOptions
{
FileProvider
= new PhysicalFileProvider(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "UploadFiles")),
RequestPath
= "/UploadFiles"});

.........

这样上传的文件,在对应目录里面,就可以通过URL地址访问了。

另外,前面我们看到的数据记录里面,没有绝对的URL地址,一般是为了适应性方便,不需要绝对的地址。

但是前端为了方便,服务器后端返回的接口中,我们一般增加一个绝对的地址信息供查看或者下载文件,那么我们可以在后端对应附件记录的转义函数里面增加一个对相对地址转换为绝对访问的URL地址的转换即可。

        /// <summary>
        ///对记录进行转义/// </summary>
        /// <param name="item">dto数据对象</param>
        /// <returns></returns>
        protected override voidConvertDto(FileUploadDto item)
{
//转义相对地址为绝对地址 item.FileUrl =GetFileUrl(item.BasePath, item.SavePath);
}
        /// <summary>
        ///根据记录的basePath和savePath,以及HttpContext上下文确定绝对路径/// </summary>
        /// <param name="basePath">附件的基础路径</param>
        /// <param name="savePath">附件的保存路径</param>
        /// <returns></returns>
        private string GetFileUrl(string basePath, stringsavePath)
{
var httpContext =_httpContext.HttpContext;string serverRealPath =basePath.UriCombine(savePath);if (!Path.IsPathRooted(basePath) && !basePath.StartsWith("http://") && !basePath.StartsWith("https://"))
{
//如果是相对目录,加上当前程序的目录才能定位文件地址 var url = string.Format("{0}://{1}", httpContext.Request.Scheme, httpContext.Request.Host.Value);
serverRealPath
= url.UriCombine(serverRealPath).Replace('\\', '/');
}
returnserverRealPath;
}

而前端界面中,一般的图片和附件上传界面如下所示。

编辑界面下,附件上传界面,可以加载已有的记录展示,如下所示。

用图片列表控件的方式展示图片信息,如下所示。

如果我们用Element的上传组件,大概的界面代码如下所示,主要设置好上传的API地址,以及给它提供好对应的授权头部信息即可。

前端界面的代码如下所示。

    <el-form-itemlabel="封面图片">
      <el-uploadref="upload"action="/abp/services/app/FileUpload/PostUpload"list-type="picture-card":on-preview="handlePictureCardPreview":on-remove="handleRemove":on-success="onSuccess":on-error="onError"accept="image/jpeg,image/gif,image/png,image/bmp":headers="myHeaders":file-list="editForm.fileList"
      >
        <iclass="el-icon-plus" />
      </el-upload>
      <el-dialog:visible.sync="dialogVisible">
        <imgwidth="100%":src="dialogImageUrl"alt="">
      </el-dialog>
    </el-form-item> 

其中授权头部信息,是ABP约定的授权请求头部信息,如下属性设置

  myHeaders: { Authorization: 'Bearer ' + getToken() }, //用于上传文件的身份认证

这样就可以整合文件上传的管理操作了,而前端就只需要针对附件信息,做统一的管理即可。

如下是统一的附件管理界面入口。

为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:

循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作

循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用

循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理

循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理

循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理


循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用

循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数

循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用


循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理

循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示

循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用


循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理


循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理

循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示

循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理

循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理

循序渐进VUE+Element 前端应用开发(17)--- 菜单管理

循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

VUE+Element 前端应用开发框架功能介绍

循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合

使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面

循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码

循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用

循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中

循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理

循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理

循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)

循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

电商商品数据库的设计和功能界面的处理

循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

循序渐进VUE+Element 前端应用开发(28)--- 附件内容的管理

循序渐进VUE+Element 前端应用开发(29)--- 高级查询条件的界面设计

部署基于.netcore5.0的ABP框架后台Api服务端,以及使用Nginx部署Vue+Element前端应用

循序渐进VUE+Element 前端应用开发(30)--- ABP后端和Vue+Element前端结合的分页排序处理

循序渐进VUE+Element 前端应用开发(31)--- 系统的日志管理,包括登录日志、接口访问日志、实体变化历史日志

循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理

循序渐进VUE+Element 前端应用开发(33)--- 邮件参数配置和模板邮件发送处理

使用代码生成工具快速开发ABP框架项目

使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理

使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题

在系统模块中的业务列表展示里面,一般我们都会在列表中放置一些查询条件,如果是表字段不多,大多数情况下,放置的条件有十个八个就可以了,如果是字段很多,而这些条件信息也很关键的时候,就可能放置很多条件,但是界面空间比较有限,而常规的查询一般就那么几个常用条件,如果每次都占用很多版面,好像不是很合理和友好。本篇随笔探讨Vue+Element前端界面中处理高级查询模块的界面设计,提供了两种处理的思路供参考借鉴。

1、弹出框的高级查询条件的界面设计

如常规的列表界面如下所示。

单击【高级查询】弹出一个新对话框窗口,里面可以查看到所有的查询条件

我们来一段动画效果,操作界面的动态效果如下所示。

这里我为了降低单页面的代码量,把高级查询模块的代码抽取到一个独立的文件中,然后在主体页面中引入使用。

    <testproduct-advanceref="advancesearch"@search="advanceSearch"
    />

我们在弹出高级查询对话框中,执行查询的时候,会获得条件对象,然后通过事件的方式给调用页面

    async handleSearch() { //表单提交
      this.isVisible = false;this.$emit('search', this.searchForm)
},

在主页面里面,会对高级查询的几个事件进行处理,如打开窗口,确定高级查询后触发查询。

methods: {
advanceSearch(searchObj) {
//高级查询 //console.log(searchObj) this.advanceSearchForm =searchObj;this.msgSuccess('已选择高级查询条件进行查询了')this.isAdvanceSeach = false //关闭高级查询窗口 this.getlist()
},
onResetAdvance(searchObj) {
//重置高级查询条件 //console.log(searchObj) this.advanceSearchForm =searchObj;
},
showAdvanceForm() {
//显示自定义的高级查询对话框 this.$refs.advancesearch.show()
},

在getlist函数里面,需要对高级查询对象进行转换处理

    getlist() { //列表数据获取
      //如果高级查询对象非空,则采用高级查询条件
       var form = this.isEmpty(this.advanceSearchForm) ? this.searchForm : this.advanceSearchForm;var param = { //构造常规的分页查询条件
        //分页条件
        SkipCount: (this.pageinfo.pageindex - 1) * this.pageinfo.pagesize,
MaxResultCount:
this.pageinfo.pagesize,//查询过滤条件 ProductNo: form.productNo,
BarCode: form.barCode,
MaterialCode: form.materialCode,
ProductType: form.productType,
ProductName: form.productName,
Status: form.status
}

.............

不过,由于高级查询和普通的查询界面代码有很多重复的地方,因此这样做感觉也相对比较啰嗦。

下面的思路就折叠的方案进行讨论。

2、折叠式的高级查询条件的界面设计

这个思路来自于AntDesign的查询条件展示,默认它是基于常规条件的展示,如果展开则展示更多的条件。

单击【展开】则展开更多的条件,以供查询。

借鉴了这个方式,我们也可以使用这样的折叠方式来隐藏更多的查询条件,从而也使得不常用的条件默认隐藏起来,提高界面的友好性。

这样折叠的条件和不折叠的条件在一个表单里面,只是通过一个状态的切换隐藏部分条件而已,虽然界面代码多增加一些,不过处理却变得简单一些,不需要单独编写一个高级查询的条件组件页面。

界面代码大致布局如下所示。

这样我们通过一个条件按钮来切换它的状态即可实现常用条件、高级查询条件的切换显示了。

<el-button:icon="expandMore ?'el-icon-arrow-up':'el-icon-arrow-down'"type="text"@click="expandMore =!expandMore">{{ expandMore ? '收起':'展开' }}</el-button>

这样查询处理,并不需要变化什么特殊的操作了,和常规操作一样,只不过多一些条件而已。

    getlist() { //列表数据获取
      var param = { //构造常规的分页查询条件
        //分页条件
        SkipCount: (this.pageinfo.pageindex - 1) * this.pageinfo.pagesize,
MaxResultCount:
this.pageinfo.pagesize,//查询过滤条件 Name: this.searchForm.name,
Sex:
this.searchForm.sex,
.............

State:
this.searchForm.state
};
//使用日期范围选择控件,在查询对象增加开始日期CreationTimeStart、结束日期CreationTimeEnd this.addDateRange(param, this.searchForm.creationTime)//获取列表,绑定到模型上,并修改分页数量 this.listLoading = truetestUser.GetAll(param).then(data=>{this.list =data.result.itemsthis.pageinfo.total =data.result.totalCountthis.listLoading = false})
},

以上就是两种不同高级查询条件的界面设计,一般来说,我倾向于使用后者来实现,这样界面效果也比较完整统一。

为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:

循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作

循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用

循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理

循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理

循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理


循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用

循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数

循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用


循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理

循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示

循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用


循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理


循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理

循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示

循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理

循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理

循序渐进VUE+Element 前端应用开发(17)--- 菜单管理

循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

VUE+Element 前端应用开发框架功能介绍

循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合

使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面

循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码

循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用

循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中

循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理

循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理

循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)

循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

电商商品数据库的设计和功能界面的处理

循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

循序渐进VUE+Element 前端应用开发(28)--- 附件内容的管理

循序渐进VUE+Element 前端应用开发(29)--- 高级查询条件的界面设计

部署基于.netcore5.0的ABP框架后台Api服务端,以及使用Nginx部署Vue+Element前端应用

循序渐进VUE+Element 前端应用开发(30)--- ABP后端和Vue+Element前端结合的分页排序处理

循序渐进VUE+Element 前端应用开发(31)--- 系统的日志管理,包括登录日志、接口访问日志、实体变化历史日志

循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理

循序渐进VUE+Element 前端应用开发(33)--- 邮件参数配置和模板邮件发送处理

使用代码生成工具快速开发ABP框架项目

使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理

使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题

ABP默认的数据库是SQLServer,不过ABP框架底层是EF框架,因此也是很容易支持其他类型的数据库的,本篇随笔介绍在ABP框架使用Mysql数据库,以及基于SQLServer创建MySql数据库的架构和数据的处理。

如果需要其他方式数据库的支持,那么需要引入相应的支持程序集。Nuget中安装MySql相关包,Oracle公司提供的MySql 包试了很多次,都还是用起来有问题,所以现在都是用的第三方的MySql包,这个包也是ABP官方文档中推荐使用的。

pomelo
.entityframeworkcore
.mysql

pomelo
.entityframeworkcore
.mysql
.design

目前使用的是基于正式版的.NETCore5.0,那么Microsoft.EntityFrameworkCore.Relational对应版本是5.0.0。因此,对应这些版本的驱动如下图所示。

在EntityFrameworkCore层中找到XXDbContextConfigurer,修改DbContext中数据库配置,默认使用的SqlServer,修改为mysql。

namespaceMyProject.EntityFrameworkCore
{
/// <summary> ///配置使用特定数据库的处理/// </summary> public static classMyProjectDbContextConfigurer
{
public static void Configure(DbContextOptionsBuilder<MyProjectDbContext> builder, stringconnectionString)
{
//builder.UseSqlServer(connectionString);//默认SQlServer builder.UseMySql(connectionString, new MySqlServerVersion(new Version(5, 7)));//MySQL//builder.UseNpgsql(connectionString);//PostgreSQL//builder.UseOracle(connectionString);//Oracle }public static void Configure(DbContextOptionsBuilder<MyProjectDbContext>builder, DbConnection connection)
{
//builder.UseSqlServer(connection);//默认SQlServer builder.UseMySql(connection, new MySqlServerVersion(new Version(5, 7)));//MySQL//builder.UseNpgsql(connection);//PostgreSQL//builder.UseOracle(connection);//Oracle }
}
}

其中UseMySql需要用到第二个版本参数,我Mysql用的是Mysql5.7,因此使用代码构建版本参数。

new MySqlServerVersion(new Version(5, 7)

我们在Host启动项目里面的appsettings.json里面定义了不同数据库的连接信息,如下所示。

{"ConnectionStrings": {"Default": "Server=.\\SQL2014; Database=MyProjectDb; Trusted_Connection=True;","Oracle": "User Id=orcl;Password=orcl;Data Source=orcl;Persist Security Info=True;","MySql": "Server=localhost;Database=myprojectdb;Uid=root;Pwd=123456;","PostgreSQL": "Server=localhost;Port=5432;Database=myprojectdb;User Id=postgres;Password=123456"},"RedisCache": {"ConnectionString": "localhost","DatabaseId": "-1"},

...............

而我们系统不同的数据库连接就就是来自ConnectionStrings里面的键值对象,我们ABP框架里面,使用引用常数来配置对应的数据库连接。

    public class MyProjectDbContextFactory : IDesignTimeDbContextFactory<MyProjectDbContext>{public MyProjectDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<MyProjectDbContext>();//获取配置文件信息 var configuration =AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());//通过连接字符串切换对应的数据库,ConnectionStringName默认为Default MyProjectDbContextConfigurer.Configure(builder, configuration.GetConnectionString(Constants.ConnectionStringName));return newMyProjectDbContext(builder.Options);
}
}

那么我们修改其中对应的常数定义,切换为我们所需要的Mysql数据库即可,如下所示。

至此,代码上修改就完成了,那么我们现有运行的SQLServer数据库,如何迁移到Mysql环境中去呢?

2、基于SQLServer创建Mysql数据库的架构和数据

上面小节介绍了切换到其他数据库Mysql数据库的时候,代码上的调整修改,但是我们还需要把已有SQLServer上运行的数据库迁移到Mysql上去,如何处理呢。

有些人希望通过ABP带有的Migrator项目进行项目数据库的重构,不过我不建议使用这个,因为我们实际开发的时候,变动了很多数据库,而变动了再映射回到代码里面,比较麻烦,不如在数据库基础上进行迁移来的快捷完整。

我这里通过工具的方式,把SQLServer数据库导入到Mysql数据库里面,然后在导出脚本进行一定的调整就可以完成。

我这里说到的工具就是Navicat Premium,由于我的数据库用的是SQLServer2014,因此建议使用Navicat Premium15或更高版本来处理数据库的迁移,我测试过Navicat Premium·11是不行的。

在Navicat Premium工具上连接好Mysql数据库和SQLServer数据库,然后在Mysql里面创建一个同名的数据库,以Utf-8编码创建数据库即可,如下所示。

然后在工具上选择数据库传输

在弹出的界面中选择源数据库和目标数据库,如下所示。

然后一步步处理即可完成数据库结构和数据的导入,这样Mysql数据库里面就有对应的数据库结构和数据了。

不过完成这部还是不够十分完美,因为从SQLServer钟导入到Mysql数据库里面,数据库的自增长列全部被取消了,如果我们在管理界面里面修改,则无法修改带有约束关系的主键为自增长。

因此我们需要再次调整一下,也就是把Mysql数据库导出为SQL脚本,然后在脚本上进行一定的调整即可。

用Notepad++打开导出的数据库脚本,对自增长的表字段进行添加一个关键字AUTO_INCREMENT,标注为自增长即可,如下所示。

然后删除已有的Mysql数据库,然后重新创建对应名称的数据库,重新导入Mysql脚本进行运行生成新的表和数据即可完成。

运行ABP后台项目,启动Swagger界面,如下所示。

然后启动Vue+Element的客户端进行测试Mysql数据库的运行情况,前端正常。

以上就是介绍ABP框架使用Mysql数据库的代码调整,以及基于SQLServer创建Mysql数据库的架构和数据的操作过程

前面介绍了很多关于ABP框架的后台Web API 服务端,以及基于Vue+Element前端应用,本篇针对两者的联合部署,以及对部署中遇到的问题进行处理。ABP框架的后端是基于.net core5.0 的Asp.net core 应用,因此和常规的Asp.net core 应用部署一样;而Vue+Element前端应用则是基于nodejs的应用,部署方式又有所不同,这里介绍基于Nginx的部署。

1、部署基于.netcore5.0的ABP框架后台Api服务端

1)安装.net core 环境

在部署asp.net core服务前,需要在服务器中安装必须的环境。

由于当前ABP的Web API是基于.net core 5的,因此,我们打开.net core 5的页面:
https://dotnet.microsoft.com/download/dotnet/5.0

在这里选择下载Hosting Bundle。因为Hosting Bundle包括了.Net Core运行时和IIS支持。下载完成以后,双击exe文件即可进行安装即可。

安装完成以后我们在命令行里面输入下面的命令,检查是否安装成功:

dotnet --info

如下图所示:

可以看到提示我们已经安装了.NET Core runtimes环境等所需软件。

由于我们需要部署到IIS上面,所以安装完需使用下面的命令行重启IIS服务:

net stop was /y
net start w3svc

如下图所示:

也可以在管理服务器里面重启启动IIS。

2) 发布.net core项目

服务器.net core环境弄好后,下一步就是准备好发布包,我们在ABP框架的Host项目进行发布。

发布选择文件发布,如下所示。

然后调整设置如下所示。

最后我们生成发布包,在对应的目录下,如下所示。

G:\***\Web.Host\bin\Release\net5.0\publish\

3)在服务器中设置IIS

把文件上传到服务器上,然后就是准备设置好IIS了。

先在IIS服务器上创建一个网站,指定对应目录和端口等信息,如下所示。

然后找到对应的应用程序池,找到刚才创建的ABP应用程序池。设置.net clr版本为【
无托管代码

在其中把标识由
ApplicationPoolIdentity
修改为
LocalSystem
,以提供应用权限可以访问数据库。

最后点击【确定】按钮,网站及部署完成。我们就可以在浏览器里面进行浏览了。

顺利弄完asp.net core的后端API服务,那么下面就需要同时把Vue+Element的前端部署在服务端了。

2、使用Nginx部署Vue+Element前端应用

部署Vue+Element的前端应用,建议使用Nginx服务,这个对于Vue里面的URL代理转向设置比较方便些。

Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器。

首先到nginx服务网站下载对应的程序包进行安装:
http://nginx.org/en/download.html
,  建议下载稳定版本进行安装。

nginx的DOS操作命令有几个,比较简单

startnginx    启动

nginx -s reload 刷新配置文件

tasklist
/fi "imagename eq nginx.exe"查看所有的nginx进程

taskkill
/fi "imagename eq nginx.exe" /f 停止所有nginx进程

定位到解压的目录,然后在DOS窗口中执行  start nginx  启动nginx服务。

在使用前,我们需要检查nginx是否启动成功,直接在浏览器地址栏输入网址 http://localhost:80,回车,出现以下页面说明启动成功。

也可以在cmd命令窗口输入命令 tasklist /fi "imagename eq nginx.exe" ,出现如下结果说明启动成功

nginx的配置文件是conf目录下的nginx.conf,默认配置的nginx监听的端口为80,如果80端口被占用可以修改为未被占用的端口即可。

在处理网站的URL代理设置前,我们先回到我们Vue+Element 项目里面,我们在vue.config.js里面一般都有为跨域处理实现的代理设置,如下图所示。

而发布应用到服务器的时候,我们需要配置它的反向代理设置。

使用Nginx部署Vue+Element前端应用的时候,我们可以利用它的反向代理设置配置即可。

在nginx下的conf\nginx.conf中修改nginx的配置文件,配置修改。

根据我在Vue前端项目上的devServer的配置,我们在nginx的反向代理设置如下所示。

完整设置信息如下所示:

server {
listen
8000;
server_name localhost;

#charset koi8
-r;
#access_log logs
/host.access.log main; location/{
root html
/dist; index index.html index.htm;
try_files $uri $uri
/ /index.html =404; }
location
/baseabp { proxy_set_header Host $host:21021; #图片等资源需带端口获取
proxy_set_header x
-forwarded-for$remote_addr;
proxy_set_header X
-Real-IP $remote_addr;
proxy_pass http:
//localhost:21021; }
location
/abp { proxy_set_header Host $host:21021; #图片等资源需带端口获取
proxy_set_header x
-forwarded-for$remote_addr;
proxy_set_header X
-Real-IP $remote_addr;
proxy_pass http:
//localhost:21021/api; }

以上设置处理后,前端使用到Web API端的文件,反向代理也会带上对应的端口号,实现图片等上传API目录下的资源的正常访问了。

在很多列表展示数据的场合中,大多数都会需要一个排序的处理,以方便快速查找排序所需的数据,本篇随笔介绍如何结合ABP后端和Vue+Element前端结合的分页排序处理过程。

1、Vue+Element前端的排序代码

我们首先参考一下Element界面组件的排序说明。如下效果是Element表格组件的排序效果。

在列中设置
sortable
属性即可实现以该列为基准的排序,接受一个
Boolean
,默认为
false
。可以通过 Table 的
default-sort
属性设置默认的排序列和排序顺序。可以使用
sort-method
或者
sort-by
使用自定义的排序规则。
如果需要后端排序,需将
sortable
设置为
custom

,同时在 Table 上监听
sort-change
事件,在事件回调中可以获取当前排序的字段名和排序顺序,从而向接口请求排序后的表格数据。在本例中,我们还使用了
formatter
属性,它用于格式化指定列的值,接受一个
Function
,会传入两个参数:
row

column
,可以根据自己的需求进行处理。

根据上面说明,我们这里采取的是后端排序,将
sortable
设置为
custom。如下案例代码所示。

    <!--表格列表信息-->
    <el-tablev-loading="listLoading":data="list"border
fit
stripe
highlight-current-row
:header-cell-style
="{background:'#eef1f6',color:'#606266'}"@selection-change="selectionChange"@row-dblclick="rowDbclick"@sort-change="sortChange" > <el-table-columntype="selection"width="40" /> <el-table-columnalign="center"sortable="custom"prop="name"label="姓名"> <templateslot-scope="scope">{{ scope.row.name }}</template> </el-table-column> <el-table-columnalign="center"sortable="custom"prop="age"label="年龄"> <templateslot-scope="scope">{{ scope.row.age }} 岁</template> </el-table-column> <!--日期字段信息,显示完整时间格式:formatter="timeFormat",否则使用:formatter="dateFormat"--> <el-table-columnalign="center"label="创建时间"sortable="custom"width="160"prop="creationTime":formatter="timeFormat" />

以上是界面端的HTML代码,那么前端JS的处理逻辑也需要相应处理下,上面代码中 @sort-change="sortChange" 定义了排序变化的触发事件处理。

methods: {
sortChange({ column, prop, order }) {
var direction = (order === 'ascending') ? ' ASC' : ' DESC' this.sorting = !this.isEmpty(prop) ? (prop + direction) : null this.getlist()
},

以上代码主要逻辑是构建一个排序字符串,如:字段 ASC 或 字段 DESC的字符串,以供ABP后端的排序处理即可,这个主要是根据ABP框架排序特点进行提交的。

我们通过getlist的方法提交对应的查询条件和排序字符串,让后端再次获取数据给我们前端处理。

    getlist() { //列表数据获取
      var param = { //构造常规的分页查询条件
        //分页条件
        SkipCount: (this.pageinfo.pageindex - 1) * this.pageinfo.pagesize,
MaxResultCount:
this.pageinfo.pagesize,
Sorting:
this.sorting,//查询过滤条件 Name: this.searchForm.name,
AgeStart:
this.searchForm.ageStart
};
//使用日期范围选择控件,在查询对象增加开始日期CreationTimeStart、结束日期CreationTimeEnd this.addDateRange(param, this.searchForm.creationTime)//获取列表,绑定到模型上,并修改分页数量 this.listLoading = truecustomer.GetAll(param).then(data=>{this.list =data.result.itemsthis.pageinfo.total =data.result.totalCountthis.listLoading = false})
},

那么剩下的时候,就是后端如何处理数据的问题了。

2、 ABP后端分页排序的处理

我们查看ABP的应用服务层的接口基类可以看到,对应的获取所有记录GetAll方法的定义如下所示。

        public virtual async Task<PagedResultDto<TEntityDto>>GetAllAsync(TGetAllInput input)
{
CheckGetAllPermission();
//检查权限 var query = CreateFilteredQuery(input); //构建查询条件 var totalCount = await AsyncQueryableExecuter.CountAsync(query); //获取记录总数 query= ApplySorting(query, input); //排序 query = ApplyPaging(query, input); //分页 var entities = await AsyncQueryableExecuter.ToListAsync(query); //返回实体记录 return new PagedResultDto<TEntityDto>( //构建分页展示数据 totalCount,
entities.Select(MapToEntityDto).ToList() //实体转换DTO
);
}

一般情况下,这些逻辑我们一般不会变化,我们只需要进行一定的重写构建查询条件,以及排序规则即可。

例如重写条件查询获取查询对象的处理如下。

详细代码如下所示:

        /// <summary>
        ///自定义条件处理/// </summary>
        /// <param name="input">查询条件Dto</param>
        /// <returns></returns>
        protected override IQueryable<Customer>CreateFilteredQuery(CustomerPagedDto input)
{
return base.CreateFilteredQuery(input)
.WhereIf(
!input.ExcludeId.IsNullOrWhiteSpace(), t=>t.Id != input.ExcludeId) //不包含排除ID .WhereIf(!input.Name.IsNullOrWhiteSpace(), t => t.Name.Contains(input.Name)) //如需要精确匹配则用Equals//年龄区间查询 .WhereIf(input.AgeStart.HasValue, s => s.Age >=input.AgeStart.Value)
.WhereIf(input.AgeEnd.HasValue, s
=> s.Age <=input.AgeEnd.Value)//创建日期区间查询 .WhereIf(input.CreationTimeStart.HasValue, s => s.CreationTime >=input.CreationTimeStart.Value)
.WhereIf(input.CreationTimeEnd.HasValue, s
=> s.CreationTime <=input.CreationTimeEnd.Value)
;
}

排序的处理,我们可以重写下规则如下所示。

        /// <summary>
        ///自定义排序处理/// </summary>
        /// <param name="query">可查询LINQ</param>
        /// <param name="input">查询条件Dto</param>
        /// <returns></returns>
        protected override IQueryable<Customer> ApplySorting(IQueryable<Customer>query, CustomerPagedDto input)
{
if (!string.IsNullOrEmpty(input.Sorting))//排序字段是否有值 {
query
=query.OrderBy(input.Sorting);
}
else{
query
= query.OrderByDescending(t =>t.CreationTime);
}
returnquery; }

完成这些,我们就可以测试前端的界面效果了。

为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:

循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作

循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用

循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理

循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理

循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理


循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用

循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数

循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用


循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理

循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示

循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用


循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理


循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理

循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示

循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理

循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理

循序渐进VUE+Element 前端应用开发(17)--- 菜单管理

循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制

VUE+Element 前端应用开发框架功能介绍

循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合

使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面

循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码

循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用

循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中

循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理

循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理

循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)

循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

电商商品数据库的设计和功能界面的处理

循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

循序渐进VUE+Element 前端应用开发(28)--- 附件内容的管理

循序渐进VUE+Element 前端应用开发(29)--- 高级查询条件的界面设计

部署基于.netcore5.0的ABP框架后台Api服务端,以及使用Nginx部署Vue+Element前端应用

循序渐进VUE+Element 前端应用开发(30)--- ABP后端和Vue+Element前端结合的分页排序处理

循序渐进VUE+Element 前端应用开发(31)--- 系统的日志管理,包括登录日志、接口访问日志、实体变化历史日志

循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理

循序渐进VUE+Element 前端应用开发(33)--- 邮件参数配置和模板邮件发送处理

使用代码生成工具快速开发ABP框架项目

使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理

使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题