循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)
在我们使用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-tree style="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" > <span slot-scope="{ node, data }" class="custom-tree-node"> <span> <i :class="node.icon ? node.icon : 'el-icon-price-tag'" /> {{ node.label }} </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 function getJsonTree(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加图标 return jsonTree; }
这样树列表的绑定操作如下代码所示。
getTree() { // 树列表数据获取 var param = { SkipCount: 0, MaxResultCount: 1000 } category.GetAll(param).then(data => { this.treedata = [];// 树列表清空 var list = data.result.items if (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-button type="primary">主要按钮</el-button> <el-button type="success">成功按钮</el-button> <el-button type="info">信息按钮</el-button> <el-button type="warning">警告按钮</el-button> <el-button type="danger">危险按钮</el-button> </el-row> <el-row> <el-button plain>朴素按钮</el-button> <el-button type="primary" plain>主要按钮</el-button> <el-button type="success" plain>成功按钮</el-button> <el-button type="info" plain>信息按钮</el-button> <el-button type="warning" plain>警告按钮</el-button> <el-button type="danger" plain>危险按钮</el-button> </el-row> <el-row> <el-button round>圆角按钮</el-button> <el-button type="primary" round>主要按钮</el-button> <el-button type="success" round>成功按钮</el-button> <el-button type="info" round>信息按钮</el-button> <el-button type="warning" round>警告按钮</el-button> <el-button type="danger" round>危险按钮</el-button> </el-row> <el-row> <el-button icon="el-icon-search" circle></el-button> <el-button type="primary" icon="el-icon-edit" circle></el-button> <el-button type="success" icon="el-icon-check" circle></el-button> <el-button type="info" icon="el-icon-message" circle></el-button> <el-button type="warning" icon="el-icon-star-off" circle></el-button> <el-button type="danger" icon="el-icon-delete" circle></el-button> </el-row>
我在之前的界面中采用的按钮样式如下所示。
后来我觉得把按钮组合起来更好看,也更节省空间。
这里其实就是使用了按钮组的作用。
<!--功能操作按钮 --> <el-row style="float:right;padding-bottom:10px"> <el-button-group> <el-button icon="el-icon-search" type="primary" size="mini" round @click="search()">查询</el-button> <el-button icon="el-icon-refresh-left" type="warning" size="mini" round plain @click="resetSeachForm('searchForm')">重置</el-button> </el-button-group> <el-button-group> <el-button icon="el-icon-plus" type="success" size="mini" round @click="showAdd()">添加</el-button> <el-button icon="el-icon-document-remove" type="danger" size="mini" round :disabled="listSelection.length === 0" @click="batchDelete()">批量删除</el-button> </el-button-group> <el-button-group> <el-button icon="el-icon-upload2" type="warning" size="mini" round @click="showImport()">导入</el-button> <el-button icon="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-column prop="date" label="日期" width="180"> </el-table-column> <el-table-column prop="name" label="姓名" width="180"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> </el-table> </template>
但往往我们处理表格列的各种内容承载的时候,需要丰富很多,如包括标签、Switch开关,按钮操作、日期格式化、内容转换等操作。
如标签列,可以进行循环处理。
<el-table-column align="center" label="标签" width="100"> <template slot-scope="scope"> <el-tag v-for="tag in scope.row.tags.split(',')" :key="tag" type="primary" > {{ tag }} </el-tag> </template> </el-table-column>
或者结合过滤器进行处理。
<el-table-column align="center" label="状态"> <template slot-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-column align="center" label="创建时间" width="160" prop="creationTime" :formatter="timeFormat" />
表格的操作列,整个对单个记录操作的各种操作入口。
<!--表格行操作按钮 --> <el-table-column align="center" label="操作" width="110"> <template scope="scope"> <el-row> <el-button-group> <el-tooltip effect="light" content="查看" placement="top-start"> <el-button icon="el-icon-search" type="success" circle size="mini" @click="showView(scope.row.id)" /> </el-tooltip> <el-tooltip effect="light" content="编辑" placement="top-start"> <el-button icon="el-icon-edit" type="primary" circle size="mini" @click="showEdit(scope.row.id)" /> </el-tooltip> <el-tooltip effect="light" content="删除" placement="top-start"> <el-button icon="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-column align="center" label="状态"> <template slot-scope="scope"> <el-tag :type="scope.row.status==0 ? 'success' : 'warning'" effect="dark"> {{ scope.row.status==0 ? '正常' : '停用' }} </el-tag> <el-switch v-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 = true import('@/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 { return v[j] } })) },
一般来说,Element的表格控件已经具有非常丰富的功能了,包括固定行列、展开行、懒加载数据、多级表头、排序筛选、自定义列、合计行列等操作,
如果有需要直接编辑表格数据的操作,建议可以采用第三方表格组件来实现:https://github.com/huangshuwei/vue-easytable
10、树形下拉列表框及封装
除了常规的树列表展示内容外,我们也需要一个在下拉列表中展示树内容的界面组件。
这里又得引入一个第三方的界面组件,因此Element的Select组件不支持树列表。
GitHub地址:https://github.com/riophae/vue-treeselect
官网地址:https://vue-treeselect.js.org/
常规的界面代码如下使用
<treeselect v-model="value" :multiple="true" :options="options" />
复杂一点的使用界面代码,可以指定更多的属性设置,以及处理事件操作。
<treeselect v-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-item label="父项名称" prop="pid"> <MyTreeselectTop v-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-chart ref="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 前端应用开发(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)--- 邮件参数配置和模板邮件发送处理
使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理
使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题
转载请注明出处:撰写人:伍华聪 http://www.iqidi.com