怕什么真理无穷,进一步有进一步的欢喜呀,不得不承认的就是,兴趣和擅长是一个良性迭代的循环啊,你擅长某件事情,就会越喜欢它,越喜欢,就越愿意花时间,进而越擅长。所以代码码起来吧哈哈哈
近期用上了vue3,还顺手写了个小需求,用vant-picker封装一个省市的二级联动,并且在从后端接口获取省市的数据,点开弹出框需要展示默认的选项,比如点击广东省-深圳市输入框,弹出的picker级联选择器需要默认广东 -深圳。
如下图:
好,那么话不多说,接下来一步一步来实现。
首先,项目中引入vant-ui
import { Popup, Picker } from 'vant' components: { [Picker.name]: Picker, [Popup.name]: Popup, },
vant官网中,地区联动的形式是这样的
而我的数据格式是这样的
[ { "citys": [ { "cityName": "北京市" } ], "provinceName": "北京市" }, { "citys": [ { "cityName": "天津市" } ], "provinceName": "天津市" }, { "citys": [ { "cityName": "上海市" } ], "provinceName": "上海市" }, { "citys": [ { "cityName": "重庆市" } ], "provinceName": "重庆市" },]
然后对数据做了处理,处理成官网的形式,这样就可以联动了
const columnsData = chinaAreaDataType.map((item, index) => { const children: { text: string }[] = [] item.citys.forEach((item) => { children.push({ text: item.cityName, }) }) return { text: item.provinceName, children } })
其中关于一些vue3父子组件的传值就不过多赘述了,有需要可自行查看官方文档
接下来说一说,点开弹出框需要展示默认的选项怎么实现的吧,此处,vant官网给出了一个api,不过没有使用示例,捣鼓了半天。
使用setIndexes设置获取到的索引值,就可以实现点开弹出框需要展示默认的选项了,在onMounted中通过ref去设置相应的索引即可。
onMounted(() => { const cityName = toRef(props, 'areaValue').value.split('-') const findProvinceName = columnsData.findIndex((value) => value.text === cityName[0]) const findCityName = columnsData[findProvinceName].children.findIndex((value) => value.text === cityName[1]) areaPicker.value?.setIndexes([findProvinceName, findCityName]) })
你以为这样就结束了吗,no no no,还要在popup 中设置lazy-render为false,这样就结束拉~
好吧废话不多说,上子组件代码
子组件代码PickerArea
<template> <div class="container"> <van-popup v-model:show="refShowOverlay" position="bottom" :lazy-render="false" round> <van-picker ref="areaPicker" show-toolbar :columns="columnsData" value-key="text" @change="handleChangeArea" @cancel="handleCancelArea" @confirm="handleConfirmArea" > <template v-slot:cancel> <div class="icon--button"> <img src="https://www.atool.online/article/@/assets/icons/lineCross.svg" /> </div> </template> <template v-slot:title> <p class="title">{{ title }}</p> </template> <template v-slot:confirm> <div class="icon--button"> <img src="https://www.atool.online/article/@/assets/icons/lineHook.svg" /> </div> </template> </van-picker> </van-popup> </div> </template> <script lang="ts"> import { defineComponent, ref, toRef, watch, reactive, onMounted } from 'vue' import { Popup, Picker, Field } from 'vant' import chinaAreaDataType from './chinaAreaData.json' export default defineComponent({ name: 'TasPickerArea', components: { [Picker.name]: Picker, [Popup.name]: Popup, [Field.name]: Field, }, props: { areaValue: { type: String, default: '', }, show: { type: Boolean, default: false, }, showDatePicker: { type: Boolean, default: false, }, title: { type: String, default: '请选择地区', }, }, setup(props, { emit }) { const refShowOverlay = ref(false) const computedShowOverlay = toRef(props, 'showDatePicker') const areaPicker = ref<typeof Picker>() const handleCancelArea = () => { emit('close', false) } const columnsData = chinaAreaDataType.map((item, index) => { const children: { text: string }[] = [] item.citys.forEach((item) => { children.push({ text: item.cityName, }) }) return { text: item.provinceName, children } }) const handleChangeArea = (area: Array<{ text: string }>) => { emit('change-area', area) } const handleConfirmArea = (area: Array<{ text: string }>) => { emit('confirm-area', area) handleCancelArea() } watch(computedShowOverlay, (nV) => { refShowOverlay.value = nV }) onMounted(() => { const cityName = toRef(props, 'areaValue').value.split('-') const findProvinceName = columnsData.findIndex((value) => value.text === cityName[0]) const findCityName = columnsData[findProvinceName].children.findIndex((value) => value.text === cityName[1]) areaPicker.value?.setIndexes([findProvinceName, findCityName]) }) return { refShowOverlay, areaPicker, columnsData, handleChangeArea, handleCancelArea, handleConfirmArea, } }, }) </script> <style lang="scss" scoped src="https://www.atool.online/article/index.scss" />
父组件代码
<template> <div> <van-field v-model="areaValue" label="地区" placeholder="请输入地区" @click="handleClickOFF" /> <tas-picker-china-area :show-date-picker="show" @close="show = false" @confirm-area="handleConfirmArea" @change-area="handleChangeArea" :areaValue="areaValue" /> </div> </template> <script lang="ts"> import { computed, defineComponent, ref, watch, reactive, toRefs } from 'vue' import { Field } from 'vant' import { px2remWithUnit } from '@/utils/ui' import { getThemeVarValue } from '@/config/ui' import { showDialog } from './components/TasDialog/dialog' import TasPickerChinaArea from '@/components/TasPickerChinaArea/index.vue' export default defineComponent({ name: 'TasButton', components: { [Field.name]: Field, TasPickerChinaArea, }, setup() { const show = ref(false) const refShowDatePicker = ref(true) const areaValue = ref('广东省-深圳市') const handleClickOFF = () => { show.value = !show.value } const handleConfirmArea = (area: Array<{ text: string }>) => { areaValue.value = `${area[0].text} - ${area[1].text}` } const handleChangeArea = (area: Array<{ text: string }>) => {} return { show, areaValue, refShowDatePicker, handleClickOFF, handleConfirmArea, handleChangeArea, } }, }) </script>
css代码
.container { overflow: hidden; &::v-deep(.van-picker-column) { font-size: 32px; } ::v-deep(.van-picker__toolbar) { height: auto; padding: 30px 45px; border-bottom: 1px solid rgba(0, 0, 0, 0.1); } } .icon--button img { width: 52px; } .title { font-size: 32px; font-weight: 500; color: #000000; line-height: 45px; }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持阿兔在线工具。