先看看设计图:





网上找了一溜,都是扯淡,样式也没个

自己动手吧,先把样式搞定

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封装(单选框、多选框、全选功能)的更多相关文章

  1. jQuery 复选框全选/取消全选/反选

    jQuery实现的复选框全选/取消全选/反选及获得选择的值. 完整代码: <!DOCTYPE html> <html> <head> <script type ...

  2. Jquery CheckBox复选框 全选/取消全选 最佳实现方式 参考案例

    <input id="chkAll" type="checkbox" />全选/取消全选</div> <asp:Repeater ...

  3. vue实现功能 单选 取消单选 全选 取消全选

    vue实现功能 单选 取消单选 全选 取消全选 代码部分 <template> <div class=""> <h1>全选框</h1> ...

  4. 让前端的下拉框支持单选、多选及全选,后台MyBaits解决方案

    目录 一.解决思路 二.请求参数 三.后台相关代码 四.Mybatis注意要点 一.解决思路   让前端的下拉框支持单选.多选及全选,后台让Mybatis使用** trim **标签拼接动态SQL,实 ...

  5. jQuery--checkbox全选/取消全选

    用JavaScript使页面上的一组checkbox全选/取消全选,逻辑很简单,实现代码也没有太难的语法.但使用jQuery实现则更简单,代码也很简洁,精辟! jQuery版本:1.3.2 <h ...

  6. vue多级复杂列表展开/折叠,全选/分组全选实现

    首先,来看下效果图 在线体验地址:https://hxkj.vip/demo/multipleList/.温馨提示,打开之后按F12,使用手机模式食用,口味更佳! 可以看出,这个列表有三种展现形式: ...

  7. checkbox全选/取消全选

    //checkbox全选/取消全选 $(function() { $("#checkAll").click(function() { if(this.checked){ $(&qu ...

  8. 关于在repeater中的checkbox实行多选和全选

    今天项目中用到这一块,是一个b2b商城,业务是别人给客户留言后,客户从会员中心的留言管理中查看,用checkbox实行多选和全选后进行批量审核 首先在checkbox后加个hidden,作用见代码: ...

  9. AngularJS--购物车全选/取消全选功能实现

    刚学习angularJS,于是练习写了一个类似于购物车的全选/取消全选的功能,主要实现的功能有: 1.勾选全选checkbox,列表数据全部被勾选,取消同理,用ng-model实现双向绑定: 2.选中 ...

  10. html checkbox 实现全选/取消全选

    html checkbox  实现全选/取消全选 <html> <body> <table border="1"> <tr> < ...

随机推荐

  1. Go 1.21发布!

    原文在这里. 由Eli Bendersky, on behalf of the Go team 发布于 8 August 2023 Go团队今天非常高兴地发布了Go 1.21版本,你可以通过访问下载页 ...

  2. 【主流技术】15 分钟掌握 Redis 的安装部署和基本特性

    目录 前言 一.Redis 概述 1.1Redis 是什么? 1.2Redis 能做什么? 1.3基础知识 二.Redis 安装与基本命令 2.1Windows 安装 方式一 方式二 2.2Linux ...

  3. 3.6 Windows驱动开发:内核进程汇编与反汇编

    在笔者上一篇文章<内核MDL读写进程内存>简单介绍了如何通过MDL映射的方式实现进程读写操作,本章将通过如上案例实现远程进程反汇编功能,此类功能也是ARK工具中最常见的功能之一,通常此类功 ...

  4. 4.6 C++ Boost 函数绑定回调库

    Boost 库是一个由C/C++语言的开发者创建并更新维护的开源类库,其提供了许多功能强大的程序库和工具,用于开发高质量.可移植.高效的C应用程序.Boost库可以作为标准C库的后备,通常被称为准标准 ...

  5. 为Win12做准备?微软Win11 23H2将集成AI助手:GPT4免费用

    微软日前确认今年4季度推出Win11 23H2,这是Win11第二个年度更新. Win11 23H2具体有哪些功能升级,现在还不好说,但它会集成微软的Copilot,它很容易让人想到多年前的" ...

  6. Leetcode刷题第一天-贪心

    455-分饼干 链接:455. 分发饼干 - 力扣(LeetCode) 优先使用最小饼干满足最小胃口,一个娃只能分一个饼干T_T不能加 1 class Solution: 2 def findCont ...

  7. CentOS 安装字体

    问题来源,今天服务端生成中文图片验证码的Base64字符串,结果返回到客户端后,解码Base64发现是乱码,怀疑是Centos 系统不包含中文字体的问题, 安装字体后,果然问题解决了,以后做系统镜像的 ...

  8. JS Leetcode 26. 删除有序数组中的重复项 题解分析,字典与快慢双指针

    壹 ❀ 引 本题来自LeetCode26. 删除有序数组中的重复项,是一道简单题,题目描述如下: 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组 ...

  9. JS leetcode 找到所有数组中消失的数字 题解分析

    壹 ❀ 引 十天前做的一道题了,一直没整理,今天才花时间去读了官方题解思路,这道题也凸显出了算法思路的重要性,执行耗时差的真不是一点半点.题目来自448. 找到所有数组中消失的数字,题目描述如下: 给 ...

  10. NC200324 魔改森林

    题目链接 题目 题目描述 曾经有一道叫做迷雾森林的题目,然而牛牛认为地图中的障碍太多,实在是太难了,所以删去了很多点,出了这道题. 牛牛给出了一个n行m列的网格图 初始牛牛处在最左下角的格点上(n+1 ...