写在前面
表单中经常会用到选择框的组件,HTML自带的样式太丑了,第三方的组件也不满足项目的使用需求,而且样式也不好看,于是自己就撸了一个,代码很简单,可以实现单选和多选,且选择后不需要再对选择的数据进行处理。
我写的这个组件是按钮的样式,如果需要图标+文字的哪种,自行更改样式即可。
效果图
选择子组件代码
<template>
<div class="selectBtn">
<!-- 标题 -->
<h1 v-if='title'>{{title}}</h1>
<!-- 单选框 -->
<ul :class='{readonly}'>
<li v-for="(item, index) in checkArr" :key="index" @click="change(item)">
<button :class="{selected: item.selected}" :readonly='readonly'>{{item[itemName]}}</button>
</li>
</ul>
<slot/>
<p class="tips" v-if="tips">{{tips}}</p>
</div>
</template>
<script>
// 超出选择限制弹窗提示,请自行引入自己的弹窗
import { toast } from '../../../mixins/popup'
export default {
props: {
// 必传,第一项是name,第二项是value
propName: Array,
// 非必传,限选项数
limit: {
type: Number,
default: 999
},
// 非必传,只读属性
readonly: Boolean,
// 非必传,图中的红色提示文字
tips: String,
// 非必传,默认单选,多选传checkbox
type: {
type: String,
default () {
return 'radio'
}
},
// 非必传,标题
title: String,
// 必传,选项数据仅支持数组形式,如[{name: '选项一', value: 1}],
// name和value即为propName中的第一个和第二项
options: {
type: Array,
default: []
},
// 必传,选中的值,支持三种类型,单选可传String、Number类型,
// 多选只能传Array类型
value: [String, Array, Number]
},
data () {
return {}
},
computed: {
// 处理父组件传过来的数组,与双向绑定的vlaue值比较,
// 选中项的要加上selected为true的属性
checkArr () {
let checkArr = JSON.parse(JSON.stringify(this.options))
checkArr.forEach(item => {
// 默认添加上selected为false的属性
this.$set(item, 'selected', false)
// 单选
if (this.type === 'radio') {
if (item[this.itemVal] === this.value) {
item.selected = true
}
} else if (this.type === 'checkbox') {
// 多选
this.value.forEach(val => {
if (item[this.itemVal] === val[this.itemVal]) {
item.selected = true
}
})
}
})
return checkArr
},
// 选项名,使用computed转换一下,方便处理
itemName () {
return this.propName[0]
},
// 选项值,使用computed转换一下,方便处理
itemVal () {
return this.propName[1]
}
},
methods: {
change (item) {
// 只读属性则不可点击
if (this.readonly) return
// 选中的值
let value = item[this.itemVal]
// 单选
if (this.type === 'radio') {
this.$emit('input', value)
console.log('单选选中的值', value)
return
}
// 多选
if (this.type === 'checkbox') {
let newValue = JSON.parse(JSON.stringify(this.value))
if (item.selected) {
let index = 0
newValue.forEach((newVal, i) => {
if (value === newVal[this.itemVal]) {
index = i
}
})
newValue.splice(index, 1)
} else {
// 超出选择
if (this.value.length === this.limit) {
console.log(`最多选择${this.limit}个`)
return toast('warn', `最多选择${this.limit}个`)
}
newValue.push(item)
}
console.log('多选选中的值', newValue)
this.$emit('input', newValue)
}
}
}
}
</script>
<style lang='less' scoped>
.selectBtn{
padding: 0 24/75rem;
// 标题
h1{
font-size:28/75rem;
color:#222222;
line-height:28/75rem;
background: #fff;
padding-top: 30/75rem;
display: flex;
align-items: cneter;
background:#fff;
margin-bottom: 6/75rem;
&:before {
display: inline-block;
content: '*';
width: 6/75rem;
color: #ff6633;
// background: #ff6633;
height: 25/75rem;
border-radius: 2/75rem;
margin-right: 12/75rem;
margin-top: 4/75rem;
}
}
ul{
&.readonly{
opacity: 0.65;
}
min-height: 84/75rem;
display: flex;
align-items: center;
padding-left:6/75rem;
flex-wrap: wrap;
li{
margin-right: 20/75rem;
margin-top: 18/75rem;
button{
padding: 0 20/75rem;
height: 54/75rem;
border-radius: 4/75rem;
color: fff;
font-size: 26/75rem;
background: #fff;
border: 1px solid #ccc;
color: #666;
&.selected{
color: #fff;
border: 1px solid #079ff7;
background: #079ff7;
}
}
}
}
.tips{
color: #ff6633;
font-size: 26/75rem;
line-height: 36/75rem;
margin-top: 12/75rem;
padding-left:6/75rem;
}
}
</style>
父组件调用代码
<template>
<me-select
type='checkbox' // 多选的话,必须传
:limit='3' // 有限制的话需要传
v-model="selectData" // 必传
title="选择分类" // 非必传
:propName="['name', 'value']" // 必传,数组第一项key是页面中按钮的文字,第二项key是按钮的值
:options='options' // 总数据,必传
tips='最多选择3个分类'
/>
</template>
<script>
// 引入选择框组件
import MeSelect from './MeSelect'
export default {
data () {
return {
// 单选
// selectData: 1,
// 多选
selectData: [{name: '选项一', value: 1}],
options: [
{name: '选项一', value: 1},
{name: '选项二', value: 2},
{name: '选项三', value: 3},
{name: '选项四', value: 4},
{name: '选项五', value: 5}
]
}
},
components: {
MeSelect
}
}
</script>
<style lang="less" scoped></style>
有问题就评论问我吧,(●ˇ∀ˇ●)
个人联系方式(添加请备注):
QQ:332983810
微信:hu_jiliang