2023年2月

用户在系统登录后,一般会提供一个入口给当前用户更改当前的密码,其实更改密码操作是很简单的一个处理,不过本篇随笔主要是介绍结合前后端来实现这个操作,后端是基于ABP框架的,需要对密码的安全性进行一个设置,否则严格的密码规则非常不友好,毕竟我们系统很多情况下不需要那么复杂的密码规则。

1、ABP后端的密码设置和修改密码处理

    //密码验证规则
    services.Configure<IdentityOptions>(options =>{
options.Password.RequireDigit
= false;
options.Password.RequireLowercase
= false;
options.Password.RequireNonAlphanumeric
= false;
options.Password.RequireUppercase
= false;
options.Password.RequiredLength
= 6;
});

如果没有设置上面的操作,那么简单的密码修改,是无法通过ABP框架默认密码规则的检验的 。

如果嫌弃这样的密码规则太麻烦,那么设置了允许简单密码处理,那么一般符合6位密码都可以顺利通过了。

2、前端界面的处理和组件化操作

在管理系统中,一般在用户头像附近增加一些常用菜单,其中就包括修改密码的操作入口。

而往往我们还有其他地方,可能也需要增加对应的修改密码入口,如在用户管理的界面下。

也就是说,修改密码对话框符合组件重用的规则,在多处都可能使用到的。

那么,我们就需要把常用的界面封装层一个界面组件的方式,方便重用。

修改密码窗体作为一个组件进行开发,它的界面模板代码如下所示。

<template>
  <el-dialog:title="$t('changepass.title')":visible="isVisible":modal-append-to-body="false"@close="close">
    <el-formref="form":model="user":rules="rules"label-width="160px">
      <el-form-item:label="$t('changepass.oldpassword')"prop="oldPassword">
        <el-inputv-model="user.oldPassword":placeholder="$t('changepass.oldpassword_tip')"type="password" />
      </el-form-item>
      <el-form-item:label="$t('changepass.newpassword')"prop="newPassword">
        <el-inputv-model="user.newPassword":placeholder="$t('changepass.newpassword_tip')"type="password" />
      </el-form-item>
      <el-form-item:label="$t('changepass.confirmpassword')"prop="confirmPassword">
        <el-inputv-model="user.confirmPassword":placeholder="$t('changepass.confirmpassword_tip')"type="password" />
      </el-form-item>
      <el-form-item>
        <el-buttontype="primary"@click="submitChange">{{ $t('form.save') }}</el-button>
        <el-buttontype="danger"@click="close">{{ $t('form.close') }}</el-button>
      </el-form-item>
    </el-form>
  </el-dialog>
</template>

这里面使用到了多语言的设置,以便从对应的语言文件中读取对应的键值内容。

例如中文的zh.js里面部分内容如下所示。

切换到英文的话,自动通过多语言的函数,获取对应英文en.js的文件中的配置值。

其中修改密码的操作,是通过Api发起后端的数据处理,操作函数如下所示。

submitChange() {var param ={
userId: getUserId(),
newPassword:
this.user.newPassword,
currentPassword:
this.user.oldPassword
}
//console.log(param) this.$refs['form'].validate(valid =>{if(valid) {
user.ChangePassword(param).then(data
=>{if(data.result) {this.msgSuccess('修改成功')this.$emit('update:visible', false) //更新 } else{this.msgError('密码修改失败')
}
})
}
})
},

前端Api类的接口函数定义如下所示。

ABP后端接口的定义如下所示

修改密码成功后提示。

这样就顺利完成简单密码就可以修改的操作的了。

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

循序渐进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开发前端的时候,往往涉及到很多界面组件的使用,其中很多直接采用Element官方的案例即可,有些则是在这个基础上封装更好利用、更少代码的组件;另外有些则是直接采用第三方开发好的组件,目的就是实现所需功能外,尽量简化界面使用代码。本篇随笔介绍在我的项目中经常用到的各种界面组件和它的界面效果,以便在实际开发中进行相应的参考,提高开发效率。

我们以几个常规界面来介绍相关控件的使用,如列表界面,包括查询、表格、分页等信息,查询框中包含一些关键字段的信息查询,而表格中则包含重要字段信息的展示或者相关操作。

然后编辑界面,简单的如下所示。

复杂界面中,包含很多录入信息的界面组件

另外还可以设计富文本、图片上传等操作。

以上就是一些常规界面的录入情况,其中使用到了很多相关的界面组件,包括文本录入框、下拉选框、树列表、标签、图片上传、富文本、日期、省市区列表等等。

下面分别对一些常规的界面组件的使用进行介绍。

1、普通文本输入框

文本框一般是比较常见的界面组件,一个是内容录入比较灵活,其次也是因为使用简单方便。

一般这种录入界面组件,都是放在表单里面处理比较多,因为可以利用表单的校验功能实现校验等操作,也可以利用表单对应的对象属性一并提交。

使用代码很简单,如下所示。

  <el-form-itemlabel="姓名"prop="name">
    <el-inputv-model="searchForm.name" />
  </el-form-item>

如果是有些录入需要有前缀或者后缀的,则可以通过Slot模板进行处理。

  <el-col:span="12">
    <el-form-itemlabel="身高"prop="height">
      <el-inputv-model="addForm.height"><spanslot="suffix">厘米</span></el-input>
    </el-form-item>
  </el-col>
  <el-col:span="12">
    <el-form-itemlabel="体重"prop="weight">
      <el-inputv-model="addForm.weight"><spanslot="suffix">公斤</span></el-input>
    </el-form-item>
  </el-col>

或者我们参考官方
Element的案例
,也可以看到使用的效果和代码

<div>
  <el-inputplaceholder="请输入内容"v-model="input1">
    <templateslot="prepend">Http://</template>
  </el-input>
</div>
<divstyle="margin-top: 15px;">
  <el-inputplaceholder="请输入内容"v-model="input2">
    <templateslot="append">.com</template>
  </el-input>
</div>
<divstyle="margin-top: 15px;">
  <el-inputplaceholder="请输入内容"v-model="input3"class="input-with-select">
    <el-selectv-model="select"slot="prepend"placeholder="请选择">
      <el-optionlabel="餐厅名"value="1"></el-option>
      <el-optionlabel="订单号"value="2"></el-option>
      <el-optionlabel="用户电话"value="3"></el-option>
    </el-select>
    <el-buttonslot="append"icon="el-icon-search"></el-button>
  </el-input>
</div>

另外,文本输入框还包括一个多行的输入textarea的类型输入。

<el-inputtype="textarea"placeholder="请输入内容"v-model="textarea"maxlength="30"show-word-limit>

有时候,文本框还需要输入长度的限制,这个在文本框很容易设置,界面效果如下所示。

界面代码如下所示。

<el-inputtype="text"placeholder="请输入内容"v-model="text"maxlength="10"show-word-limit>
</el-input>
<divstyle="margin: 20px 0;"></div>
<el-inputtype="textarea"placeholder="请输入内容"v-model="textarea"maxlength="30"show-word-limit>
</el-input> 

2、日期输入范围选择

日期我们一般用内置的el-date-picker即可,可以显示短日期、带时间的长日期,或者选择日期范围多项选择。

<el-form-itemlabel="出生日期"prop="birthDate">
  <el-date-pickerv-model="addForm.birthDate"align="right"type="date"placeholder="选择日期"value-format="yyyy-MM-dd"
  />
</el-form-item>

而我们在查询的时候,一般就是一个日期区间。

我们只需要指定type="daterange"即可。

<el-form-itemlabel="创建时间">
<el-date-pickerv-model="searchForm.creationTime"type="daterange"align="right"unlink-panels
range-separator
="至"start-placeholder="开始日期"end-placeholder="结束日期":picker-options="pickerOptions" /> </el-form-item>

其中的 pickerOptions 则是我们定义的快捷选择项,单独放置一个文件constantOptions.js里面,需要导入对象使用即可,这样可以减少很多页面的JS代码,其他常见变量也是这样的方式处理。

//选择时间范围的快捷选项
export const pickerOptions ={
shortcuts: [
{
text:
'昨天',
onClick(picker) {
const end
= newDate();
const start
= newDate();
start.setTime(start.getTime()
- 3600 * 1000 * 24 * 1);
picker.$emit(
'pick', [start, end]);
}
},
{
text:
'最近一周',
onClick(picker) {
const end
= newDate();
const start
= newDate();
start.setTime(start.getTime()
- 3600 * 1000 * 24 * 7);
picker.$emit(
'pick', [start, end]);
}
}, {
text:
'最近一个月',
onClick(picker) {
const end
= newDate();
const start
= newDate();
start.setTime(start.getTime()
- 3600 * 1000 * 24 * 30);
picker.$emit(
'pick', [start, end]);
}
}, {
text:
'最近三个月',
onClick(picker) {
const end
= newDate();
const start
= newDate();
start.setTime(start.getTime()
- 3600 * 1000 * 24 * 90);
picker.$emit(
'pick', [start, end]);
}
}]
}

日期输入控件,有时候会出现获得的日期值比实际的值少一天的问题,一般通过设置
value-format="yyyy-MM-dd"
即可解决。

3、标签显示和录入

标签的显示和录入,也是我们很常见的处理,多颜色的标签可以提高界面的美观,同时也是对多选值的友好显示。

我们先来看看
官方的案例

<el-tag>标签一</el-tag>
<el-tagtype="success">标签二</el-tag>
<el-tagtype="info">标签三</el-tag>
<el-tagtype="warning">标签四</el-tag>
<el-tagtype="danger">标签五</el-tag>

其实就是指定type不同就可以实现不同的效果,另外我们还可以通过设置effect属性来改变主题,默认为 light

标签的显示是有了,一般我们还需要一个标签的录入操作,虽然官方也提供了一个案例,不过是简单对Tag标签组件的使用,代码还是不少。

实际上,我们在使用组件的时候,系统尽量减少代码量,因此我们可以参考官方的方式建立一个集标签显示和录入功能一起的组件,不过Github上已经有人开发了,我就直接拿过来用吧。

https://github.com/xiispace/el-input-tag

界面效果如下所示。

使用的时候,界面代码和普通组件一样简单,非常棒。

<el-form-itemlabel="标签"prop="tags">
  <el-input-tagv-model="editForm.tags_array" />
</el-form-item>

JS部分只需要转换下我们的内容即可。我们tags属性存储是逗号分开的字符串,那么tags_array这是数组,我们获取值的时候,需要进行转换,把字符串转换为数组的值。

      //将字符串的标签,转换为Array集合
      if (this.addForm.tags) {this.addForm.tags_array = this.addForm.tags.split(',')
}

而修改组件,会引起数组的改变,为了避免集中转换,我们可以通过watch函数来统一处理,这样tags内容就会自动变化了。

  watch: { //对过滤内容进行监控,实现树列表过滤
    //键路径必须加上引号
    'addForm.tags_array': function(val, oldval) {if(val) {this.addForm.tags =val.toString()
}
},
'editForm.tags_array': function(val, oldval) {if(val) {this.editForm.tags =val.toString()
}
}
},

4、下拉列表和字典列表Select组件

下拉列表或者下拉框是我们常见的选择方式之一,下拉列表有时候可以是固定列表,如男女、有效/无效, 也可以是多个动态列表供选择。

或者

或者是一连串联动的组件构成。

Select组件的使用如下所示。

  <el-selectv-model="value"placeholder="请选择">
    <el-optionv-for="item in options":key="item.value":label="item.label":value="item.value">
    </el-option>
  </el-select>

而其中的JS部分,options就是有value,lable组成的对象数组,如下代码所示

<script>exportdefault{
data() {
return{
options: [{
value:
'选项1',
label:
'黄金糕'}, {
value:
'选项2',
label:
'双皮奶'}],

value:
''}
}
}
</script>


el-select
设置
clearable
属性,则可将选择器清空。为
el-select
添加
filterable
属性即可启用搜索功能。


el-select
设置
multiple
属性即可启用多选,此时
v-model
的值为当前选中值所组成的数组。默认情况下选中值会以 Tag 的形式展现,你也可以设置
collapse-tags
属性将它们合并为一段文字。

纵观Select组件,其数据源要么是固定列表,要么是从数据库字典里面获取的内容,为了简化界面代码的调用,我们把它作为字典组件进行了定义,可以绑定固定列表,也可以绑定字典表里面的字典类型名称。

组件使用很简单,如下代码所示。

<el-form-itemlabel="状态">
    <my-dictdatav-model="addForm.status":options="Status"/>
    <my-dictdatav-model="addForm.status"type-name="状态"/>
</el-form-item>

创建一个组件,定义好两个属性,如下代码所示。

在组件Mounted的时候,我们对这些内容进行解析,构建为对应组件的数据源即可。

mounted() {var that = this;if (this.typeName && this.typeName !== '') {//使用字典类型,从服务器请求数据
      var params = { dictTypeName: this.typeName };
dictdata.GetListItemByDictType(params).then(data
=>{var list =data.result;//console.log(list); if(list) {
list.forEach(item
=>{//console.log(item) that.dictItems.push({ text: item.text, value: item.value });
})
}
})
}
else if (this.options && this.options.length > 0) {//使用固定字典列表 this.options.forEach(item =>{if (item && typeof (item.value) !== 'undefined') {
that.dictItems.push(item)
}
});
}
//设置默认值 this.svalue = this.value;
},

这样,使用起来就很方便了,可以直接绑定字典类型即可。

<el-col:span="12">
    <el-form-itemlabel="民族"prop="nationality">
      <my-dictdatav-model="editForm.nationality"type-name="民族" />
    </el-form-item>
</el-col>
<el-col:span="12">
    <el-form-itemlabel="学历"prop="education">
      <my-dictdatav-model="editForm.education"type-name="学历" />
    </el-form-item>
</el-col>
<el-col:span="12">
    <el-form-itemlabel="婚姻状况"prop="marriage">
      <my-dictdatav-model="editForm.marriage"type-name="婚姻状况" />
    </el-form-item>
</el-col>
<el-col:span="12">
    <el-form-itemlabel="星座"prop="star">
      <my-dictdatav-model="editForm.star"type-name="星座" />
    </el-form-item>
</el-col>

这样的组件,相对于每次调用API去获取字典资源,然后进行转换处理,代码更加简单易懂了,同时也更加优雅,提高代码质量。

5、复选框和单选框

复选框和单选框也是经常使用的,对简单的一些选项进行勾选处理。

多选框有多选框组,单选框也有单选框组,这样组合起来界面更加美观。

<template>
  <el-checkbox-groupv-model="checkList">
    <el-checkboxlabel="复选框 A"></el-checkbox>
    <el-checkboxlabel="复选框 B"></el-checkbox>
    <el-checkboxlabel="复选框 C"></el-checkbox>
    <el-checkboxlabel="禁用"disabled></el-checkbox>
    <el-checkboxlabel="选中且禁用"disabled></el-checkbox>
  </el-checkbox-group>
</template>

<script>exportdefault{
data () {
return{
checkList: [
'选中且禁用','复选框 A']
};
}
};
</script>

<template>
  <el-radio-groupv-model="radio">
    <el-radio:label="3">备选项</el-radio>
    <el-radio:label="6">备选项</el-radio>
    <el-radio:label="9">备选项</el-radio>
  </el-radio-group>
</template>

<script>exportdefault{
data () {
return{
radio:
3};
}
}
</script>

这两个是官方比较典型的案例说明了,一般我们也是以组合的方式进行使用。

例如我们在产品类型绑定品牌列表的时候,就是采用了复选框汉族,通过key/value的集合组合即可实现键值对的处理。

  <el-col:span="24">
    <el-form-itemlabel="品牌关联"prop="bindBrands">
      <el-checkbox-groupv-model="addForm.bindBrands"style="padding:10px;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)">
        <el-checkboxv-for="(item, i) in brandList":key="i":label="item.id">{{ item.brandName }}</el-checkbox>
      </el-checkbox-group>
    </el-form-item>
  </el-col>
<el-form-itemlabel="虚拟商品"prop="isVirtual">
  <el-radio-groupv-model="addForm.isVirtual">
    <el-radioclass="radio":label="1"></el-radio>
    <el-radioclass="radio":label="0"></el-radio>
  </el-radio-group>
</el-form-item>

5、Switch开关

Switch开关有点类似于单选框的操作,不过提供比较美观的一个处理,表示两种相互对立的状态间的切换,多用于触发「开/关」。

<el-switchv-model="value1"active-text="按月付费"inactive-text="按年付费">
</el-switch>
<el-switchstyle="display: block"v-model="value2"active-color="#13ce66"inactive-color="#ff4949"active-text="按月付费"inactive-text="按年付费">
</el-switch>

<script>exportdefault{
data() {
return{
value1:
true,
value2:
true}
}
};
</script>

这个组件提供了很多属性配置,可以自定义激活/未激活的颜色、文本、值的信息,如下说明。

因此,我们有时候的值如果是0,1的就可以设置一下active-value/inactive-value的值,否则默认是Boolean变量。

如下,有时候我们在查询中也会增加一个Switch来处理一些属性查询。

如果我们这样用,而isVirtual属性是0,1的数值的话,那么我们就需要进行转换了

      <el-form-itemlabel="虚拟商品"prop="isVirtual">
        <el-switchv-model="searchForm.isVirtual"active-color="#13ce66"inactive-color="gray" />
      </el-form-item>

那么我们发起查询的时候,就需要把布尔变量转换为0,1的值了,如下代码所示

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

};
//使用日期范围选择控件,在查询对象增加开始日期CreationTimeStart、结束日期CreationTimeEnd this.addDateRange(param, this.searchForm.creationTime)

param.IsVirtual
= param.IsVirtual ? 1 : 0;//修改数据类型为int //console.log(param) //获取列表,绑定到模型上,并修改分页数量 this.listLoading = trueproductType.GetAll(param).then(data=>{this.list =data.result.itemsthis.pageinfo.total =data.result.totalCountthis.listLoading = false})
},

或者我们定义active-value、inactive-value来处理会简单一些。

  <el-form-itemlabel="虚拟商品"prop="isVirtual">
    <el-switchv-model="searchForm.isVirtual"active-value="1"inactive-value="0"active-color="#13ce66"inactive-color="gray" />
  </el-form-item>

那这样查询就不需要转换bool到数值了,因为已经默认是0/1的数值类型了,更改后的查询代码如下所示。

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

6、消息提示及弹框

在组件或者页面里面,我们都需要大量使用到提示信息,确认信息等信息提示功能。而Element也提供了这样不同的组件来使用。

多个不同类型的消息提示窗口,界面如下所示。

Notification 通知组件,悬浮出现在页面角落,显示全局的通知提醒消息。界面如下所示

我们为了方便,一般先简单封装一下,并在全局挂载这些方法,以便在不同的地方简化调用。

    //提示成功信息
    Vue.prototype.msgSuccess = function(msg) {this.$message({ showClose: true, message: msg, type: 'success'})
}
//提示警告信息 Vue.prototype.msgWarning = function(msg) {this.$message({ showClose: true, message: msg, type: 'warning'})
}
//提示错误信息 Vue.prototype.msgError = function(msg) {this.$message({ showClose: true, message: msg, type: 'error'})
}
//提示信息 Vue.prototype.msgInfo = function(msg) {this.$message.info({ showClose: true, message: msg })
}
//确认信息 Vue.prototype.msgConfirm = function(msg = '您确认删除选定的记录吗?') {return this.$confirm(msg, '操作提示',
{
type:
'warning' //success,error,info,warning //confirmButtonText: '确定', //cancelButtonText: '取消' })
}

这样我们在页面中就可以很方便使用了,如删除确认对话框操作。

    showDelete(id) { //删除对话框处理
      this.msgConfirm().then(() =>{//删除操作处理代码
        var param ={ id: id }
productType.Delete(param).then(data
=>{//console.log(data) if(data.success) {//提示信息 this.msgSuccess('删除成功!')//刷新数据 this.getlist()
}
else{this.msgError('删除失败:' +data.error)
}
})
})
},

由于篇幅有限,下篇在继续介绍其他组件的使用了。

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

循序渐进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开发前端的时候,往往涉及到很多界面组件的使用,其中很多直接采用Element官方的案例即可,有些则是在这个基础上封装更好利用、更少代码的组件;另外有些则是直接采用第三方开发好的组件,目的就是实现所需功能外,尽量简化界面使用代码。本篇随笔介绍在我的项目中经常用到的各种界面组件和它的界面效果,以便在实际开发中进行相应的参考,提高开发效率。

本篇继续上一篇随笔《
循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)
》,对界面控件进行进一步的介绍。

7、树列表及控件封装

在很多数据展示场景中,都需要引入树状列表进行展示,包括无穷层级的树列表,或者简单的主从表关系,都可能用到树列表的展示。

树列表控件的使用,之前在随笔中《
循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用

这些都是基于Element的树形控件进行使用或封装组件使用的界面。

树列表的简单使用代码如下所示。

<el-tree:data="data":props="defaultProps"@node-click="handleNodeClick"></el-tree>

数据这是一个嵌套的列表对象即可,如下代码所示

      treedata: [ //初始化树列表
{
label:
'一级 1',
children: [{
label:
'二级 1-1'}]
}]

我们只要根据实际情况构建好这些数据给树控件使用即可。一般树形控件顶部会带一个输入框来进行键值查询过滤的操作,以方便用户快速检索的。

如果设置有选择框,得到界面如下所示。

主要设置  show-checkbox 和 @check-change="handleCheckChange" 即可。

界面代码如下所示

<el-treestyle="padding-top:10px":data="treedata"node-key="id"default-expand-all
highlight-current
show-checkbox
:default-checked-keys
="['1-1-1']"@node-click="handleNodeClick"@check-change="handleCheckChange" > <spanslot-scope="{ node, data }"class="custom-tree-node"> <span> <i:class="node.icon ? node.icon : 'el-icon-price-tag'" />{{ node.label }}&nbsp;&nbsp; </span> </span> </el-tree>

有时候,我们树列表控件的数据可能来源于常规的二维表,如下数据格式所示。

var data =[
{
"id":1,"parent":0,"addr":"重庆"},
{
"id":11,"parent":1,"addr":"重庆"},
{
"id":12,"parent":1,"addr":"重庆"},
{
"id":121,"parent":12,"addr":"重庆"},
{
"id":2,"parent":0,"addr":"重庆"},
{
"id":21,"parent":2,"addr":"重庆"},
{
"id":211,"parent":21,"addr":"重庆"},
{
"id":3,"parent":0,"addr":"重庆"},
{
"id":4,"parent":0,"addr":"重庆"},
{
"id":31,"parent":3,"addr":"重庆"},
{
"id":32,"parent":3,"addr":"重庆"}
];

而树控件使用的data 则是嵌套数据,我们如果每次使用都需要为它们专门做一个函数有点多余,我们可以用前端转换的方式来实现树数据的转换。

具体操作可以参考:
https://blog.csdn.net/unamattin/article/details/77152451

//二维列表转树形控件所需嵌套数据格式
export functiongetJsonTree(data, config) {
const id
= config.id || 'id'; const pid = config.pid || 'pid';
const children
= config.children || 'children';
const label
= config.label || 'name';//默认label为name字段 const idMap= []; const jsonTree =[];
data.forEach(
function(v) {
idMap[v[id]]
=v;
});
data.forEach(
function(v) {
const parent
=idMap[v[pid]];if(parent) {!parent[children] && (parent[children] =[]);
parent[children].push(v);
}
else{
jsonTree.push(v);
}
});
//treeAddIcon(jsonTree, label);// 循环遍历给tree加图标 returnjsonTree;
}

这样树列表的绑定操作如下代码所示。

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

顺便说一下,由于引入一些原始控件需要实现很多相应的操作,一般情况下,我们可以把树控件常规的处理封装成一个组件的方式使用,其中包括了常规的过滤、选中等操作处理。如下是我们简单封装的树控件的自定义组件,用于处理常规的树列表信息。

这样使用起来,界面代码减少很多,只需要实现对应的事件即可。

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

另外,更高维度上,我们还可以把业务界面按模块分离出来,实现不同的界面的组装,这样可以减轻代码的臃肿程度,提高代码可读性。

如之前说过的机构信息,页面逻辑比较多,可以简化为各个不同的模块,然后拼装组件即可。

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

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

8、按钮及按钮组

按钮比较常用,一般我们依照常规的方式使用,并设置样式即可。

代码如下所示

<el-row>
  <el-button>默认按钮</el-button>
  <el-buttontype="primary">主要按钮</el-button>
  <el-buttontype="success">成功按钮</el-button>
  <el-buttontype="info">信息按钮</el-button>
  <el-buttontype="warning">警告按钮</el-button>
  <el-buttontype="danger">危险按钮</el-button>
</el-row>

<el-row>
  <el-buttonplain>朴素按钮</el-button>
  <el-buttontype="primary"plain>主要按钮</el-button>
  <el-buttontype="success"plain>成功按钮</el-button>
  <el-buttontype="info"plain>信息按钮</el-button>
  <el-buttontype="warning"plain>警告按钮</el-button>
  <el-buttontype="danger"plain>危险按钮</el-button>
</el-row>

<el-row>
  <el-buttonround>圆角按钮</el-button>
  <el-buttontype="primary"round>主要按钮</el-button>
  <el-buttontype="success"round>成功按钮</el-button>
  <el-buttontype="info"round>信息按钮</el-button>
  <el-buttontype="warning"round>警告按钮</el-button>
  <el-buttontype="danger"round>危险按钮</el-button>
</el-row>

<el-row>
  <el-buttonicon="el-icon-search"circle></el-button>
  <el-buttontype="primary"icon="el-icon-edit"circle></el-button>
  <el-buttontype="success"icon="el-icon-check"circle></el-button>
  <el-buttontype="info"icon="el-icon-message"circle></el-button>
  <el-buttontype="warning"icon="el-icon-star-off"circle></el-button>
  <el-buttontype="danger"icon="el-icon-delete"circle></el-button>
</el-row>

我在之前的界面中采用的按钮样式如下所示。

后来我觉得把按钮组合起来更好看,也更节省空间。

这里其实就是使用了按钮组的作用。

    <!--功能操作按钮-->
    <el-rowstyle="float:right;padding-bottom:10px">
      <el-button-group>
        <el-buttonicon="el-icon-search"type="primary"size="mini"round @click="search()">查询</el-button>
        <el-buttonicon="el-icon-refresh-left"type="warning"size="mini"round plain @click="resetSeachForm('searchForm')">重置</el-button>
      </el-button-group>
      <el-button-group>
        <el-buttonicon="el-icon-plus"type="success"size="mini"round @click="showAdd()">添加</el-button>
        <el-buttonicon="el-icon-document-remove"type="danger"size="mini"round :disabled="listSelection.length === 0"@click="batchDelete()">批量删除</el-button>
      </el-button-group>
      <el-button-group>
        <el-buttonicon="el-icon-upload2"type="warning"size="mini"round @click="showImport()">导入</el-button>
        <el-buttonicon="el-icon-download":loading="downloadLoading"type="primary"size="mini"round @click="handleDownload()">导出</el-button>
      </el-button-group>
    </el-row>

9、表格控件的使用

表格控件是我们展示数据库二维表内容的重要承载,一般在主列表页面中展示表格数据。

表格控件的使用也比较简单,简单案例的代码所示。

<template>
  <el-table:data="tableData"border
style
="width: 100%"> <el-table-columnprop="date"label="日期"width="180"> </el-table-column> <el-table-columnprop="name"label="姓名"width="180"> </el-table-column> <el-table-columnprop="address"label="地址"> </el-table-column> </el-table> </template>

但往往我们处理表格列的各种内容承载的时候,需要丰富很多,如包括标签、Switch开关,按钮操作、日期格式化、内容转换等操作。

如标签列,可以进行循环处理。

      <el-table-columnalign="center"label="标签"width="100">
        <templateslot-scope="scope">
          <el-tagv-for="tag in scope.row.tags.split(',')":key="tag"type="primary"
          >{{ tag }}</el-tag>
        </template>
      </el-table-column>

或者结合过滤器进行处理。

      <el-table-columnalign="center"label="状态">
        <templateslot-scope="scope">
          <el-tag:type="(scope.row.state === 0) ? 'success' : 'danger'"effect="dark">{{ scope.row.state | stateFilter }}</el-tag>
        </template>
      </el-table-column>

日期,我们也用日期过滤器实现转换。

<el-table-columnalign="center"label="创建时间"width="160"prop="creationTime":formatter="timeFormat" />

表格的操作列,整个对单个记录操作的各种操作入口。

      <!--表格行操作按钮-->
      <el-table-columnalign="center"label="操作"width="110">
        <templatescope="scope">
          <el-row>
            <el-button-group>
              <el-tooltipeffect="light"content="查看"placement="top-start">
                <el-buttonicon="el-icon-search"type="success"circle size="mini"@click="showView(scope.row.id)" />
              </el-tooltip>
              <el-tooltipeffect="light"content="编辑"placement="top-start">
                <el-buttonicon="el-icon-edit"type="primary"circle size="mini"@click="showEdit(scope.row.id)" />
              </el-tooltip>
              <el-tooltipeffect="light"content="删除"placement="top-start">
                <el-buttonicon="el-icon-delete"type="danger"circle size="mini"@click="showDelete(scope.row.id)" />
              </el-tooltip>
            </el-button-group>
          </el-row>
        </template>
      </el-table-column>

如果是开关操作,我们也可以结合一起展示和操作处理。

操作代码如下所示

          <el-table-columnalign="center"label="状态">
            <templateslot-scope="scope">
              <el-tag:type="scope.row.status==0 ? 'success' : 'warning'"effect="dark">{{ scope.row.status==0 ? '正常' : '停用' }}</el-tag>
              <el-switchv-model="scope.row.status":active-value="0":inactive-value="1"@change="changeStatus(scope.$index, scope.row)"
              />
            </template>
          </el-table-column>

另外表格数据,一般还涉及到Excel的导出操作,这个其实是采用了SheetJS的处理方式草操作XLSX的方式导出Excel的,这里引用了前人开发的Export2Excel 组件进行处理。

    handleDownload() { //导出Excel文件
      this.downloadLoading = trueimport('@/vendor/Export2Excel').then(excel =>{
const tHeader
= ['父ID', '分类编码', '分类名称', '分类描述', '分类图片封面', '分类图标', '排序', '状态']
const filterVal
= ['pid', 'categoryCode', 'categoryName', 'note', 'pictureUrl', 'icon', 'sortCode', 'status']

const list
= this.list
const data
= this.formatJson(filterVal, list)
excel.export_json_to_excel({
header: tHeader,
data,
filename:
'商品分类',
autoWidth:
true,
bookType:
'xlsx'})this.downloadLoading = false})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j =>{if (j === 'creationTime') {return this.parseTime(v[j], '{y}-{m}-{d} {h}:{i}:{s}')
}
else{returnv[j]
}
}))
},

一般来说,Element的表格控件已经具有非常丰富的功能了,包括固定行列、展开行、懒加载数据、多级表头、排序筛选、自定义列、合计行列等操作,

如果有需要直接编辑表格数据的操作,建议可以采用第三方表格组件来实现:
https://github.com/huangshuwei/vue-easytable

10、树形下拉列表框及封装

除了常规的树列表展示内容外,我们也需要一个在下拉列表中展示树内容的界面组件。

这里又得引入一个第三方的界面组件,因此Element的Select组件不支持树列表。

GitHub地址:
https://github.com/riophae/vue-treeselect

官网地址:
https://vue-treeselect.js.org/

常规的界面代码如下使用

<treeselectv-model="value":multiple="true":options="options" />

复杂一点的使用界面代码,可以指定更多的属性设置,以及处理事件操作。

<treeselectv-model="value":options="treedata":multiple="true":flat="true":default-expand-level="Infinity":open-on-click="true":open-on-focus="true"clearable
:max-height
="200" />

这里的options就是树控件的数据,是一个嵌套结构的列表对象

options: [ {
id:
'a',
label:
'a',
children: [ {
id:
'aa',
label:
'aa',
} ],

另外,有时候我们可能在树选择框中需要用来设置父级列表的处理,由于这个业务比较常见,而且逻辑相对比较通用,那么我们可以考虑把它封装为一个组件的方式,然后再在界面上使用。

界面使用效果如下所示

这样勾选【作为顶级目录项】,那么则会自动置空内容,也就是父ID为空了,如果选择具体的树节点,那么就以该节点的ID作为PID,这样操作把调用界面处理的逻辑简化了,更方便使用。

  <el-col:span="24">
    <el-form-itemlabel="父项名称"prop="pid">
      <MyTreeselectTopv-model="addForm.pid":options="treedata" />
    </el-form-item>
  </el-col>

这样我们可以在多处使用这个带有树列表展示,以及设置顶级节点的树列表框选择控件了,如在批量录入商品分类的操作界面中一样使用。

11、其他组件

组件不可能一一介绍完,一般情况下,我们依照我们的数据展示需要,引入不同的界面组件,一般可以在GitHub上找到很多对比,找适合自己的且评分较高的即可。

如条码和二维码组件,我使用 @chenfengyuan/vue-barcode和 @chenfengyuan/vue-qrcode,一般在Github上搜索下关键字,总能找到一些很受欢迎的第三方组件。

安装这些组件都有具体的说明,如下所示(
如果卸载,直接修改install为uninstall即可
)。

npm install @chenfengyuan/vue-barcode vue

以及

npm install @chenfengyuan/vue-qrcode vue

条码和二维码的展示效果如下所示

如果全局引入barcode和qrcode 组件,我们在main.js里面引入即可,如下代码所示

//引入barcode,qrcode
import VueBarcode from '@chenfengyuan/vue-barcode';
import VueQrcode from
'@chenfengyuan/vue-qrcode';
Vue.component(VueBarcode.name, VueBarcode);

图表组件,我们这里使用了基于vue-echarts组件模块来处理各种图表vue-echarts是对echarts图表组件的封装。

首先使用npm 安装vue-echarts组件。

git地址:
https://github.com/ecomfe/vue-echarts

NPM安装命令

npm install echarts vue-echarts

然后在对应模块页面里面引入对应的组件对象,如下代码所示。

import ECharts from 'vue-echarts' //主图表对象
import 'echarts/lib/chart/line' //曲线图表
import 'echarts/lib/chart/bar' //柱状图
import 'echarts/lib/chart/pie' //饼状图
import 'echarts/lib/component/tooltip' //提示信息

在Vue模块页面的Template 里面,我们定义界面代码如下即可。

<v-chartref="simplebar":options="simplebar"autoresize/>

然后在data里面为它准备好数据即可,如下代码所示。

data() {return{
simplebar: {
title: { text:
'柱形图Demo'},
tooltip: {},
xAxis: {
data: [
'衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [{
name:
'销量',
type:
'bar',
data: [
5, 20, 36, 10, 10, 20]
}]
}
}
}

详细可以参考我的随笔:《
循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示

综上这些界面控件的使用原则,原则上经常重用的部分,可以把它简单化包装为一个组件,这样可以减少很多界面代码和常规的处理JS函数,如果更高维度上还有抽离逻辑的可能呢,如我之前在角色、机构模块里面处理很多公用逻辑界面一样,我们把这些抽离出来作为一个组件模块,加上一些特殊的开关或者属性设置变量,就可以响应不同界面上的操作。

还有就是这些界面组件的使用,我们不可能一一的记住,那么使用合适的代码生成工具来处理就会跟提高效率了,代码生成工具,一般会根据字段的数据类型,名称等信息,进行不同的处理,把常规的列表界面、查看、编辑、新增、导入、导入等界面和功能处理函数整合一起生成,我们在这个基础上进行微调修改,效率更高。

我们的代码生成工具Database2sharp,专门为优化过的框架结构进行代码生成,在界面工具栏或者菜单中选择【代码生成】【ABP的Vue+Element界面代码】,如下所示。

接着在出来的对话框上选择对应数据库和表进行生成,一步步执行即可。

使用代码生成工具快速生成基于ABP框架的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变量方式解决弹出编辑对话框界面无法触发更新的问题

在我们开发业务的时候,一般数据库表都有相关的关系,除了单独表外,一般还包括一对多、多对多等常见的关系,在实际开发过程中,需要结合系统框架做对应的处理,本篇随笔介绍基于ABP框架对EF实体、DTO关系的处理,以及提供对应的接口进行相关的数据保存更新操作。

1、一对多关系的数据处理

一对多,也可以叫做主从表的关系,其中从表有一个外键和主表进行关联,如下所示。

上图是一个简单的主从表关系,其中客户信息表只有简单的一两个字段用于演示,从表用来记录对应客户的地址信息。

其中表中的CreateUserId、CreateTime、LastModifierUserId、LastModificationTime、DeleterUserId、IsDeleted、DeletionTime、TenantId字段,是我们一般设计ABP表保留的字段。

我们先从一个关系图来了解下框架下的领域驱动模块中的各个类之间的关系。

ABP框架,和应用服务层或者界面层打交道的数据对象是DTO对象,和数据库打交道的是实体对象,连接连接起来是通过AutoMapper实现映射处理,映射是通过映射文件进行配置,一般我们可以根据数据库表信息进行生成DTO、Entity,以及映射文件。

以客户及客户地址表为例,生成的DTO对象如下所示。

    /// <summary>
    ///创建客户信息,DTO对象/// </summary>
    public class CreateCustomerDto : FullAuditedEntityDto<string>{/// <summary>
        ///默认构造函数(需要初始化属性的在此处理)/// </summary>
        publicCreateCustomerDto()
{
this.Id =Guid.NewGuid().ToString();
}
#region Property Members /// <summary> ///姓名/// </summary> [Required]public virtual string Name { get; set; }/// <summary> ///年龄/// </summary> //[Required] public virtual int? Age { get; set; }#endregion}/// <summary> ///客户信息,DTO对象/// </summary> public classCustomerDto : CreateCustomerDto
{
}
    /// <summary>
    ///创建客户地址簿,DTO对象/// </summary>
    public class CreateCustomerAddressDto : CreationAuditedEntityDto<string>{/// <summary>
        ///默认构造函数(需要初始化属性的在此处理)/// </summary>
        publicCreateCustomerAddressDto()
{
this.Id =System.Guid.NewGuid().ToString();
}
#region Property Members /// <summary> ///客户ID/// </summary> //[Required] public virtual string Customer_ID { get; set; }/// <summary> ///省份/// </summary> //[Required] public virtual string Province { get; set; }/// <summary> ///城市/// </summary> //[Required] public virtual string City { get; set; }/// <summary> ///区县/// </summary> //[Required] public virtual string District { get; set; }/// <summary> ///详细地址/// </summary> //[Required] public virtual string DetailAddress { get; set; }/// <summary> ///排序/// </summary> //[Required] public virtual string SortCode { get; set; }#endregion}/// <summary> ///客户地址簿,DTO对象/// </summary> public classCustomerAddressDto : CreateCustomerAddressDto
{
}

其表对应的实体类,也和DTO类似,不过是和数据库打交道的数据对象

    /// <summary>
    ///客户信息,领域对象/// </summary>
    [Table("T_Customer")]public class Customer : FullAuditedEntity<string>{/// <summary>
        ///默认构造函数(需要初始化属性的在此处理)/// </summary>
        publicCustomer()
{
}
#region Property Members /// <summary> ///姓名/// </summary> //[Required] public virtual string Name { get; set; }/// <summary> ///年龄/// </summary> //[Required] public virtual int? Age { get; set; }#endregion}
   /// <summary>
    ///客户地址簿,领域对象/// </summary>
    [Table("T_CustomerAddress")]public class CustomerAddress : CreationAuditedEntity<string>{/// <summary>
        ///默认构造函数(需要初始化属性的在此处理)/// </summary>
        publicCustomerAddress()
{
}
#region Property Members /// <summary> ///客户ID/// </summary> //[Required] public virtual string Customer_ID { get; set; }/// <summary> ///省份/// </summary> //[Required] public virtual string Province { get; set; }/// <summary> ///城市/// </summary> //[Required] public virtual string City { get; set; }/// <summary> ///区县/// </summary> //[Required] public virtual string District { get; set; }/// <summary> ///详细地址/// </summary> //[Required] public virtual string DetailAddress { get; set; }/// <summary> ///排序/// </summary> //[Required] public virtual string SortCode { get; set; }/// <summary> ///客户ID/// </summary> //[Required] [ForeignKey("Customer_ID")]public virtual Customer Customer { get; set; }#endregion}

映射文件如下所示。

    /// <summary>
    ///客户信息,映射文件/// </summary>
    public classCustomerMapProfile : Profile  
{
publicCustomerMapProfile()
{
CreateMap
<CustomerDto, Customer>();
CreateMap
<Customer, CustomerDto>();
CreateMap
<CreateCustomerDto, Customer>();
}
}
    /// <summary>
    ///客户地址簿,映射文件/// </summary>
    public classCustomerAddressMapProfile : Profile  
{
publicCustomerAddressMapProfile()
{
CreateMap
<CustomerAddressDto, CustomerAddress>();
CreateMap
<CustomerAddress, CustomerAddressDto>();
CreateMap
<CreateCustomerAddressDto, CustomerAddress>();
}
}

然后在EFCore的上下文中添加对应的DBSet对象即可。

有了这些,基于ABP框架的基础上就可以实现数据的创建、更新提交了。

2、一对多关系的界面处理和服务端ABP接口的处理

但是主从表之间的关系,我们这里还没有详细说明,一般我们在界面处理数据的时候,主表数据可能和从表数据一起显示,编辑的时候一起保存,如下界面所示。

在编辑/新增界面中绑定GridView的相关显示和处理事件。

我们可以在新增窗口中加载空地址列表,或者编辑窗口加载已有地址列表记录

保存新增的记录如下所示。

        /// <summary>
        ///新增状态下的数据保存/// </summary>
        /// <returns></returns>
        public async override Task<bool>SaveAddNew()
{
CustomerDto info
= tempInfo;//必须使用存在的局部变量,因为部分信息可能被附件使用 SetInfo(info);try{#region 新增数据tempInfo= awaitCustomerApiCaller.Instance.CreateAsync(info);if (tempInfo != null)
{
//可添加其他关联操作 var list =GetDetailList();foreach(var detailInfo inlist)
{
awaitCustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
}
return true;
}
#endregion}catch(Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
return false;
}

其中GetDetailList是获取编辑状态下的数据记录

        /// <summary>
        ///获取明细列表/// </summary>
        /// <returns></returns>
        private List<CustomerAddressDto>GetDetailList()
{
var list = new List<CustomerAddressDto>();for (int i = 0; i < this.gridView1.RowCount; i++)
{
var detailInfo = gridView1.GetRow(i) asCustomerAddressDto;if (detailInfo != null)
{
list.Add(detailInfo);
}
}
returnlist;
}

如果数据更新的时候,操作也是类似

        /// <summary>
        ///编辑状态下的数据保存/// </summary>
        /// <returns></returns>
        public override async Task<bool>SaveUpdated()
{
CustomerDto info
= awaitCustomerApiCaller.Instance.GetAsync(ID);if (info != null)
{
SetInfo(info);
try{#region 更新数据tempInfo= awaitCustomerApiCaller.Instance.UpdateAsync(info);if (tempInfo != null)
{
//可添加其他关联操作 var list =GetDetailList();foreach(var detailInfo inlist)
{
awaitCustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
}
return true;
}
#endregion}catch(Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
}
return false;
}

我们这里注意到不管更新还是插入地址记录,都用到了一个函数InsertOrUpdateAsync,这个是我们后台判断记录是新增或者更新,在写入数据库操作中的处理函数。

这个函数比较通用,我们可以考虑把它写入公用的基类接口里面即可。

同样,客户端的ApiCaller调用,也需要进行一个简单的基类接口增加即可。

有了这些支持,Winform客户端的处理就可以直接调用这些简单的接口进行主从表的数据提交了。

    //可添加其他关联操作
    var list =GetDetailList();foreach(var detailInfo inlist)
{
awaitCustomerAddressApiCaller.Instance.InsertOrUpdateAsync(detailInfo);
}

另外,除了这种细粒度的接口处理,我们还可以把整个DTO对象包装一下,在主表DTO对象中包含从表明细列表,然后重写Create、Update的服务端应用服务层接口,接收从表明细,然后一个接口就可以处理主从表的数据保存或者更新了。

具体如何选择数据处理的方式,需要根据业务的场景进行衡量。

3、结合代码生成工具实现后台代码和主从表界面代码的快速生成

一旦业务规则确定,我们可以运用代码生成工具来提高开发效率了。由于主从表关系的处理比较统一,因此我们可以按照他们的关系以及界面常见的处理方式来生成这些内容。

首先,我们打开代码生成工具,展开对应数据库的表信息,如下界面所示。

选择ABP框架代码生成,可以生成后台框架代码,其中包括DTO实体、实体对象、映射文件,服务端应用层接口和实现等内容。

生成Winform主从表界面的时候,选择Winform代码生成,如下界面所示。

然后在弹出的界面里面选择主从表界面的生成选项卡即可。

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

循序渐进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 前端应用开发(19)--- 后端查询接口和Vue前端的整合

循序渐进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)

ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)

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

前阵子对电商商品及其相关的内容很感兴趣,总有一种不弄明白不罢休的冲劲。因此整整花了几周的时间来了解电商商品的各种概念,参考看不同的人数据库设计,以及参考不同的思路。网上确实有很多文章对这方面进行介绍,而且基础、通用的概念都差不多,不过数据库设计方向倒是有所差异。本篇随笔针对一些介绍比较好的文章或者资料进行对比,并着手进行一定的数据库设计和基于ABP框架+Vue+Element的后台管理进行设计,后续可能会基于商品、会员、订单、售后的基础上进行一个多端的系统整合,如ABP后端API、Vue+Element的后台管理、UniApp小程序或H5公众号进行开发。

1、参考部分优秀的做法

关于电商商品的数据库设计,介绍比较详细的文章有:
https://blog.csdn.net/qq_37457564/article/details/101289358
,或者
https://www.cnblogs.com/cnblogs-programs/articles/5651557.html

https://blog.csdn.net/thc1987/article/details/80426063

http://www.360doc.com/content/18/0124/11/40769523_724661250.shtml

https://www.cnblogs.com/eggTwo/p/6404805.html

这些设计理念都一致,另外关于概念的介绍,这篇随笔:
http://www.woshipm.com/pd/2122609.html

https://www.sohu.com/a/341684657_114819
也介绍的很详细。

关于电商商品的分类,我看基本上是分为三类,包括京东、淘宝商品类别,也都基本按照这样分级处理。关于商品的类别分类,我们可以参考一篇文档的介绍《
电商商城产品三级分类
》,这个文档挺好,不过没有办法获得Word格式的文档用来参考。另外我们可以参考《
国美的电商商品分类
》,国美的商品分类很好,可以用作我们前期商品的类别数据。

后来,无意间看到了《
商派ONex在线零售产品操作手册
》,这个文档的概念各方面感觉更精确,而且这个公司好像一直从事电子商务方面的咨询、软件开发等事宜,其中它对商品类型和商品分类的分开,我觉得更加贴切,毕竟商品的分类可以更加弹性化一些或自定义组合一些特殊的类别。

因此综合这些考虑,把整体的概念梳理一遍,着手做一下电商商品的数据库设计和功能界面的管理开发了。

在开始之前,我们先来参考一下电商领域的一些概念。

商品分类

商品分类,俗称商品类别、商品目录,指的是为了方便顾客分门别类查找商品,同时方便商家进行商品管理的分类方式。

虚拟分类

在原商品分类基础上,依据商品的品牌、属性、价格等条件筛选而形成的新分类方式,例如200—300元的商品,女性滑盖手机等分类。

商品类型

商品类型不同于商品分类,指的是依据某一类商品的相同属性归纳成的属性集合,例如手机类型都有屏幕尺寸、铃声、网络制式等共同的属性;书籍类型都有出版社、作者、ISBN号等共同的属性。

通用商品类型

系统内置的仅含有商品名、重量、销售价格、简介、库存、品牌等基本属性的一种商品类型。

商品规格

是依据顾客的购买习惯而独立出来的一种商品的特殊属性,例如顾客先选好了某一款衬衫,然后必须再选择颜色和尺码才可以订购,这里的颜色和尺码被称为规格。

商品关键词

商品关键词是商品名称的有效补充,可以实现更多的搜索结果匹配机会,如:索尼爱立信W910i手机中设 置商品关键词“索爱W910i”,则用户搜索“索爱W910i”也可以找到这款手机。

版块

商店前台面页的不同区域,例如特价商品、销售排行榜、最新发货清单等,商家可以在后台的模版编辑中进行版块设置来修改前台表现样式。

市场价

顾客购买商品时的参考价格,不作为购买支付价格。

销售价

是普通顾客在商店中购买商品的结算价格。

会员价

顾客注册成为商店的会员之后,购买商品所享受的价格。商家针对同一商品可以根据会员等级不同,设置不同的价格。

商品配件

是与此商品出现在同一个页面并且可同时购买的其他商品,如:购买诺基亚N95,可同时购买手机电池、内存卡、蓝牙耳机等配件。

相关商品

商家为了促进其它商品的销售而将其显示在当前商品的页面上,这些商品就叫做相关商品。

商品

在系统中,商品是一个销售单位,在前台表现为一个商品详细页。

货品

在系统中,货品与商品不相同,货品是一个库存单位,例如“索爱W910i”是一个商品,但红色的“索爱W910i”是一个货品,黑色的是另一个货品。

货号

是货品的唯一编号,可用于仓库管理。

商品编号

商品的唯一编号,可用于商店前台的商品检索,一般使用数字编号,方便电话订购。

标签

是一种分组标识,可用于商品、订单,店主可以利用标签筛选分组,如:为某几个商品增加“热卖商品”的标签,可以通过板块设置,让这几个商品显示在前台首页的热卖商品区。

SPU:即标准化产品单位,是最接近用户认知的产品单元。比如iphone6、iphone4、小米4都是SPU。

SKU:即库存量单位,例如有iphone6这个SPU,当用户购买时要确定买什么颜色的、内存多大的、支持什么网络等等。就用库存单元SKU去规范它。库存里存在的东西是具体某种规格的,不同的颜色、版本、容量肯定有不同的价格和不同的SKU。

2、数据库设计

电商商品有品牌、商品分类、商品类型、规格分组、规格参数、规格参数选项值、商品SPU、货品SKU等等概念对象,我依照上面的一些设计思路,整合了这些概念,大概有如下的设计关系图。

其中的关系看起来很多,不过总体就那么些概念。这里我吸纳了一位仁兄说把规格和参数作为一个表设计,用标志字段分开的思路。

商品参数(有些人叫商品规格参数)信息如下所示,一般可以分为分组、属性及属性值。这些信息不影响SKU,只是作为商品的一些参数展示。

另外一些参数影响SKU的信息,可以认为是特殊的规格参数,如下所示。

我们选择不同的颜色、版本等规格,可能影响我们SKU的记录,也就是对应的销售价格和库存量。

其中商品品牌、商品列表比较独立,但是商品规格及规格值等信息设计和商品类型关联,从而影响商品信息。

商品其实设计的概念不少,不过都是为了使得数据更加有规律,实现更好的弹性设计。从商品管理扩展出去,还会设计到会员和积分管理相关信息,也是一个不小的设计领域,另外还有设计到订单管理,也是一个大的体系,但是商品是其中的关键,也是很多管理的开始。

3、软件界面的设计

针对商品的管理,主要就是后台数据的管理,前端界面的展示,一般就是电商领域的商品销售了,如可以结合小程序、公众号、官网等方式展示商品进行销售。

我们这里先对商品管理的界面进行设计,其中包括了商品品牌、商品分类、商品规格分组、规格定义、规格选项及它们之间的关系等功能的处理。

按照我们的功能规划,我们定义好以下的菜单

1)品牌管理

其中品牌管理界面如下所示。

品牌编辑或者新增界面如下

品牌信息相对独立,没有和其他模块表之间有直接关系,那么只需要维护他的基础数据和相关的图片信息即可。

2)分类管理

分类设计是一个无穷级的树列表,一般电商商品类别分为三类,我们可以通过左侧树列表快速定位,分类列表界面如下所示。

电商的类别比较多,一个个录入肯定有点麻烦,我就弄了一个快速的批量新增处理。

分类直接从
国美商品分类
中复制过来即可,非常方便。

其中,我们可以根据商品类别的分级层次,来自动构建分级编码,方便以后根据编码直接定位商品分类的一级、二级、三级信息。

3)商品类型

前面介绍过了,商品类型不同于商品分类,指的是依据某一类商品的相同属性归纳成的属性集合,例如手机类型都有屏幕尺寸、铃声、网络制式等共同的属性;书籍类型都有出版社、作者、ISBN号等共同的属性。

其实这里商品类型和品牌有多对多的关系。

也和规格分组和规格参数和规格选项有相关的关系。

功能界面设计的时候,就需要考虑和这些表之间的关系维护,如基本信息里面和品牌关系进行绑定。

以及商品规格里面的规格及规格列表的维护。

规格选项可以输入图片,也可以上传图片,到时候终端根据选择显示方式进行展示即可。

另外除了影响SKU的特殊规格参数外,还有一个常规的规格参数,这里称为商品参数。商品参数按分组的方式进行管理,如下界面所示。

而其中的参数,除了设置一些选项外(如是否可查询、数值、单位等),和上面的规格类似,也是可以填写列表选项的,如下所示。

4) 商品信息

商品信息,除了维护SPU信息外,还需要管理SKU和库存信息,因此需要综合上面很多信息进行分类,商品列表界面主要是提供快速商品的检索和创建SKU记录信息的入口,商品列表如下所示。

其中商品分类,我们可以根据数据库记录进行展示并选择过滤数据。

商品创建及SKU记录信息处理,我们可以引入
hooray / vue-sku-form
组件进行信息的创建,如下界面所示。

通过不同的商品规格,如颜色、内存等生成多个不同规格的SKU记录,并设置对应的价格和库存信息。

以上就是关于电商商品的一些数据库设计和功能界面的截图,主要就是用来理清各个电商商品的概念,以及模块之间的关系,为后面的会员管理、订单管理等大领域进行基础的处理。

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

循序渐进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 前端应用开发(19)--- 后端查询接口和Vue前端的整合

循序渐进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)

ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)

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

ABP框架中一对多,多对多关系的处理以及功能界面的处理(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变量方式解决弹出编辑对话框界面无法触发更新的问题