抑郁症健康,内容丰富有趣,生活中的好帮手!
抑郁症健康 > vue2实现移动端级联选择器

vue2实现移动端级联选择器

时间:2019-05-15 17:24:57

相关推荐

级联选择器,用于多层级数据的选择,典型场景为省市区选择

1 简易版

1.1效果展示

1.2 组件源码

<template><div><!-- 遮罩层,支持点击遮罩关闭级联选择 --><divv-show="visible"class="mask"@click="close"/><transition name="fade"><!-- 级联选择部分 --><divv-show="visible"class="cascader"><!-- 顶部标题 --><div class="title">{{ title }}<!-- 关闭按钮 --><i v-if="leftIcon":class="['back', {'arrow': leftIcon === 'arrow', 'cross': leftIcon === 'cross'}]"@click="close"/></div><!-- 展示各级选择的结果 --><ul class="nav-list line"><template v-for="(item, index) in (tabIndex + 1)"><li:key="index":class="['nav-list-item', {'active': index === tabIndex, 'more-hidden': moreHidden}]"@click="changeIndex(index)">{{ (selectedOptions[index] && selectedOptions[index][fieldNames.text]) || placeholderText }}</li></template></ul><!-- 展示当前级列表数据 --><ol class="select-list"ref="selectListRef"><template v-for="(item, index) in pickerDataArr[tabIndex]"><li:key="index"class="select-list-item line"@click="selectItem(item)">{{ item[fieldNames.text] }}</li></template></ol></div></transition></div></template><script>export default {name: 'cascaderPicker',props: {// 是否显示级联选择器,支持 .sync 修饰符visible: {type: Boolean,default: false},// 可选项数据源options: {type: Array,default: () => []},// 顶部标题title: {type: String,default: ''},// 各级未选中时的提示文案 placeholderText: {type: String,default: '请选择'},// 各级选择的结果文字展示溢出是否隐藏moreHidden: {type: Boolean,default: false},// 自定义选择层级数level: {type: Number,default: 3},// 自定义options结构中的字段fieldNames: {type: Object,default: () => ({text: 'text',value: 'value',chlidren: 'children'})},// 关闭图标:arrow(左箭头)/cross(交叉)leftIcon: {type: String,default: 'arrow'} },data() {return {// 当前选择级索引tabIndex: 0,// 各级选择的结果组合selectedOptions: [],// 每一级列表组合pickerDataArr: []}},watch: {tabIndex () {// 列表滚动置零this.$refs['selectListRef'].scrollTop = 0},// 每次弹起组件时重置数据visible (val) {if (val) {this.tabIndex = 0this.selectedOptions = []this.pickerDataArr = []// 默认展示第一级列表数据(最外层)this.pickerDataArr.push(this.options)}}},methods: {changeIndex (index) {// 往回从新选择,加visible条件防止关闭级联选择器动画误触if (this.visible && index < this.tabIndex) {// 删除选中当前级开始的结果this.selectedOptions.splice(index)// 删除选中当前级下一级的列表数据this.pickerDataArr.splice(index + 1)this.tabIndex = index}},selectItem (item) {// 关闭级联选择器动画防重if(!this.visible) return// 保存选择的结果this.selectedOptions.push(item)// 下一级没有子元素及子元素长度为0 或者 当前选择级数等于设定的层级数,结束选择if (!(item[this.fieldNames.children] && item[this.fieldNames.children].length) || this.tabIndex + 1 >= this.level) {this.$emit('finish', this.selectedOptions)return}// 保存下一级列表数据this.pickerDataArr.push(item[this.fieldNames.children])this.tabIndex++},close () {this.$emit('close')this.$emit('update:visible', false)}}}</script><style lang="less" scoped>.mask{position: fixed;left: 0;top: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.7);z-index: 99;}.cascader{position: fixed;left: 0;bottom: 0;width: 100%;height: 80%;background-color: #fff;border-radius: 16px 16px 0 0;display: flex;flex-direction: column;z-index: 999;.title{position: relative;padding: 20px 0;font-size: 18px;line-height: 18px;text-align: center;.back{position: absolute;top: 19px;left: 20px;width: 20px;height: 20px;}.arrow{background: url('./images/icon-arrow.png') no-repeat;background-size: 100%;}.cross{background: url('./images/icon-close.png') no-repeat;background-size: 100%;}}.nav-list{position: relative;display: flex;padding: 0 20px;font-size: 16px;text-align: center;.nav-list-item{padding: 14px 0;margin-right: 16px;max-width: 64px;}.nav-list-item:last-child{margin-right: 0;}.active{position: relative;color: #1752ff;}.active::after{position: absolute;left: 0;bottom: 0;display: block;content: '';width: 100%;height: 3px;background-color: #1752ff;}.more-hidden{align-items: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}}.select-list{flex: 1;overflow-y: auto;font-size: 16px;.select-list-item{position: relative;padding: 12px 20px;}}.line::after{position: absolute;left: 0;bottom: 0;display: block;content: '';width: 100%;height: 1px;background-color: #ccc;transform: scaleY(0.5);}.select-list::-webkit-scrollbar{display: none;}}.fade-enter,.fade-leave-to{opacity: 0;transform: translateY(100%);}.fade-enter-active,.fade-leave-active{transition: all 0.3s ease;}</style>

2 高级版

2.1 效果展示

2.2 组件源码

<template><div><!-- 遮罩层,支持点击遮罩关闭级联选择 --><divv-show="visible"class="mask"@click="close"/><transition name="fade"><!-- 级联选择部分 --><divv-show="visible"class="cascader"><!-- 顶部标题 --><div class="title">{{ title }}<!-- 关闭按钮 --><i v-if="leftIcon":class="['back', {'arrow': leftIcon === 'arrow', 'cross': leftIcon === 'cross'}]"@click="close"/></div><!-- 展示各级选择的结果 --><ul class="nav-list"><template v-for="(item, index) in navListLen"><li:key="index":class="['nav-list-item', {'active': index === tabIndex, 'more-hidden': moreHidden}]"@click="changeIndex(index)">{{ (selectedOptions[index] && selectedOptions[index][fieldNames.text]) || placeholderText }}</li></template></ul><!-- 展示各级选择列表数据 --><div class="select-content"><div class="select-content-box":style="translateStyle"><ol class="select-list"v-for="(item1, index1) in pickerDataArr" :key="index1"ref="selectListRefs"><template v-for="(item2, index2) in item1"><li:key="index2"class="select-list-item"@click="selectItem(item2, index1)"><span class="text">{{ item2[fieldNames.text] }}</span><iv-if="selectedOptions.includes(item2)"class="choose-active"/></li></template></ol></div></div></div></transition></div></template><script>export default {name: 'cascaderPicker',props: {// 是否显示级联选择器,支持 .sync 修饰符visible: {type: Boolean,default: false},// 可选项数据源options: {type: Array,default: () => []},// 顶部标题title: {type: String,default: ''},// 各级未选中时的提示文案 placeholderText: {type: String,default: '请选择'},// 各级选择的结果文字展示溢出是否隐藏moreHidden: {type: Boolean,default: false},// 自定义选择层级数level: {type: Number,default: 3},// 自定义options结构中的字段fieldNames: {type: Object,default: () => ({text: 'text',value: 'value',chlidren: 'children'})},// 关闭图标:arrow(左箭头)/cross(交叉)leftIcon: {type: String,default: 'arrow'}},data() {return {// 当前选择级索引tabIndex: 0,// 各级选择的结果组合selectedOptions: [],// 每一级列表组合pickerDataArr: [],// 完成选择标识finishedFlag: false}},created () {// 默认展示第一级列表数据(最外层)this.pickerDataArr.push(this.options)},computed: {navListLen () {return !this.finishedFlag && this.selectedOptions.length < this.level ? this.selectedOptions.length + 1 : this.selectedOptions.length},translateStyle () {return {// 当选择级数改变时,需要切换当前列表位置transform:`translateX(-${this.tabIndex * 100}%)`,transitionDuration: '0.3s'}}},methods: {changeIndex (index) {// 关闭级联选择器时防止动画延迟误触if (!this.visible) return// 改变当前选择级数索引this.tabIndex = index},selectItem (item, clickIndex) {// 动画延迟防重:在关闭级联选择器前,点击列表选项所处的层级数与当前选择级数相等才能进行选择if (!this.visible || clickIndex !== this.tabIndex) return// 每次选择选项时,完成选择标识需重置,以防来回选择情况this.finishedFlag = false// 先删除当前级开始选中的旧数据,再保存当前级选中的结果this.selectedOptions.splice(this.tabIndex, this.selectedOptions.length, item)// 下一级没有子元素及子元素长度为0 或者 当前选择级数等于设定的层级数,结束选择if (!(item[this.fieldNames.children] && item[this.fieldNames.children].length) || this.tabIndex + 1 >= this.level) {this.finishedFlag = truethis.$emit('finish', this.selectedOptions)return}// 下一级数据变更前,滚动位置需要置0this.$refs['selectListRefs'].forEach((ele, index) => {if (index > this.tabIndex) {ele.scrollTop = 0}})this.tabIndex++// 先删除下一级开始的旧选择列表数据,再保存当前的下一级列表数据this.pickerDataArr.splice(this.tabIndex, this.pickerDataArr.length, item[this.fieldNames.children])},close () {this.$emit('close')this.$emit('update:visible', false)},resetPicker () {this.tabIndex = 0this.finishedFlag = falsethis.selectedOptions = []this.pickerDataArr = []this.pickerDataArr.push(this.options)this.$nextTick(() => {// 重置默认展示第一级列表的滚动位置if(this.$refs['selectListRefs'][0]) {this.$refs['selectListRefs'][0].scrollTop = 0}})}}}</script><style lang="less" scoped>.mask{position: fixed;left: 0;top: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, .7);z-index: 99;}.cascader{position: fixed;left: 0;bottom: 0;width: 100%;height: 80%;background-color: #fff;border-radius: 16px 16px 0 0;display: flex;flex-direction: column;z-index: 999;.title{position: relative;padding: 20px 0;font-size: 18px;line-height: 18px;text-align: center;.back{position: absolute;top: 19px;left: 20px;width: 20px;height: 20px;}.arrow{background: url('./images/icon-arrow.png') no-repeat;background-size: 100%;}.cross{background: url('./images/icon-close.png') no-repeat;background-size: 100%;}}.nav-list{display: flex;padding: 0 20px;font-size: 16px;text-align: center;.nav-list-item{padding: 14px 0;margin-right: 16px;max-width: 64px;}.nav-list-item:last-child{margin-right: 0;}.active{position: relative;color: #1752ff;}.active::after{position: absolute;left: 0;bottom: 0;display: block;content: '';width: 100%;height: 3px;background-color: #1752ff;}.more-hidden{align-items: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}}.select-content{flex: 1;overflow: hidden;.select-content-box{width: 100%;height: 100%;display: flex;.select-list{flex: 0 0 100%;width: 100%;height: 100%;overflow-y: auto;font-size: 16px;.select-list-item{padding: 12px 20px;display: flex;align-items: center;justify-content: space-between;.text{max-width: 264px;}.choose-active{width: 20px;height: 20px;background: url('./images/icon-choose.png') no-repeat;background-size: 100%;}}}.line{position: relative;}.line::after{position: absolute;left: 0;bottom: 0;display: block;content: '';width: 100%;height: 1px;background-color: #ccc;transform: scaleY(0.5);}.select-list::-webkit-scrollbar{display: none;}}}}.fade-enter,.fade-leave-to{opacity: 0;transform: translateY(100%);}.fade-enter-active,.fade-leave-active{transition: all 0.3s ease;}</style>

3 使用说明

3.1 参数说明

3.2 事件说明

3.3 方法说明

4 使用示例代码

<template><div class="home"><div class="flex"><input type="text" v-model="cascaderValue"><button @click="showPicker">请选择所在地区</button></div><cascader-pickerref="cascaderPicker":visible.sync="isShow":options="options"title="请选择所在地区":level="3":field-names="{text: 'name', value: 'code', children: 'childList'}"@finish="onFinish"/></div></template><script>import CascaderPicker from '../components/cascaderPicker'import chinaArea from './json/chinaArea.json'export default {name: 'Home',data() {return {cascaderValue: '',isShow: false,options: chinaArea?.childList || []}},components: {CascaderPicker},methods:{showPicker() {this.isShow = true//this.$refs.cascaderPicker.resetPicker() // 高级版有效},onFinish(selectedOptions) {console.log('选择结果==>', selectedOptions)this.cascaderValue = selectedOptions.map(item => item.name).join('/')this.isShow = false}}}</script><style lang="less" scoped>.flex{display: flex;align-items: center;padding: 20px;input{flex: 1;}}</style>

5 省市区json数据

点击链接获取数据数据举例:

{"code": 100000,"name": "中华人民共和国","childList": [{"code": 110000,"name": "北京市","childList": [{"code": 110101,"name": "东城区"},{"code": 110102,"name": "西城区"},{"code": 110105,"name": "朝阳区"},{"code": 110106,"name": "丰台区"},{"code": 110107,"name": "石景山区"},{"code": 110108,"name": "海淀区"},{"code": 110109,"name": "门头沟区"},{"code": 110111,"name": "房山区"},{"code": 110112,"name": "通州区"},{"code": 110113,"name": "顺义区"},{"code": 110114,"name": "昌平区"},{"code": 110115,"name": "大兴区"},{"code": 110116,"name": "怀柔区"},{"code": 110117,"name": "平谷区"},{"code": 110118,"name": "密云区"},{"code": 110119,"name": "延庆区"}]}]}

如果觉得《vue2实现移动端级联选择器》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。