前言
对于选择器组件,vant 中的 picker 组件是一个非常合适的选择。它不仅提供了灵活的配置选项,还可以很方便地与其他 vant 组件结合使用,帮助我们快速搭建出漂亮、易用的移动端页面。在本文中,我将为大家介绍如何基于 vant 的 picker 组件进行二次封装,以便更好地满足实际的业务需求。
实现思路
- 首先定义一个子组件页面用来封装选择器;
- 在父组件中(使用的页面)引入封装组件(子组件)并注册,然后在页面中使用,在父组件中给标签(注册的组件名)上绑定多个属性, 属性上挂载需要传递的值,通过
props
在子组件(封装文件)接收数据; - 在子组件中自定义确定的事件,调用这个事件后,子组件通过
this.$emit('自定义事件名',要传递的数据)
发送父组件可以监听的数据,最后父组件监听子组件事件,调用事件并接收传递过来的数据。
定义的参数
参数 | 描述 |
---|---|
selectValue | 绑定值 model |
keyValue | 绑定的 key 字段 |
keyLabel | 绑定的 value 字段 |
columns | 绑定的 option 数据源 |
required | 是否显示红色 * 校验 |
rules | 校验规则 |
required | 是否必填 |
confirm | 选中的回调函数 |
封装文件
<template>
<div>
<van-field v-model="textValue" v-bind="$attrs" :name="$attrs.name" :rules="rules" :required="required" :readonly="readonly"
:is-link="islink" @click="show = !show" />
<van-popup v-model="show" get-container="body" position="bottom">
<van-picker :columns="columns" show-toolbar :value-key="keyValue" :title="$attrs.label" @cancel="show = !show" @confirm="onConfirm">
<template #option="option">
{{ option[keyLabel] }}
</template>
</van-picker>
</van-popup>
</div>
</template>
<script>
export default {
props: {
required: {
type: Boolean,
},
readonly: {
type: Boolean,
},
islink: {
type: Boolean,
},
columns: {
type: Array,
},
rules: {
type: Array,
},
selectValue: {
type: String,
},
keyValue: {
type: String,
},
keyLabel: {
type: String,
},
},
data() {
return {
show: false,
textValue: "",
selectOptions: [],
};
},
methods: {
onConfirm(obj) {
const selectedOption = this.columns.find(item => item[this.keyValue] === obj.value)
if (selectedOption && selectedOption[this.keyLabel] !== this.textValue) {
this.textValue = selectedOption[this.keyLabel]
}
this.show = false
this.$emit('confirm', obj)
},
formatterValue(value) {
let str = "";
if (!this.columns.length || !value.length) {
str = "";
} else {
let oArr = this.columns.filter((item) => {
return item[this.keyValue].toString() == value.toString();
});
str = oArr[0][this.keyLabel];
}
return str;
},
//根据key值格式化成picker需要的option格式
formatColumnsByKey() {
let arr = [];
let value = this.keyValue ? this.keyValue : "value";
let label = this.keyLabel ? this.keyLabel : "label";
this.columns.map((item) => {
arr.push({
label: item[label],
value: item[value],
});
});
this.selectOptions = arr;
},
},
watch: {
columns: {
handler(newValue) {
this.textValue = this.formatterValue(
this.selectValue ? this.selectValue : ""
);
},
deep: true,
immediate: true,
},
selectValue: {
handler(newValue) {
this.textValue = this.formatterValue(newValue ? newValue : "");
},
deep: true,
immediate: true,
},
},
};
</script>
使用文件
<template>
<div>
<van-form validate-first>
<VanSelect name="qylx" label="企业类型" placeholder="请选择企业类型" :selectValue="qylx" :keyValue="`value`" :keyLabel="`label`" :required="true"
:readonly="true" :columns="qylxOption" :rules="rules.qybh" @confirm="qylxConfirm" />
<div class="btnBomBox">
<van-button round size="small" block @click="submitOn" type="info">提交</van-button>
</div>
</van-form>
</div>
</template>
<script>
import VanSelect from "@/components/vanSelect/index";
export default {
components: {
VanSelect,
},
data() {
return {
qylx: "",
qylxOption: [
{ label: "自营企业", value: "1" },
{ label: "其他企业", value: "2" },
],
rules: {
qybh: [
{
required: true,
message: "请选择企业类型",
},
],
},
};
},
methods: {
// 点击确定
qylxConfirm(value) {
this.qylx = value.value;
},
// 提交
submitOn() {
console.log(this.qylx);
},
},
};
</script>
<style scoped>
.btnBomBox {
padding: 0px 16px;
display: flex;
justify-content: center;
}
</style>
实现效果
拓展 – 模糊查询
封装文件
<template>
<div>
<van-field v-model="textValue" v-bind="$attrs" :name="$attrs.name" :rules="rules" :required="required" :readonly="readonly"
:is-link="islink" @click="show = !show" />
<van-popup v-model="show" get-container="body" position="bottom">
<!-- isSearch:是否模糊查询 -->
<van-search v-if="isSearch" v-model="searchKey" placeholder="请输入搜索关键词" @input="onSearch" @clear="resetColumns" />
<van-picker :columns="columnsData" show-toolbar :value-key="keyValue" :title="$attrs.label" @cancel="canclePop" @confirm="onConfirm">
<template #option="option">
{{ option[keyLabel] }}
</template>
</van-picker>
</van-popup>
</div>
</template>
<script>
export default {
props: {
required: {
type: Boolean,
},
readonly: {
type: Boolean,
},
islink: {
type: Boolean,
},
isSearch: {
type: Boolean,
default: true,
},
columns: {
type: Array,
},
rules: {
type: Array,
},
selectValue: {
type: String,
},
keyValue: {
type: String,
},
keyLabel: {
type: String,
},
},
data() {
return {
show: false,
textValue: "",
selectOptions: [],
searchKey: "",
copyList: [],
columnsData: [],
};
},
methods: {
resetColumns() {
this.searchKey = "";
this.columnsData = this.columns;
},
canclePop() {
this.show = !this.show;
this.resetColumns();
},
onSearch() {
if (!this.searchKey.length) {
this.columnsData = this.columns;
return;
}
let newArr = [];
if (this.columnsData.length) {
newArr = this.columnsData.filter((item) => {
return item[this.keyLabel].toString().indexOf(this.searchKey) > -1;
});
}
this.columnsData = newArr;
},
onConfirm(obj) {
const selectedOption = this.columns.find(item => item[this.keyValue] === obj.value)
if (selectedOption && selectedOption[this.keyLabel] !== this.textValue) {
this.textValue = selectedOption[this.keyLabel]
}
this.show = false
this.$emit('confirm', obj)
},
formatterValue(value) {
let str = "";
if (!this.columns.length || !value.length) {
str = "";
} else {
let oArr = this.columns.filter((item) => {
return item[this.keyValue].toString() == value.toString();
});
str = oArr[0][this.keyLabel];
}
return str;
},
//根据key值格式化成picker需要的option格式
formatColumnsByKey() {
let arr = [];
let value = this.keyValue ? this.keyValue : "value";
let label = this.keyLabel ? this.keyLabel : "label";
this.columns.map((item) => {
arr.push({
label: item[label],
value: item[value],
});
});
this.selectOptions = arr;
},
},
watch: {
columns: {
handler(newValue) {
this.columnsData = newValue;
this.copyList = JSON.parse(JSON.stringify(newValue));
this.textValue = this.formatterValue(
this.selectValue ? this.selectValue : ""
);
},
deep: true,
immediate: true,
},
selectValue: {
handler(newValue) {
this.textValue = this.formatterValue(newValue ? newValue : "");
},
deep: true,
immediate: true,
},
},
};
</script>
使用文件
<template>
<div>
<van-form validate-first>
<VanSelect :isSearch="true" name="qylx" label="排放阶段" placeholder="请选择排放阶段" :selectValue="qylx" :keyValue="`value`" :keyLabel="`label`"
:required="true" :readonly="true" :columns="qylxOption" :rules="rules.qybh" @confirm="qylxConfirm" />
<div class="btnBomBox">
<van-button round size="small" block @click="submitOn" type="info">提交</van-button>
</div>
</van-form>
</div>
</template>
<script>
import VanSelect from "@/components/vanSelect/index";
export default {
components: {
VanSelect,
},
data() {
return {
qylx: "",
qylxOption: [
{ label: "国一", value: "1" },
{ label: "国二", value: "2" },
{ label: "国三", value: "3" },
{ label: "国四", value: "4" },
{ label: "国五", value: "5" },
{ label: "国六", value: "6" },
{ label: "新能源", value: "7" },
{ label: "X", value: "8" },
],
rules: {
qybh: [
{
required: true,
message: "请选择排放阶段",
},
],
},
};
},
methods: {
// 点击确定
qylxConfirm(value) {
this.qylx = value.value;
},
// 提交
submitOn() {
console.log(this.qylx);
},
},
};
</script>
<style scoped>
.btnBomBox {
padding: 0px 16px;
display: flex;
justify-content: center;
}
</style>