使用VUE自定义组件封装部门选择功能
背景
照惯例,先交待下背景,从真实需求出发,讲述实现效果、设计思路和实现方式。
软件系统中,会有一些常见常用的选择功能,如部门选择、人员选择等,用于填报表单,使用频率很高。直接使用一方面会比较繁琐,另一方面造成代码重复,当需要调整时,则需要遍历整个项目源码,容易因漏改引发问题。这种情况下,更好的实现方案,是通过封装组件,来实现简化使用和复用的目的。
前面有一篇,介绍了使用VUE自定义组件封装数据字典,功能比较简单,今天进一步,封装一个比较复杂的部门单选组件,其他业务实体,如人员、角色等,实现非常类似,就不再赘述。
实现效果
首先展示下实现效果。
用户管理列表,左边是部门树,右侧是用户列表,如下图。
点击“新增”按钮,打开用户编辑页面,自动显示传入的部门。
第一行就是我们封装的部门单选组件,点击最右侧的图标,弹出部门选择页面来。
选择新部门后,确定,即可实现部门的变更。
站在使用的角度看,也非常简单,用户编辑页面整体源码如下
<template><el-dialog :title="title" :visible="visible" @close="visible=false"><el-formref="form":model="entityData":rules="rules"label-width="80px"label-position="right"style="width:90%;margin:0px auto;"><!--表单区域 --><el-form-item label="组织机构" prop="organizationId"><organization-single-selectv-model="entityData.organizationId"/></el-form-item><el-form-item label="账号" prop="account"><el-input v-model="entityData.account" /></el-form-item><el-form-item label="姓名" prop="name"><el-input v-model="entityData.name" /></el-form-item><el-form-item label="职务" prop="position"><el-input v-model="entityData.position" /></el-form-item><el-form-item label="性别" prop="gender"><dictionary-radio-groupv-model="entityData.gender":code="constant.DICTIONARY_TYPE_CODES.GENDER"/></el-form-item><el-form-item label="出生日期" prop="birthday"><el-date-pickerv-model="entityData.birthday"value-format="yyyy-MM-dd HH:mm:ss"format="yyyy年MM月dd日"type="date"placement="bottom-end"placeholder="请选择"class="form-item"/></el-form-item><el-form-item label="手机号" prop="telephone"><el-input v-model="entityData.telephone" /></el-form-item><el-form-item label="邮箱地址" prop="email"><el-input v-model="entityData.email" /></el-form-item><el-form-item label="状态" prop="status"><dictionary-radio-groupv-model="entityData.status":code="constant.DICTIONARY_TYPE_CODES.STATUS"/></el-form-item><el-form-item label="排序号" prop="orderNo"><el-input v-model="entityData.orderNo" /></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button v-show="saveButtonVisible" type="primary" @click="save">保存</el-button><el-button @click="close">关闭</el-button></div></el-dialog>
</template>
,引入部门单选组件后,具体使用的时候,只需要如下代码即可
<el-form-item label="组织机构" prop="organizationId"><organization-single-selectv-model="entityData.organizationId"/></el-form-item>
也就是说,封装组件后,使用部门单选功能,跟使用一个文本输入框类似,所有与之相关的页面展现和逻辑处理都在组件内部实现了,是不是对使用方非常友好?
设计与实现
技术栈采用的还是vue2.0,UI组件库使用element ui。
使用简单,是因为将复杂工作封装在了内部,这里同样用组件化的思路,将部门单选组件拆分成了两个vue页面,一个是整体组件,负责文本框的展示,对弹出部门选择页面的调度,另一个是负责具体的部门选择。
先附上整体组件源码
<template><div><el-input v-model="organizationName" disabled><el-button slot="append" icon="el-icon-s-grid" @click="openModal" /></el-input><organization-modal ref="organizationModel" @ok="handleOK" /></div>
</template><script>
import OrganizationModal from './OrganizationModal'
export default {name: 'OrganizationSingleSelect',components: {OrganizationModal},model: {prop: 'value',event: 'change'},props: {width: {type: Number,default: 500,required: false},value: {type: String,default: '',required: false},disabled: {type: Boolean,required: false,default: false}},data() {return {visible: false,organizationName: ''}},watch: {value: {immediate: true,handler: 'handleValue'}},methods: {openModal() {this.$refs.organizationModel.show(this.value, this.organizationName)},handleValue() {if (this.value) {this.$api.system.organization.get(this.value).then((res) => {this.organizationName = res.data.name})}},handleOK(id) {// 更新父组件绑定值this.$emit('change', id)}}
}
</script><style scoped>
</style>
下面重点说下封装需要注意的点。
1.UI元素比较简单,就一个文本框,默认设置为禁用状态,并且追加了一个按钮,用于触发部门选择页面的弹出。
<el-input v-model="organizationName" disabled><el-button slot="append" icon="el-icon-s-grid" @click="openModal" /></el-input>
在这基础上当然也可以进行功能扩展,如再追加一个清空已选择的按钮,视需求而定。
2.很重要的一点,是设置model选项。因为默认情况下,model使用名为 value 的 prop 和名为 input 的事件,而我们封装的效果是选择控件,将文本框禁用了,事件应该使用chang而不是input,所以需要做如下设置:
model: {prop: 'value',event: 'change'}
3.为了组件的可配置性,设置了部分prop属性,如宽度、是否禁用等,这样在使用的时候,就能通过属性绑定的方式灵活配置了。
width: {type: Number,default: 500,required: false},value: {type: String,default: '',required: false},disabled: {type: Boolean,required: false,default: false}
4.通过vue的watch机制,监视value的变化,该值变化后,调用后端部门服务接口,拿到部门名称后更新显示。
watch: {value: {immediate: true,handler: 'handleValue'}},……handleValue() {if (this.value) {this.$api.system.organization.get(this.value).then((res) => {this.organizationName = res.data.name})}}
5.选择项变化时,通过change事件,调用emit,把最新的值传递给使用方,这一步很关键。
change(value) {this.$emit('change', value)}
接下来看下部门选择页面的实现,完整源码如下:
<template><div><el-dialog title="组织机构——单选" :visible="visible" width="400px" append-to-body @close="close"><el-input v-model="searchValue" placeholder="请输入关键字过滤" style="margin-bottom:10px" /><el-tag>当前机构:{{ selectedName }}</el-tag><el-treeref="tree":data="treeData"node-key="id":default-expanded-keys="defaultExpandedKeys":filter-node-method="filterNode"@current-change="handleTreeSelectChange"/><div slot="footer" class="dialog-footer"><el-button type="primary" @click="confirm">确定</el-button><el-button @click="close">关闭</el-button></div></el-dialog></div>
</template><script>export default {data() {return {visible: false,treeData: [],searchValue: '',defaultExpandedKeys: [],selectedValue: '',selectedName: ''}},watch: {searchValue(value) {this.$refs.tree.filter(value)}},methods: {show(id, name) {this.searchValue = ''this.defaultExpandedKeys = []this.selectedValue = idthis.selectedName = namethis.loadTree()this.visible = true},loadTree() {this.$api.system.organization.tree().then(res => {this.treeData = res.data// 默认展开根节点this.defaultExpandedKeys.push(this.treeData[0].id)// 默认展开当前节点this.defaultExpandedKeys.push(this.selectedValue)})},close() {this.visible = false},confirm() {this.$emit('ok', this.selectedValue)this.visible = false},// 树节点选中改变handleTreeSelectChange(data) {this.selectedValue = data.idthis.selectedName = data.label},filterNode(value, data) {if (!value) return truereturn data.label.indexOf(value) !== -1}}
}</script><style scoped>
</style>
具体功能包括了数据加载、默认展开、显示已选择值、搜索功能,已经可以满足常见的需求了。
多选功能的实现
上面实现了单选功能,其实多选功能实现也类似,这里只放代码,就不再展开介绍了
<template><div><el-input v-model="organizationName" disabled><el-button slot="append" icon="el-icon-s-grid" @click="openModal" /></el-input><organization-modal ref="organizationModel" @ok="handleOK" /></div>
</template><script>
import OrganizationModal from './organizationModal'
export default {name: 'OrganizationMultipleSelect',components: {OrganizationModal},model: {prop: 'value',event: 'change'},props: {value: {type: String,default: '',required: false},width: {type: Number,default: 500,required: false},disabled: {type: Boolean,required: false,default: false}},data() {return {visible: false,organizationName: ''}},watch: {value: {immediate: true,handler: 'handleValue'}},methods: {openModal() {this.$refs.organizationModel.show(this.value)},handleValue() {if (this.value) {const idList = this.value.split(',')this.$api.system.organization.getOrganization({ idList: idList }).then((res) => {this.organizationName = res.data.map(x => x.name).join(',')})}},handleOK(value) {// 处理父组件绑定值this.$emit('change', value.join(','))}}
}
</script><style scoped>
</style>
<template><div><el-dialog title="组织机构——多选" :visible="visible" width="400px" append-to-body @close="close"><el-input v-model="searchValue" placeholder="请输入关键字过滤" style="margin-bottom:10px" /><el-treeref="tree":data="treeData"node-key="id"show-checkbox:default-expanded-keys="defaultExpandedKeys":filter-node-method="filterNode":default-checked-keys="checkedNodesId"/><div slot="footer" class="dialog-footer"><el-button type="primary" @click="confirm">确定</el-button><el-button @click="close">关闭</el-button></div></el-dialog></div>
</template><script>export default {data() {return {visible: false,treeData: [],searchValue: '',defaultExpandedKeys: [],selectedValue: [],checkedNodesId: []}},watch: {searchValue(value) {this.$refs.tree.filter(value)}},methods: {show(idList) {this.searchValue = ''this.defaultExpandedKeys = []this.selectedValue = idListthis.loadTree()this.visible = true},loadTree() {this.$api.system.organization.tree().then(res => {this.treeData = res.data// 默认展开根节点this.defaultExpandedKeys.push(this.treeData[0].id)this.checkedNodesId = []this.getLeafNodeChecked(this.treeData)this.$refs.tree.setCheckedKeys(this.checkedNodesId)})},close() {this.visible = false},confirm() {this.$emit('ok', this.$refs.tree.getCheckedKeys())this.visible = false},filterNode(value, data) {if (!value) return truereturn data.label.indexOf(value) !== -1},getLeafNodeChecked(node) {// 遍历树节点,设置for (const treeNode of node) {// 如果节点有子节点,那他的选中状态不被考虑,继续往下找if (treeNode.children && treeNode.children.length > 0) {this.getLeafNodeChecked(treeNode.children)} else {// 是叶子节点,如果是check状态就记录if (this.selectedValue.includes(treeNode.id)) {this.checkedNodesId.push(treeNode.id)}}}}}
}</script><style scoped>
</style>
最后需要说一下的是,通过组件化的思想,可以将复杂功能拆分成一个个小的功能组件,降低复杂性和提高复用度。
相关文章:

使用VUE自定义组件封装部门选择功能
背景 照惯例,先交待下背景,从真实需求出发,讲述实现效果、设计思路和实现方式。 软件系统中,会有一些常见常用的选择功能,如部门选择、人员选择等,用于填报表单,使用频率很高。直接使用一方面会…...

C语言基础应用(一)数据类型
一、数据类型 1、数据类型的分类 2、常量 常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。 2.1 常量举例 // 整型常量 举例 /*718 十进制0213 八进制0x4b 十六进制30u 无符号整数30l 长整型30ul 无符号长整型*/ // 浮点常量…...

算法笔记(三)—— 桶排序及排序总结
堆 逻辑上是一棵完全二叉树(依次遍满或者全满)。 数组可以转为完全二叉树,完全二叉树某结点左孩子(2*i1),右孩子(i*22),父结点((i-1/)2),根节点的父还是自己。 如何将数组转化为堆(大根堆&…...

Linux入门篇(一)
Linux前言Linux初探Linux内核GNU实用工具shellLinux发行版bash shell 基础Linux文件系统Linux文件操作命令前言 在阅读诸如docker之类的书的时候,经常碰到Linux的知识。同时,大部分的盲区也是在Linux方面。因此就想稍微了解一下这个广为人使用的操作系统…...

HTTPSHandler SSL Error
我在服务器ubuntu中,尝试使用pip3,但是出现下面的报错 ImportError: cannot import name HTTPSHandler 通过查询资料,发现报错的原因是,该pip3.5中没有安装好openssl. 我尝试在python3.5中使用import ssl, 确实是会显示下面的报错…...
基于Android的高校食堂餐厅配送系统
需求信息: 商家客户端: 1:登录注册:用户可以通过自己的信息进行账号的注册 2:发布菜单:发布自己经营的美食信息 3:用户订单:查看用户的购买订单 4:订单配送:对…...

Java设计模式-02工厂模式
为什么需要工厂模式,其作用什么?如何实现,代码演示解析优缺点。Q1:为什么需要工厂模式?工厂模式的作用(优点)是什么? 解耦。把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的…...

AXI-Lite 学习笔记
AXI-Lite 学习笔记 参考 FPGA:AXI_Lite总线基础2-1]、第二节 AXI总线介绍、ZYNQ PL与PS交互专题_哔哩哔哩_bilibili AXI-Lite总线系列1 - 基础知识_哔哩哔哩_bilibili AXI4 介绍 AXI4 是ARM公司提出的一种片内总线,描述了主从设备之间的数据传输方式。主…...

77页智慧城市顶层设计方案
【版权声明】本资料来源网络,知识分享,仅供个人学习,请勿商用。【侵删致歉】如有侵权请联系小编,将在收到信息后第一时间删除!完整资料领取见文末,部分资料内容:篇幅有限,无法完全展…...

JavaWeb--MavenMybatis基础
JavaWeb--Maven&Mybatis基础1 Maven1.1 Maven简介1.1.1 Maven模型1.1.2 仓库1.2 Maven基本使用1.2.1 Maven 常用命令1.2.2 Maven 生命周期1.3 IDEA使用Maven1.3.1 IDEA配置Maven环境1.3.2 Maven 坐标详解1.3.3 IDEA 创建 Maven项目1.3.4 IDEA 导入 Maven项目1.4 依赖管理1.…...

博客系统--测试用例编写
目录一,整体概览1.1,登录页面测试用例1.2,注册页面测试用例1.3,发布博客功能测试1.4,删除博客功能测试二,具体设计2.1,注册页面测试--等价类法2.2,删除博客功能测试--判定表法一&…...

SpringCloud Alibaba
文章目录🚏 第十七章 SpringCloud Alibaba入门简介🚬 一、为什么使用Alibaba🚭 1、spring netflix进入维护模式🚭 Spring cloud alibaba🚬 二、如何使用?🚬 三、版本对应🚏 第十八章…...
地平线slam算法岗位 面试分享
本专栏分享 计算机小伙伴秋招春招找工作的面试经验和面试的详情知识点 专栏首页:秋招算法类面经分享 主要分享计算机算法类在面试互联网公司时候一些真实的经验 小伙伴自我介绍: 写在前面,南京某炮专,研二上阶段,简历写了两个竞赛和一个项目,一个机器人相关的二等奖,一个…...

32、基于51单片机红外智能垃圾桶系统设计
摘要 随着现代化进程的日益推进,科技越来越发达,人们的生活水平也提高了,城市化程度越来越高,与此同时也带了许多问题,生活垃圾越来越多垃圾设施却不够完善。无论是在公共场合还是家庭厨房的垃圾大都是没有盖或者有盖…...

PIL.Image与cv2之间的常用API汇总
简单介绍 主要是因为经常用到这两个,经常弄混淆,所以,总结一番。持续更新。 from PIL import Image import cv2 as cv import numpy as np import matplotlib.pyplot as plt1、读取文件与写入文件 1.1 Image.open() img_pil Image.open…...

【csdn首发】全网爆火的从零到一落地接口自动化测试
前段时间写了一系列自动化测试相关的文章,当然更多的是方法和解决问题的思路角度去阐述我的一些观点。结合我自己实践自动化测试的一些经验以及个人理解,这篇文章来聊聊新手如何从零到一落地实践接口自动化测试。 为什么要做接口测试 测试理念的演变 早…...

基于应力的拓扑优化的高效3D灵敏度分析代码(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
PMP®十万个为什么(二)
11.我的职位与项目管理并没有多大联系,PMP对我应该就没有什么价值了吧? 其实不然,首先,我们知道项目管理是一个系统性的工作,在一个企业内部如果要把项目管理的工作做好,除了项目团队的工作与管理水平不断提…...

【Linux】生产者消费者模型
🎇Linux: 博客主页:一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 看似不起波澜的日复一日,一定会在某一天让你看见坚持…...
2023/2/13 蓝桥备战acwing刷题(set的使用、简单推个不等式+差分、快速幂、01背包模板回顾、类似01背包的题)
4454未初始化警告 set计数 #include<iostream> #include<set> using namespace std;int main(){int n,m;cin>>n>>m;set<int> s;int res 0;s.insert(0);while(m--){int l,r;cin>>l>>r;if(s.count(r)0){res;}s.insert(l);}cout<…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...