2023年2月

VUE+Element 前端应用,比较不错的一点就是界面组件化,我们可以根据重用的指导方针,把界面内容拆分为各个不同的组合,每一个模块可以是一个组件,也可以是多个组件的综合体,而且这一个过程非常方便。组件封装的目的就是为了能够更加便捷、快速的进行业务功能的开发。组件(component)是vue的最强大功能之一,组件可以实现一些类似功能的复用及与其它业务逻辑的解耦。本篇随笔介绍前端框架中常用到的一些组件封装处理,用于简化界面、隐藏实现细节的目的。

1、组件封装的需要

在我们开发一个主要界面的时候,一般需要排列很多内容,各个部分的内容可能会在其他界面上比较常见,那么这些如果是简单的使用原始的Element界面组件,以及简单的复制处理的话,页面代码比较臃肿,而且随着不断的复制过去使用,各部分的界面又不在统一了。这个时候如果把那些相同的功能,抽象出来抽离成组件,通过组件引用方式就会显得格外省事了。

在Winform开发中,我们知道有很多常见的用户自定义控件,就是对常规界面内容的一些通用部分进行封装,拖动过来即可使用。现在VUE+Element 前端应用的组件开发也很容易,把界面代码和Vue的控制逻辑JS代码组合一起形成一个小组件,通过使用Prop的方式传入所需参数,由组件内部控制界面的展示逻辑及事件处理,那么界面调用组件的时候就非常简洁,这样我们主页面上的代码量就降低不少,也方便代码的维护。

例如对于常规的字典下拉列表,我们希望绑定一个字典类型就可以实现系统的下拉列表的显示,那么我们可以封装这个常见的界面内容为一个字典组件。

还有对于树列表及其过滤实现等常规的处理,我们也可以通过简单的封装,实现树列表的展示,这样我们传入对应的树列表数据即可呈现所需要的树状列表内容。

另外,还有一种情况,是界面内容太多,我们把它按内容划分为不同的界面组件,然后页面进行独立的维护,主界面直接一行代码即可集成所需的内容板块,极大减少主界面的代码铺陈。

例如:对于权限系统中的每个角色,除了包含基本信息外,还会包含拥有的权限(功能控制点)、包含用户,以及拥有的菜单,其中权限是用来控制界面元素,如操作按钮的显示的,而拥有的菜单,则是用户以指定账号登录系统后,获得对应角色的菜单,然后构建对应的访问入口的。角色界面模块UML类图如下所示。

那么对应界面元素上,我们就应该以不同的Tab来展示这些信息,每个Tab内容部分就可以作为一个独立的界面组件来开发。

在比如,组织机构里面添加成员和添加角色的操作UML图。

其中添加成员、添加角色,涉及界面的列表数据展示以及对应的添加展示操作,独立一个界面组件还是比较方便的,而且添加成员 ,在角色维护模块里面也需要用到,那么可以兼容两个场景来设计组件模块。

2、界面组件的封装处理

在VUE+Element 的前端场景中,想要封装好一个组件,一般要熟练掌握的三个技能:1、父组件传值到子组件(props) 2、子组件传值到父组件($emit)3、插槽使用(slot)。对于一个独立的组件,props是用来为组件内部注入核心内容;$emit用来使这个组件通过一些操作来融入其它组件中。

我们来详细分析下前面介绍的组件场景。

对于字典模块来说,一般如果需要实现和系统字典模块里面的Api对接,并展示对应字典类型的下拉列表,实现代码如下所示。

<el-selectv-model="searchForm.ProductType"filterable clearable placeholder="请选择">
  <el-optionv-for="(item, key) in typeList":key="key":label="item.value":value="item.key"
  />
</el-select>

然后在JS逻辑里面使用字典的API获取对应数据,然后绑定到typeList属性上面即可。这样的逻辑如果每个用到字典的地方都需要这样处理,代码显然比较臃肿,而且使用组件封装更方便。

我们在Componen目录下创建一个Common目录,然后在其中添加一个my-dictdata.vue文件,用来封装字典内容处理的。

如果我们完成组件的编写,那么引用组件只需要一行代码即可实现相同的功能了。

<my-dictdatav-model="searchForm.ProductType"type-name="商品类型" />

以及在页面的组件里面引入这个自定义组件即可。

import myDictdata from '@/components/Common/my-dictdata'exportdefault{
components: { myDictdata },

如果为了简便,也可以在main.js里面统一全局引入。

这样相对于使用原始的Element界面组件,既有界面代码,又有JS代码,这样的一行代码就实现功能,显得非常简洁。

为了了解其中的奥秘,我们对组件的开发过程进行简单的了解,其中组件界面部分的代码如下所示,和上面的差不多。

<template>
  <el-selectv-model="svalue"filterable clearable placeholder="请选择">
    <el-optionv-for="(item, index) in dictItems":key="index":label="item.Text":value="item.Value"
    >
      <spanstyle="float: left;color:yello;">
        <iclass="el-icon-tickets"style="color:blue;" />{{ item.Text }}</span>
    </el-option>
  </el-select>
</template>

然后在JS部分引入字典操作的Api类,以及定义Prop属性typeName,如下所示。

并对传入Model值进行监控

我们在Mounted的实现里面,增加对字典数据的请求绑定,如下所示。

对于树形列表

一般来说它的使用代码如下所示。

还是可以感觉比较臃肿,如果放到主页面里面,会占用很多代码行,维护起来也不方便,而且树列表也是一个常见的界面展示内容,可以通过简单的封装进行减少主页面的代码量。

同样我们可以通过封装一个树列表组件

我们可以在my-tree.vue里面定义很多常见的的Prop属性,以方便调用的时候传入对应的值来改变界面的呈现。

这样主界面调用组件来实现功能的时候,只需要一行界面代码即可。

<myTree:data="treedata"icon-class="el-icon-price-tag"@nodeClick="nodeClick" />

相应的处理获得treedata的操作,是下面的函数

    getTree() { //树列表数据获取
      var param ={
SkipCount:
0,
MaxResultCount:
1000,
Tag:
'web' //Web端专用 }
menu.GetAll(param).then(data
=>{this.treedata = [];//树列表清空 var list =data.result.itemsif(list) { //使用getJsonTree函数,实现对二维表转换为嵌套树对象集合 var newTreedata =getJsonTree(list, {
id:
'id',
pid:
'pid',
children:
'children',
label:
'name'}); this.treedata =newTreedata
}
});
},

而对于部分页面组件的方式,这种一般不是很通用的组件,一般我们可以把它放在同一个目录上的Components目录里面,如下是权限模块里面用到的一些界面内容组件封装。

完成添加成员、添加角色的界面组件后,我们就可以在组织机构界面里面引入使用。

角色管理,我们也依据这个来对模块的内容进行划分,不同业务设计不同的界面组件,最后整合一起使用即可。

<el-dialogtitle="查看信息":visible="isView":modal-append-to-body="false"@close="closeDialog">
  <el-col>
    <el-tabsvalue="basicPage"type="border-card">
      <el-tab-panename="basicPage"label="基本信息">
        <el-formref="viewForm":model="viewForm"label-width="120px">
          <el-form-itemlabel="角色名">
            <el-inputv-model="viewForm.name"disabled/>
          </el-form-item>
          <el-form-itemlabel="角色显示名">
            <el-inputv-model="viewForm.displayName"disabled/>
          </el-form-item>
          <el-form-itemlabel="角色描述">
            <el-inputv-model="viewForm.description"type="textarea"disabled/>
          </el-form-item>
        </el-form>
      </el-tab-pane>

      <el-tab-panename="permitPage"label="权限">
        <rolefunctionref="rolefunction":role-id="selectRoleId" />
      </el-tab-pane>

      <el-tab-panename="userPage"label="用户">
        <roleuserref="roleuser":role-id="selectRoleId":can-add="false":can-delete="false" />
      </el-tab-pane>

      <el-tab-panename="menuPage"label="菜单">
        <rolemenuref="rolemenu":role-id="selectRoleId" />
      </el-tab-pane>
    </el-tabs>
  </el-col>
  <divslot="footer"class="dialog-footer">
    <el-buttontype="success"@click="closeDialog">关闭</el-button>
  </div>
</el-dialog>

或者

以上就是我们封装一些常见通用组件,以及页面模块的组件,主要的目的就是可以简化主调用界面的代码,以及提高使用的效率。

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

循序渐进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变量方式解决弹出编辑对话框界面无法触发更新的问题

在一些场合,我们往往需要使用印章来给每页文档加盖一个印章,以表示该文档经过某个部门的认证的,常规的做法就是打印文档后盖章,如果需要电子档再行扫描一下。这样的处理,如果文档很多,且仅仅需要电子文档的就很麻烦了,需要打印再盖章再扫描,而且电子文档往往有失真的问题。本篇随笔介绍如何在在Winform程序中使用Spire.Pdf实现页面添加印章处理,解决这一痛处。

1、在PDF增加印章处理

首先我们来了解下,用Adobe Acrobat Reader 进行文档的印章处理下,看看如何实现的,后面再用软件实现自动的添加。

印章的来源,我们需要一个图片格式的印章。那么我们可以通过盖章到白纸上,然后拍照进行抠图,把图片弄成PNG的透明图片,也可以使用在线的印章生成工具生成图片,然后在进行适当的加工。

在做图章之前,我们先用Photoshop处理好图片,并保存图片为PDF格式的文件。

首先用Adobe Acrobat Reader打开一个PDF文档,然后找到对也给你的工具选项卡,选择图章功能,如下图所示。

然后会切换到文档的自定义图章功能上,如下所示。

然后选择创建自定义图章,导入刚才做好的PDF格式图片印章即可。

印章创建好后,就可以在文档中调用印章模板进行添加了,也就是调用印章调板处理。

拖动到文档的任意位置即可实现电子印章的添加了,添加后的文档界面效果如下所示。

2、在Winform程序中使用Spire.Pdf实现页面添加印章处理

以上是通过手工操作,在Adobe Acrobat Reader上添加印章,并给PDF文档指定位置添加印章的过程,这样的处理,已经可以减少电子档的扫描失真问题,并可以减少很多繁琐的工作。但是如果我们每次都要给很多文档页面添加这样的印章处理,也是很繁琐的事情,那么我们这里就可以编写一个程序,用程序是给每个页面指定位置盖章即可。

我们知道Spire.Pdf是对PDF操作非常不错的一个组件,我们可以利用它来实现我们对这些PDF创建印章和盖章的处理过程。

我们在Winform开发框架模块的综合案例里面,添加一个Demo页面,然后实现这个功能展示一下。

整个代码其实不复杂,如下代码所示。

        private void btnAddImageSign_Click(objectsender, EventArgs e)
{
string signPdf = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Examples/PdfDemo/TestSign2.pdf");string signImage = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Examples/PdfDemo/印章.png");

PdfDocument doc
= newPdfDocument();
doc.LoadFromFile(signPdf);
//每页都添加印章 foreach (PdfPageBase page indoc.Pages)
{
PdfRubberStampAnnotation loStamp
= new PdfRubberStampAnnotation(new RectangleF(new PointF(400, 600), new SizeF(120, 120)));
PdfAppearance loApprearance
= newPdfAppearance(loStamp);
PdfImage image
=PdfImage.FromFile(signImage);

PdfTemplate template
= new PdfTemplate(160, 160);
template.Graphics.DrawImage(image,
0, 0);
loApprearance.Normal
=template;
loStamp.Appearance
=loApprearance;

page.AnnotationsWidget.Add(loStamp);
}
//保存文档 var fileSave = FileDialogHelper.SavePdf("ImageStamp.pdf", System.AppDomain.CurrentDomain.BaseDirectory);if(!string.IsNullOrEmpty(fileSave))
{
doc.SaveToFile(fileSave);
Process.Start(fileSave);
}
}

最后页面的效果如下所示,在每页的指定位置,都盖章我们的印章了。

以上处理,就是在Winform程序中使用Spire.Pdf实现页面添加印章处理,可以减轻我们实际工作的繁琐操作。

在很多应用中,往往都涉及到记录用户所在省份、城市、区县或者街道等信息,一般我们可以通过联动的Select或者类似的界面组件进行展示,或者使用Element中的el-cascader界面组件进行展示,而全国的省份、城市、区县或者街道等信息我们可以通过官方的数据进行获取,可以直接存放在JS里面,也可以存储在自己的数据库里面,最后统一进行联动展示即可。本篇随笔介绍几种组件对省市区县联动处理的效果及做法,可以直接应用在我们项目中,也可以做为一个组件开发的参考学习。

1、基于element-china-area-data 第三方组件的使用

在github往往有很多我们需要的开源组件,我们可以拿来直接使用,如这个地址是:
https://github.com/Plortinus/element-china-area-data
,在Vue+Element的项目中,直接通过npm进行安装组件即可:

npm install element-china-area-data -S

然后在页面组件中引入对象数据,绑定在el-cascader界面组件即可。

import { provinceAndCityData, regionData, rovinceAndCityDataPlus, regionDataPlus,  CodeToText, TextToCode } from 'element-china-area-data';

例如界面代码如下所示。

    <divclass="three">
      <spanclass="imp">3. 三级联动(不带“全部”选项)</span>
      <el-cascaderv-model="selectedOptions2"class="long"size="large":options="regionData"@change="handleChange"
      />
    </div>
    <divclass="bind">
      <div>绑定值:{{ selectedOptions2 }}</div>
      <div>区域码转汉字:{{ CodeToText[selectedOptions2[0]] }},{{ CodeToText[selectedOptions2[1]] }},{{ CodeToText[selectedOptions2[2]] }}</div>
      <div>汉字转区域码:{{ convertTextToCode(CodeToText[selectedOptions2[0]], CodeToText[selectedOptions2[1]], CodeToText[selectedOptions2[2]]) }}</div>
    </div>

其中数据selectedOptions2格式如下所示

selectedOptions2: ['120000', '120100', '120101'],

而选中数据后,获得的数据格式同样是一个数组集合,如下所示

几种界面组件的效果如下所示。

省市区三级联动的案例Demo代码如下。

<template>
  <divid="app">
    <el-cascadersize="large":options="options"v-model="selectedOptions"@change="handleChange">
    </el-cascader>
  </div>
</template>

<script>import { regionDataPlus } from'element-china-area-data'exportdefault{
data () {
return{
options: regionDataPlus,
selectedOptions: []
}
},

methods: {
handleChange (value) {
console.log(value)
}
}
}
</script>

2、基于
v-region控件省市区街道选择组件

除了上面的 element-china-area-data 第三方组件,还有一个v-region的省市区街道的组件也做的不错,地址是:
https://github.com/TerryZ/v-region

它的主要特点是支持 “省/直辖市”、“市”、“区/县”、“乡/镇/街道” 4级行政区域选择,可以选择Select的方式,或者分组方式展示,功能比较多样化一些。

或者

这个v-region已经封装为组件进行使用,所以使用上更加简化一些,如下简单的界面声明即可使用。

  <h3>常规表单下拉选择元素模式</h3>
  <p>Regular form element with select tag</p>
  <v-region:town="true"></v-region>

街道可以包含,也可以省略,如果省略就是省市区县的三级选择了。这个组件的几种用法如下所示。

   <spanclass="imp">基于v-region控件省市区街道选择组件</span>
    <divclass="vregion">
      <h3>常规表单下拉选择元素模式</h3>
      <v-regionv-model="selectedRegion":town="true"@values="regionChange" />
      <br><br>

      <h3>多分组切换模式</h3>
      <v-regionv-model="modelGroup":town="true"type="group"@values="regionChange"
      />

      <br><br><br>
      <h3>多列竖排模式</h3>
      <p>Column group</p>
      <v-regiontype="column"@values="regionChange" />

      <br><br><br>
      <h3>城市选择器</h3>
      <p>City picker</p>
      <v-regiontype="city":city-picker="true"@values="regionChange" />
    </div>

这个组件如果是省市区县街道模式的话,需要设置初始化值就是一个对象的格式,如下所示。

selectedRegion: {
province:
'350000',
city:
'350100',
area:
'350104',
town:
'350104008'}

例如,我在一个业务表的案例界面中,就涉及到了省市区街道的选择处理,就是采用了这个v-region的组件进行展示处理的。界面效果如下所示。

如果是新建窗体的时候,我们可以指定组件的默认值,如下的数据格式

selectedRegion: { province: '440000', city: '440100', area: '440666666', town: '440666666010' },

另外,我们一般需要在数据库里面存储对应的省市区县的数据,以便查询或者其他需要,那么我们就需要在选择数据变化的时候,设置一下对应的属性字段,如下所示。

    //修改编辑窗体的省市区街道
changeEditRegion(data) {
console.log(data)
if(data) {this.editForm.province = data.province ? data.province.key : '';this.editForm.city = data.city ? data.city.key : '';this.editForm.district = data.area ? data.area.key : '';this.editForm.street = data.town ? data.town.key : '';//console.log(this.editForm) }
},

而在展示对话框的时候,我们则可以组合省市区数据,作为v-region组件的初始化值,如下代码使用。

3、自定义省市区的组件

一般情况上,使用上面的方式就能解决问题了,不过这里介绍另外一种思路,就是基于数据库数据的方式进行省市区联动的处理。

以前我在开发系统的时候,引入了省市区的数据,存储在几个数据库表里面,然后通过接口的方式检索省市区及处理器联动过程。

那么在基于这些数据的基础上,我们也可以这样处理的。

首先我们创建省、市、区县的表,并在后端发布对应的API接口,如我的ABP后端接口展示。

然后在根据这些接口,构建好对应的API客户端,再在界面引入使用,通过定义自定义组件的方式来整合使用则更加简单。

在定义一个自定义组件my-citypicker,组件代码如下所示。

<!--用来演示联动的案例-->
<template>
  <divclass="flex-container">
    <divclass="flex-item">
      <label></label>
      <el-selectv-model="provinceValue"placeholder="请选择省"@change="chooseProvince">
        <el-optionv-for="item in provinceData":key="item.id":label="item.provinceName":value="item.id"
        />
      </el-select>
    </div>
    <divclass="flex-item">
      <label></label>
      <el-selectv-model="cityValue"placeholder="请选择市"@change="chooseCity">
        <el-optionv-for="item in cityData":key="item.id":label="item.cityName":value="item.id"
        />
      </el-select>
    </div>
    <divclass="flex-item">
      <label>区、县</label>
      <el-selectv-model="areaValue"placeholder="请选择区、县"@change="chooseArea">
        <el-optionv-for="item in areaData":key="item.id":label="item.districtName":value="item.id"
        />
      </el-select>
    </div>
  </div>
</template>

<script>
//导入Axios的HTTP请求处理封装类
import { Province, City, District } from'@/api/city'exportdefault{
data() {
return{
provinceValue:
'',
cityValue:
'',
areaValue:
'',

provinceData: [],
cityData: [],
areaData: []
}
},
created() {
Province.GetAll().then(res
=>{if(res.result) {this.provinceData=res.result.items//console.log(res.result); }
}).
catch(e=>{this.$message.error('网络连接超时');
})
},
methods: {
chooseProvince(value) {
this.cityValue= '';this.areaValue= '';this.cityData=[];this.areaData=[];vardata={ ProvinceID: value };
City.GetAll(data).then(res
=>{if(res.result) {this.cityData=res.result.items
}
}).
catch(e=>{this.$message.error('网络连接超时');
})
},
chooseCity(value) {
this.areaValue= '';vardata={ CityID: value };
District.GetAll(data).then(res
=>{if(res.result) {this.areaData=res.result.items
}
}).
catch(e=>{this.$message.error('网络连接超时');
})
},
chooseArea() {

}
}
}
</script> <style>.flex-container{display:flex;flex-flow:row wrap;justify-content:space-around;padding:0;margin:0;list-style:none; }.flex-item{padding:5px;height:auto;color:tomato;font-weight:bold;text-align:center; } </style>

一样可以实现省市区县的联动处理

以上就是几种VUE+Element 前端应用中,关于省市区县联动处理的组件使用的案例分析,希望大家在借鉴使用别人组件的基础上,也丰富自己的组件处理,毕竟Vue赋予我们强大的组件定制能力。

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

循序渐进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 前端应用开发过程中,实现简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中做法。

1、main.js处理逻辑

我们知道Vue的前端应用,main.js函数里面承载的逻辑内容很多,往往涉及到一些常用过滤器函数、全局界面函数、组件注册等常规的处理过程,在我们应用越来愈大,引入的控件越来越多的时候,往往需要很多代码来实现这些功能,但是单个文件代码行数到达一定程度的时候,可读性就降低了,而且可维护性就复杂了,于是我们往往需要对代码进行重构处理,本篇介绍的处理就是实现关注点分离,把不同的逻辑抽离到不同的文件里面,这样大大减少了main.js的代码行数,并提高main.js的可维护性。

main.js处理逻辑的内容大概可以分为几个重要的部分,如下图所示,同时这个也是我们分离逻辑代码的思路。

2、全局函数挂载

使用vue 时,有时需要设置公共的函数,便于在所有组件中调用或者在组件的生命周期中都可调用,这便需要自定义全局函数。

在main.js中写入函数

Vue.prototype.getToken = function(){
...
}

那么,在所有组件里都可调用函数

this.getToken();

里面我们把常规经常用到的一些JS处理函数,以及一些界面信息处理函数,通过全局挂载的方式,使得它们在任何模块页面都可以使用。

我们把这些处理放在一个单独的文件,如prototype.js里面,如下所示。

然后在main.js函数里面,使用如下代码加入即可。

//导入一些全局函数
import prototype from './prototype'Vue.use(prototype)

这样我们就可以在任何页面、模块里面使用我们全局的函数了。

或者

3、全局过滤器挂载

和全局函数做法类似,也可以用相同的方法挂载全局过滤器,过滤器处理一般用于对界面部分的内容进行格式化或者转义的操作。

我们可以自定义一些常用的过滤器放在filter/index.js文件里面,如下所示。

里面放置一些常见的js函数处理,如下所示。

export functionuppercaseFirst(string) {return string.charAt(0).toUpperCase() + string.slice(1)
}

然后我们在main.js里面引入这个全局过滤器即可。

//导入自定义全局过滤器
import * as filters from './filters'
//注册全局过滤器
Object.keys(filters).forEach(key =>{
Vue.filter(key, filters[key])
})

除了一些自己自定义过滤器外,建议可以使用一些第三方的常用过滤器,如下所示

//导入第三方常见过滤器,介绍参考https://github.com/freearhey/vue2-filters
import Vue2Filters from 'vue2-filters'Vue.use(Vue2Filters)

这个里面的过滤器很多比较常见,介绍参考
https://github.com/freearhey/vue2-filters

引入后,直接在界面中使用即可,如下使用代码。

或者

是不是觉得很方便。

4、常见组件注册

组件的注册,分为全局main.js函数中注册,和页面组件中注册,一般我们可以把常见的组件放置到全局中注册,这样避免每个页面都重复填写注册组件的代码,省却很多功夫。

例如,我们可以创建一个plugin.js的文件,用来分离全局注册注册的处理过程,在其中引入我们常用的一些组件,并进行注册处理。

有了这些文件的定义处理,我们在main.js里面,进行简单的导入使用即可。

//导入常见的组件,进行注册
import common from './components/Common/plugin'Vue.use(common)

最后这些就是实现了我们常规的几个过程的处理,包括抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中,并简化全局挂载的操作。

截取几个VUE+Element 前端应用系统模块的界面参考下。

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

循序渐进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变量方式解决弹出编辑对话框界面无法触发更新的问题

在我们一般系统中,往往都会涉及到附件的处理,有时候附件是图片文件,有时候是Excel、Word等文件,一般也就是可以分为图片附件和其他附件了,图片附件可以进行裁剪管理、多个图片上传管理,及图片预览操作,如果是其他附件,则只需上传和下载处理即可。本篇随笔基于ABP后端的接口整合,实现前后端的附件上传管理。

1、ABP后端附件管理接口

ABP框架是基于最新.net core 的技术方向,应用非常广泛的一个技术框架系列,它整合了很多.net core 领域相关开发技术,后端主要是发布Web API方式实现数据和前端的交互的,因此前端可以基于Web API基础上实现多种平台的对接,可以是常规的BS框架,如Vue+Element 前端路线,也可以是Winform/WPF的前端框架接入等。

ABP+Swagger UI 负责API接口的开发和公布,如下是附件上传模块的API接口的管理界面。

ABP框架后端接口的常规处理,如增删改查等接口命名都一致,参数方面也比较一致,而我们附件上传,则可以自定义一个自己喜欢名称的一个API接口名称,如这里定义一个PostUpload的方法,除了文件信息外,还包括一些参数来说明附件信息的。上传成功后,返回对应的路径集合。

ABP后端定义的接口实现,我们为了获得上下文对象的文件对象信息,我们在构造函数注入一个IHttpContextAccessor 对象。

    /// <summary>
    ///上传附件信息,应用层服务接口实现/// </summary>
[AbpAuthorize]public class FileUploadAppService : MyAsyncServiceBase<FileUpload, FileUploadDto, string, FileUploadPagedDto, CreateFileUploadDto, FileUploadDto>, IFileUploadAppService
{
private AppConfig config = newAppConfig();private readonly IRepository<FileUpload, string> _repository;//业务对象仓储对象 private readonly IRepository<User, long> _userRepository;//用户信息仓储对象 private IHttpContextAccessor _httpContext;//上下文对象 public FileUploadAppService(IRepository<FileUpload, string> repository, IRepository<User, long> userRepository, IHttpContextAccessor httpContext) : base(repository)
{
_repository
=repository;
_userRepository
=userRepository;
_httpContext
=httpContext;
}

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

在后端上传文件的时候,和我其他开发框架,如Winform框架、混合开发框架、BS等框架一样,我们后端为了方便,也可以使用FTP方式进行附件的上传(这里依旧是使用FluentFTP组件),如我们指定配置如下所示。

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

另外,我们需要整合Api和界面部分实现完整的功能,那么需要提供两个部分:一个是Api类的继承子类,一个是视图界面的内容。如下所示包含Api封装类文件和Vue + Element界面视图。

2、前端附件管理的实现

上传图片或者其他附件信息,我们可以用Element组件里面的el-Upload组件操作,这个控件基本上能够满足大多数的应用了,参考地址:
https://element.eleme.cn/#/zh-CN/component/upload

如下是其中的界面使用代码:

            <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> 

只是我们一般为了简化处理,往往使用一些基于El-Upload组件之上封装好的组件,更显方便而已。

一般的图片和附件上传界面如下所示。

如我往往喜欢喜欢使用稍加封装,代码量更少的一些第三方组件,如:

dream2023
/
vue-ele-upload-image

dream2023
/
vue-ele-upload-file

等这位仁兄的二次封装的组件来做附件管理,可以更加简洁一些。如下是使用的代码。

<el-row>
  <el-col:span="24">
    <el-form-itemlabel="图片"prop="picture">
      <ele-upload-imagev-model="editForm.picture":is-show-tip="false":size="100"action="/abp/services/app/FileUpload/PostUpload"title="封面图片":headers="myHeaders":data="{guid:editForm.id, folder:'用户图片'}":crop="true"
      />
    </el-form-item>
  </el-col>
  <el-col:span="24">
    <el-form-itemlabel="资料文档"prop="attachGUID">
      <ele-upload-filev-model="editForm.attachGUID_files":response-fn="handleAttachResponse"action="/abp/services/app/FileUpload/PostUpload":headers="myHeaders":data="{guid:editForm.attachGUID, folder:'用户图片'}":before-remove="beforeRemoveAttach"
      />
    </el-form-item>
  </el-col>

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

而附件列表我们通过调用ABP后端API即可获取,然后进行绑定即可。

//获取附件文件列表
param = { guid: this.editForm.attachGUID }
fileupload.GetByAttachGUID(param).then(data
=>{if(data.result) {this.editForm.attachGUID_files =[...data.result]
}
})

附件上传的操作,我们一般需要通过设置Header方式,来传递用户的身份信息,如下data部分的代码

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

而其中的界面组件中的data,是指定额外上传的文件附带信息,方便我们区分或者归类文件的。

附件列表,如果需要删除的,那么我们提示确认后,需要调用ABP后端API进行删除文件。

    beforeRemoveAttach(file, fileList) { //移除附件图片
      //服务端删除文件
      var param = { guid: this.editForm.attachGUID, index: fileList.indexOf(file) }
fileupload.RemoveAttach(param).then(data
=>{
console.log(data.result)
})
},

另外,如果确认附件是全部图片,我们也可以用图片列表控件的方式展示图片信息,如下所示。

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

循序渐进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变量方式解决弹出编辑对话框界面无法触发更新的问题