el-select封装(单选框、多选框、全选功能)
先看看设计图:
网上找了一溜,都是扯淡,样式也没个
自己动手吧,先把样式搞定
popper-class="xx-option"
所有单选框都用 :after和:before类 + 定位 实现
样式逻辑复杂点,再加上:hover、:active伪类,看不惯还要封装
就出来了
.xx-option {
.el-select-dropdown__list .el-select-dropdown__item {
background-color: var(--select-bg);
color: var(--select-txt);
font-weight: 400;
padding-left: 40px;
&.hover {
background-color: var(--select-hover-bg);
color: var(--select-hover-txt);
}
&.selected {
background-color: var(--select-active-bg);
color: var(--select-active-txt);
}
&.selected.hover {
background-color: var(--select-active-hover-bg);
color: var(--select-active-hover-txt);
}
&.selected::before {
height: 10px;
width: 10px;
box-sizing: border-box;
content: "";
display: inline-block;
position: absolute;
left: 17px;
top: 50%;
transform: translateY(-50%);
background-color: var(--select-radio-bg);
border-radius: 50%;
transition: 0.2s;
}
&::after {
height: 16px;
width: 16px;
box-sizing: border-box;
position: absolute;
font-family: element-icons;
content: "";
font-size: 12px;
font-weight: 700;
left: 14px;
top: 50%;
transform: translateY(-50%);
border: 1px solid var(--select-checkbox-border);
border-radius: 50%;
text-align: center;
line-height: 16px;
transition: 0.2s;
}
&.hover::after {
border-color: var(--select-checkbox-hover-border);
background-color: transparent;
}
&.selected::after {
border-color: var(--select-checkbox-bg);
}
}
&.is-multiple .el-select-dropdown__list .el-select-dropdown__item {
&::after {
border-radius: 0;
}
&.selected::after {
content: "\E6DA";
color: #fff;
background-color: var(--select-checkbox-bg);
}
&.hover.selected::after {
border-color: var(--select-checkbox-bg);
background-color: var(--select-checkbox-bg);
}
}
}
用看的肯定是看不懂的,要不就直接拿走换颜色用,要不就动动小手自己敲一遍,再比对一番~
接下来是全选功能,先看看代码
组件
<el-select
ref="selector"
popper-class="xx-option"
v-model="selectValue"
v-bind="$attrs"
v-on="$listeners"
:multiple="multiple"
collapse-tags
>
<div
v-if="multiple"
class="el-select-dropdown__item"
@click="onAllClick"
@mouseenter="onAllEnter"
@mouseleave="hoverAll = false"
:class="{
selected: selectedAll,
hover: hoverAll
}"
>
<span>全选</span>
</div>
<el-option
v-for="(item, key) in options"
:key="key"
:label="item[labelKey]"
:value="item[valueKey]"
>
</el-option>
</el-select>
el-options写在了封装组件内,也是因为全选功能的局限性导致
在调用组件时,要传展示的labelKey
和取值的valueKey
事件
onAllClick() {
this.selectedAll = !this.selectedAll;
// 选中全选
if (this.selectedAll) {
if (this.selectValue.length < this.options.length) {
this.selectValue = !this.valueKey
? this.options
: this.options.map(item => item[this.valueKey]);
}
} else {
this.selectValue = [];
}
this.$emit("change", this.selectValue);
},
onAllEnter() {
this.hoverAll = true;
const options = this.$refs.selector.options;
this.$nextTick(() => {
this.$refs.selector.options = options.map(item => {
item.hover = false;
return item;
});
});
this.$refs.selector.hoverIndex = -1;
},
用v-bind和v-on接受所有的参数和事件,这里就有点繁琐了
对比vue3 :=$attrs
属性逻辑都搞定
v-bind的参数,如果已经通过props传了过来,那就不会出现在this.$attrs
里面
另外$attrs拿到的是个对象{string: string},对Boolean类型的数据不太友好
v-on就不像v-bind,可以存在同名的事件emit出去,自己组件本身和v-on的事件都能调用到(其实也是坑)
另外,要想调用自身事件的而不用组件的,需要加上修饰符.native
(扯远了..)
onAllClick
就是全选和反选功能,再emit一个change
事件
那为什么要有onAllEnter
事件呢,样式有问题
看了下源码,ele对option的鼠标移入事件添加了个hoverIndex
状态来记录
需要手动处理一下,重置hoverIndex
和option组件内的hover
属性
功能完成,基本看得过去,处理一些数据绑定的小bug
@visible-change="onVisibleChange"
onVisibleChange(visible) {
// 验证多选全选
this.selectedAll =
visible &&
this.multiple &&
this.selectValue.length >= this.options.length;
},
数据一进来,先判断是否全部选择,勾上全选按钮
每次数据change,除了绑定到外层v-model上,再加个全选判断
computed: {
selectValue: {
get() {
return this.value;
},
set(val) {
this.selectedAll = this.multiple && val.length >= this.options.length;
this.$emit("input", val);
}
}
},
最后就是一些小细节,hover边框,active边框
上完整代码!!
<template>
<div
:class="[
'xx-select',
focusing && 'xx-select-focus',
!title && 'xx-select-no-title'
]"
>
<span class="title" v-if="title">{{ title }}</span>
<el-select
ref="selector"
class="select"
popper-class="xx-option"
v-model="selectValue"
v-bind="$attrs"
v-on="$listeners"
:multiple="multiple"
:placeholder="placeholder"
:clearable="clearable"
collapse-tags
@visible-change="onVisibleChange"
@blur="blur"
@focus="focus"
>
<div
v-if="multiple"
class="el-select-dropdown__item"
@click="onAllClick"
@mouseenter="onAllEnter"
@mouseleave="hoverAll = false"
:class="{
selected: selectedAll,
hover: hoverAll
}"
>
<span>全选</span>
</div>
<el-option
v-for="(item, key) in options"
:key="key"
:label="labelKey ? item[labelKey] : item"
:value="valueKey ? (valueKey === '$key' ? key : item[valueKey]) : item"
>
</el-option>
</el-select>
</div>
</template>
<script>
export default {
name: "XxSelect",
props: {
title: {
type: String,
default: ""
},
placeholder: {
type: String,
default: ""
},
value: {
type: [String, Number, Object, Array],
required: true
},
multiple: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: true
},
options: {
type: [Array, Object, Number],
default: () => {
return [];
}
},
// valueKey
// - 不传则为整个item赋值
// - 传`$key` 返回index(array)或key(object)
valueKey: {
type: String
},
labelKey: {
type: String
}
},
data() {
return {
focusing: false,
hoverAll: false,
selectedAll: false
};
},
computed: {
selectValue: {
get() {
return this.value;
},
set(val) {
this.selectedAll = this.multiple && val.length >= this.options.length;
this.$emit("input", val);
}
}
},
methods: {
onAllEnter() {
this.hoverAll = true;
const options = this.$refs.selector.options;
this.$nextTick(() => {
this.$refs.selector.options = options.map(item => {
item.hover = false;
return item;
});
});
this.$refs.selector.hoverIndex = -1;
},
onAllClick() {
this.selectedAll = !this.selectedAll;
// 选中全选
if (this.selectedAll) {
if (this.selectValue.length < this.options.length) {
this.selectValue = !this.valueKey
? this.options
: this.options.map(item => item[this.valueKey]);
}
} else {
this.selectValue = [];
}
this.$emit("change", this.selectValue);
},
onVisibleChange(visible) {
// 验证多选全选
this.selectedAll =
visible &&
this.multiple &&
this.selectValue.length >= this.options.length;
},
focus() {
this.focusing = true;
this.$refs.selector.focus();
},
blur() {
this.focusing = false;
}
}
};
</script>
<style lang="scss" scoped>
.xx-select {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
width: 24%;
box-sizing: border-box;
padding-left: 16px;
border: 1px solid var(--default-border);
border-radius: 4px;
background-color: #fff;
overflow: hidden;
&:hover {
border-color: var(--default-hover-border);
}
&-focus {
border-color: var(--default-active-border) !important;
}
&-no-title {
padding-left: 0px;
}
.title {
flex: 2;
font-size: 16px;
color: #333;
}
.select {
flex: 5;
height: 100%;
:deep(.el-select__tags) {
margin-top: 1px;
}
:deep(.el-input--suffix) {
line-height: 1;
.el-input__inner {
border: none;
}
.el-input__icon {
line-height: 1;
}
.el-select__caret:not(.el-icon-circle-close) {
transform: rotateZ(90deg);
&::before {
content: "\E6E1";
}
}
&.is-focus {
.el-select__caret:not(.el-icon-circle-close) {
transform: rotateZ(0deg);
}
}
}
}
}
.xx-option {
.el-select-dropdown__list .el-select-dropdown__item {
background-color: var(--select-bg);
color: var(--select-txt);
font-weight: 400;
padding-left: 40px;
&.hover {
background-color: var(--select-hover-bg);
color: var(--select-hover-txt);
}
&.selected {
background-color: var(--select-active-bg);
color: var(--select-active-txt);
}
&.selected.hover {
background-color: var(--select-active-hover-bg);
color: var(--select-active-hover-txt);
}
&.selected::before {
height: 10px;
width: 10px;
box-sizing: border-box;
content: "";
display: inline-block;
position: absolute;
left: 17px;
top: 50%;
transform: translateY(-50%);
background-color: var(--select-radio-bg);
border-radius: 50%;
transition: 0.2s;
}
&::after {
height: 16px;
width: 16px;
box-sizing: border-box;
position: absolute;
font-family: element-icons;
content: "";
font-size: 12px;
font-weight: 700;
left: 14px;
top: 50%;
transform: translateY(-50%);
border: 1px solid var(--select-checkbox-border);
border-radius: 50%;
text-align: center;
line-height: 16px;
transition: 0.2s;
}
&.hover::after {
border-color: var(--select-checkbox-hover-border);
background-color: transparent;
}
&.selected::after {
border-color: var(--select-checkbox-bg);
}
}
&.is-multiple .el-select-dropdown__list .el-select-dropdown__item {
&::after {
border-radius: 0;
}
&.selected::after {
content: "\E6DA";
color: #fff;
background-color: var(--select-checkbox-bg);
}
&.hover.selected::after {
border-color: var(--select-checkbox-bg);
background-color: var(--select-checkbox-bg);
}
}
}
</style>
options的label和value为啥这么复杂呢,主要是为了通用
支持数组、对象、数字
valueKey传$key
可拿到key(对象)、index(数组、数字)
不传valueKey,则获取整个"item"
试试就明白了
给两个调用示例
<xx-select
:ref="`productCode${index}`"
v-model="product.productCode"
filterable
remote
:remote-method="
$event =>
onFilterProduct(product.typeCode, 'productCode', $event)
"
:loading="productLoading"
@visible-change="onProductVisibleChange($event, product.typeCode)"
@change="onProductChange($event, 'productCode', index)"
:options="products"
valueKey="productCode"
labelKey="productName"
/>
<xx-select v-model="product.priority" :options="9" />
好的。
el-select封装(单选框、多选框、全选功能)的更多相关文章
- jQuery 复选框全选/取消全选/反选
jQuery实现的复选框全选/取消全选/反选及获得选择的值. 完整代码: <!DOCTYPE html> <html> <head> <script type ...
- Jquery CheckBox复选框 全选/取消全选 最佳实现方式 参考案例
<input id="chkAll" type="checkbox" />全选/取消全选</div> <asp:Repeater ...
- vue实现功能 单选 取消单选 全选 取消全选
vue实现功能 单选 取消单选 全选 取消全选 代码部分 <template> <div class=""> <h1>全选框</h1> ...
- 让前端的下拉框支持单选、多选及全选,后台MyBaits解决方案
目录 一.解决思路 二.请求参数 三.后台相关代码 四.Mybatis注意要点 一.解决思路 让前端的下拉框支持单选.多选及全选,后台让Mybatis使用** trim **标签拼接动态SQL,实 ...
- jQuery--checkbox全选/取消全选
用JavaScript使页面上的一组checkbox全选/取消全选,逻辑很简单,实现代码也没有太难的语法.但使用jQuery实现则更简单,代码也很简洁,精辟! jQuery版本:1.3.2 <h ...
- vue多级复杂列表展开/折叠,全选/分组全选实现
首先,来看下效果图 在线体验地址:https://hxkj.vip/demo/multipleList/.温馨提示,打开之后按F12,使用手机模式食用,口味更佳! 可以看出,这个列表有三种展现形式: ...
- checkbox全选/取消全选
//checkbox全选/取消全选 $(function() { $("#checkAll").click(function() { if(this.checked){ $(&qu ...
- 关于在repeater中的checkbox实行多选和全选
今天项目中用到这一块,是一个b2b商城,业务是别人给客户留言后,客户从会员中心的留言管理中查看,用checkbox实行多选和全选后进行批量审核 首先在checkbox后加个hidden,作用见代码: ...
- AngularJS--购物车全选/取消全选功能实现
刚学习angularJS,于是练习写了一个类似于购物车的全选/取消全选的功能,主要实现的功能有: 1.勾选全选checkbox,列表数据全部被勾选,取消同理,用ng-model实现双向绑定: 2.选中 ...
- html checkbox 实现全选/取消全选
html checkbox 实现全选/取消全选 <html> <body> <table border="1"> <tr> < ...
随机推荐
- 爱了爱了!推荐一个Github 70k+点赞的Java学习指南!
大家好,我是 Guide 哥!今天给大家推荐一个非常不错的 Java 教程类开源项目-JavaGuide ,Github 地址: https://github.com/Snailclimb/JavaG ...
- OCR文字检测与识别系统:融合文字检测、文字识别和方向分类器的综合解决方案
1. OCR文字检测与识别系统:融合文字检测.文字识别和方向分类器的综合解决方案 前两章主要介绍了DBNet文字检测算法以及CRNN文字识别算法.然而对于我们实际场景中的一张图像,想要单独基于文字检测 ...
- 3.2 Windows驱动开发:内核CR3切换读写内存
CR3是一种控制寄存器,它是CPU中的一个专用寄存器,用于存储当前进程的页目录表的物理地址.在x86体系结构中,虚拟地址的翻译过程需要借助页表来完成.页表是由页目录表和页表组成的,页目录表存储了页表的 ...
- C/C++ 使用CRC检测内存映像完整性
前面的那一篇文章中所使用的技术只能有效抵抗解密者直接修改硬盘文件,当我们使用动态补丁的时候,那么内存中同样不存在校验效果,也就无法抵御对方动态修改机器码了,为了防止解密者直接对内存打补丁,我们需要在硬 ...
- 内存池是什么原理?|内存池简易模拟实现|为学习高并发内存池tcmalloc做准备
前言 那么这里博主先安利一些干货满满的专栏了! 这两个都是博主在学习Linux操作系统过程中的记录,希望对大家的学习有帮助! 操作系统Operating Syshttps://blog.csdn.ne ...
- Sea-Search03总结&&un finish
使用到的设计模式 Facade门面模式 为何使用? 在搜索项目中,由于使用Mvc架构且数据库中各种不同类型的数据源并没有放在同一张表,于是我们不可避免的在Controller中需要注入多个servic ...
- Hadoop的stop-all无法关闭集群原因及解决方案
问题现象:在服务器上长时间运行hadoop之后,如果运行stop-all.sh,会发现: [root@node1 sbin]# stop-all.shThis script is Deprecated ...
- C++文件输入输出的简单实现(Debug)
1.前言: 文件输入输出是个很有用的东西,有时比赛时要有:要求使用文件输入输出,还有时候-- 遇到这种时间限制非常恶心的题目:手动测试会有误差-- 文件输入输出是个很好的选择! 2.写法: C C语言 ...
- Skywalking-Aop Docker单机环境搭建
1.OAP-SERVER和UI环境搭建 本次搭建是基于MySQL进行持久化,因此需要提前准备好一个MySQL容器 (MySQL容器部署略过).如有错误还请指正. 1.1 OAP服务搭建 拉取skywa ...
- linux如何发送电子邮件
使用linux时,有时我们想发邮件给朋友或同事,可不可以通过命令行直接发呢? 想通过linux监控网站或者系统状况并自动报警,如何使用脚本发出邮件给外部邮箱呢? 不 ...