uniapp自定义picker城市多级联动组件

  • 支持多端——h5、app、微信小程序、支付宝小程序...
  • 支持自定义配置picker插件级数
  • 支持无限级

注意事项:插件传入数据格式为children树形格式,内部包含:id、name

参数 类型 描述 默认值 必选
title string 标题 ''
layer number 控制几级联动 1
data arr 数据 如:[{text: '', adcode: '', children: [{text: '', adcode: ''}]}] []

组件运行图示:
2

组件选择后返回数据如:
console

引用示例:

<template>
<view class="content">
<view class="aui-content" :style="{height: contentHeight}">
<view class="aui-btn aui-btn-blue" @click.stop="showPicker($event)">picker无限级联动</view>
</view>
<aui-picker
ref="picker"
:title="auiPicker.title"
:layer="auiPicker.layer"
:data="auiPicker.data"
@callback="pickerCallback"
></aui-picker>
</view>
</template>
<script>
import auiPicker from '@/components/aui-picker/aui-picker.vue';
export default {
components: {
auiPicker
},
data() {
return {
auiPicker: {
title: 'picker多级联动',
layer: null,
data: []
},
}
},
created(){
},
mounted() {
},
methods: {
//显示picker多级联动弹窗
showPicker(e){
const _this = this;
_this.auiPicker.data=[{
id: "1001",
name: "一级菜单1",
children: [{
id: "1002",
name: "二级菜单1-1",
children: [{
id: "1003",
name: "三级菜单1-1",
children: [{
id: "1004",
name: "四级菜单1-1"
}]
}]
}]
},
{
id: "1005",
name: "一级菜单2",
children: [{
id: "1006",
name: "二级菜单2-1",
children: [{
id: "1007",
name: "三级菜单2-1",
children: [{
id: "1008",
name: "四级菜单2-1"
}]
}]
}]
}];
_this.$refs.picker.open().then(function(){
console.log('picker打开');
});
},
//picker多级联动回调
pickerCallback(e){
const _this = this;
console.log(e);
let result = '';
e.data.forEach(function(item, index){
result += item.name + ' ';
});
uni.showModal({
title: '提示',
content: result,
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
}
}
}
</script>
<style>
.aui-content{padding: 15px 0 0 0;}
</style>

aui-picker组件完整代码:

项目components文件夹下创建aui-picker夹,此文件夹下创建aui-picker.vue——多级联动组件

<template name="aui-picker">
<view class="aui-picker" v-if="SHOW" :class="{
'aui-picker-in': FADE==1,
'aui-picker-out': FADE==0}"
>
<view class="aui-mask" @click.stop="close"></view>
<view class="aui-picker-main">
<view class="aui-picker-header">
<view class="aui-picker-title" v-if="title">{{title}}</view>
<view class="aui-picker-close iconfont iconclose" @click.stop="close"></view>
</view>
<view class="aui-picker-nav">
<view class="aui-picker-navitem"
v-if="nav.length>0"
v-for="(item, index) in nav"
:key="index"
:data-index="index"
:class="[index==navCurrentIndex ? 'active' : '', 'aui-picker-navitem-'+index]"
:style="{margin: nav.length>2 ? '0 10px 0 0' : '0 30px 0 0'}"
@click.stop="_changeNav($event)"
>{{item.name}}</view>
<view class="aui-picker-navitem"
:key="nav.length"
:data-index="nav.length"
:class="[nav.length==navCurrentIndex ? 'active' : '', 'aui-picker-navitem-'+nav.length]"
:style="{margin: nav.length>2 ? '0 10px 0 0' : '0 30px 0 0'}"
@click.stop="_changeNav($event)"
>请选择</view>
<view class="aui-picker-navborder" :style="{left: navBorderLeft+'px'}"></view>
</view>
<view class="aui-picker-content">
<view class="aui-picker-lists">
<view class="aui-picker-list"
v-for="(list, index) in queryItems.length + 1"
:key="index"
:data-index="index"
:class="[index==navCurrentIndex ? 'active' : '']"
>
<view class="aui-picker-list-warp" v-if="index == 0">
<view class="aui-picker-item"
v-for="(item, key) in items"
v-if="item.pid=='0'"
:key="key"
:data-pindex="index"
:data-index="key"
:data-id="item.id"
:data-pid="item.pid"
:data-name="item.name"
:class="{'active': result.length>index && result[index].id==item.id}"
:style="{'background': touchConfig.index==key && touchConfig.pindex==index ? touchConfig.style.background : ''}"
@click.stop="_chooseItem($event)"
@touchstart="_btnTouchStart($event)"
@touchmove="_btnTouchEnd($event)"
@touchend="_btnTouchEnd($event)"
>{{item.name}}</view>
</view>
<view class="aui-picker-list-warp" v-else>
<view class="aui-picker-item"
v-for="(item, key) in queryItems[index-1]"
:key="key"
:data-pindex="index"
:data-index="key"
:data-id="item.id"
:data-pid="item.pid"
:data-name="item.name"
:class="{'active': result.length>index && result[index].id==item.id}"
:style="{'background': touchConfig.index==key && touchConfig.pindex==index ? touchConfig.style.background : ''}"
@click.stop="_chooseItem($event)"
@touchstart="_btnTouchStart($event)"
@touchmove="_btnTouchEnd($event)"
@touchend="_btnTouchEnd($event)"
>{{item.name}}</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'aui-picker',
props: {
title: { //标题
type: String,
default: ''
},
layer: { //控制几级联动,默认无限级(跟随数据有无下级)
type: Number,
default: null
},
data: { //数据 如:[{id: '', name: '', children: [{id: '', name: ''}]}]
type: Array,
default (){
return [
// [{id: '', name: '', children: [{id: '', name: ''}]}]
]
}
}
},
data(){
return {
SHOW: false,
FADE: -1,
nav: [],
items: [],
queryItems: [],
navCurrentIndex: 0,
navBorderLeft: 40,
result: [],
touchConfig: {
index: -1,
pindex: -1,
style: {
color: '#197DE0',
background: '#EFEFEF'
}
}
}
},
created(){
const _this = this;
},
watch:{
data(){
const _this = this;
const data = _this.data;
_this.items = _this._flatten(data, '0')
    }  
  },
mounted(){
},
methods:{
// 打开
open(){
const _this = this;
_this.reset(); //打开时重置picker
return new Promise(function(resolve, reject){
_this.SHOW = true;
_this.FADE = 1;
resolve();
});
},
// 关闭
close(){
const _this = this;
return new Promise(function(resolve, reject){
_this.FADE = 0;
const _hidetimer = setTimeout(()=>{
_this.SHOW = false;
_this.FADE = -1;
clearTimeout(_hidetimer);
resolve();
},100)
});
},
//重置
reset(){
const _this = this;
_this.queryItems = [];
_this.nav = [];
_this.navBorderLeft = 40;
_this.navCurrentIndex = 0;
_this.result = [];
},
//导航栏切换
_changeNav(e){
const _this = this;
const index = Number(e.currentTarget.dataset.index);
_this.navCurrentIndex = index;
const _el = uni.createSelectorQuery().in(this).select(".aui-picker-navitem-"+index);
_el.boundingClientRect(data => {
_this.navBorderLeft = data.left + 20;
}).exec();
},
//数据选择
_chooseItem(e){
const _this = this;
const id = e.currentTarget.dataset.id;
const name = e.currentTarget.dataset.name;
const pid = e.currentTarget.dataset.pid;
const _arr = [];
_this.result[_this.navCurrentIndex] = {id: id, name: name, pid: pid};
if(
(!_this._isDefine(_this.layer) && _this._isDefine(_this._deepQuery(_this.data, id).children))
||
(_this.navCurrentIndex < (Number(_this.layer) - 1) && _this._isDefine(_this._deepQuery(_this.data, id).children))
)
{ //有下级数据
_this._deepQuery(_this.data, id).children.forEach(function(item, index){
_arr.push({id: item.id, name: item.name, pid: id});
});
if(_this.navCurrentIndex == _this.queryItems.length)
{ //选择数据
_this.queryItems.push(_arr);
_this.nav.push({name: name});
}
else
{ //重新选择数据
_this.queryItems.splice(_this.navCurrentIndex+1, 1);
_this.nav.splice(_this.navCurrentIndex+1, 1);
_this.queryItems.splice(_this.navCurrentIndex, 1, _arr);
_this.nav.splice(_this.navCurrentIndex, 1, {name: name});
}
_this.navCurrentIndex = _this.navCurrentIndex + 1;
const _el = uni.createSelectorQuery().in(this).select(".aui-picker-navitem-"+_this.navCurrentIndex);
setTimeout(()=>{
_el.boundingClientRect(data => {
_this.navBorderLeft = data.left + 20;
}).exec();
},100)
}
else
{ //无下级数据
_this.close().then(()=>{
_this.$emit("callback", {status: 0, data: _this.result});
});
}
},
//递归遍历——将树形结构数据转化为数组格式
_flatten(tree, pid) {
return tree.reduce((arr, {id, name, children = []}) =>
arr.concat([{id, name, pid}], this._flatten(children, id)), [])
},
//根据id查询对应的数据(如查询id=10100对应的对象)
_deepQuery(tree, id) {
let isGet = false;
let retNode = null;
function deepSearch(tree, id){
for(let i = 0; i < tree.length; i++) {
if(tree[i].children && tree[i].children.length > 0) {
deepSearch(tree[i].children, id);
}
if(id === tree[i].id || isGet) {
isGet||(retNode = tree[i]);
isGet = true;
break;
}
}
}
deepSearch(tree, id);
return retNode;
},
/***判断字符串是否为空
@param {string} str 变量
@example: aui.isDefine("变量");
*/
_isDefine(str){
if (str==null || str=="" || str=="undefined" || str==undefined || str=="null" || str=="(null)" || str=='NULL' || typeof (str)=='undefined'){
return false;
}else{
str = str + "";
str = str.replace(/\s/g, "");
if (str == ""){return false;}
return true;
}
},
_btnTouchStart(e){
const _this = this,
index = Number(e.currentTarget.dataset.index),
pindex = Number(e.currentTarget.dataset.pindex);
_this.touchConfig.index = index;
_this.touchConfig.pindex = pindex;
},
_btnTouchEnd(e){
const _this = this,
index = Number(e.currentTarget.dataset.index),
pindex = Number(e.currentTarget.dataset.pindex);
_this.touchConfig.index = -1;
_this.touchConfig.pindex = -1;
},
}
}
</script>
<style scoped>
/* ====================
多级联动弹窗
=====================*/
.aui-picker{
width: 100vw;
height: 100vh;
opacity: 0;
position: fixed;
top: 0;
left: 0;
z-index: 999;
/* display: none; */
}
.aui-picker.aui-picker-in{
-moz-animation: aui-fade-in .1s ease-out forwards;
-ms-animation: aui-fade-in .1s ease-out forwards;
-webkit-animation: aui-fade-in .1s ease-out forwards;
animation: aui-fade-in .1s ease-out forwards;
}
.aui-picker.aui-picker-out{
-moz-animation: aui-fade-out .1s ease-out forwards;
-ms-animation: aui-fade-out .1s ease-out forwards;
-webkit-animation: aui-fade-out .1s ease-out forwards;
animation: aui-fade-out .1s ease-out forwards;
}
.aui-picker-main{
width: 100vw;
height: 50vh;
background: #FFF;
border-radius: 15px 15px 0 0;
position: absolute;
left: 0px;
bottom: -50vh;
z-index: 999;
}
.aui-picker.aui-picker-in .aui-picker-main{
-moz-animation: aui-slide-up-screen .2s ease-out forwards;
-ms-animation: aui-slide-up-screen .2s ease-out forwards;
-webkit-animation: aui-slide-up-screen .2s ease-out forwards;
animation: aui-slide-up-screen .2s ease-out forwards;
}
.aui-picker.aui-picker-out .aui-picker-main{
-moz-animation: aui-slide-down-screen .2s ease-out forwards;
-ms-animation: aui-slide-down-screen .2s ease-out forwards;
-webkit-animation: aui-slide-down-screen .2s ease-out forwards;
animation: aui-slide-down-screen .2s ease-out forwards;
}
.aui-picker-header{
width: 100%;
min-height: 50px;
position: relative;
z-index: 999;
background: #F2F2F2;
border-radius: 15px 15px 0 0;
}
.aui-picker-header::after{
content: '';
width: 100%;
height: 1px;
background: rgba(100,100,100,.3);
-moz-transform: scaleY(.3);
-ms-transform: scaleY(.3);
-webkit-transform: scaleY(.3);
transform: scaleY(.3);
position: absolute;
left: 0;
bottom: 0;
z-index: 999;
}
.aui-picker-title{
line-height: 20px;
text-align: center;
font-size: 17px;
color: #333;
padding: 15px;
box-sizing: border-box;
position: absolute;
left: 50px;
right: 50px;
top: 0;
}
.aui-picker-close.iconfont{
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
font-size: 20px;
color: #aaa;
border-radius: 0 10px 0 0;
position: absolute;
right: 0;
top: 0;
}
.aui-picker-content{
width: 100%;
height: -webkit-calc(100% - 100px);
height: calc(100% - 100px);
}
.aui-picker-nav{
width: 100%;
height: 50px;
text-align: left;
padding: 0 20px;
margin: 0 0 1px 0;
justify-content: flex-start;
white-space: nowrap;
box-sizing: border-box;
position: relative;
}
.aui-picker-nav::after{
content: '';
width: 100%;
height: 1px;
background: rgba(100,100,100,.3);
-moz-transform: scaleY(.3);
-ms-transform: scaleY(.3);
-webkit-transform: scaleY(.3);
transform: scaleY(.3);
position: absolute;
left: 0;
bottom: 0;
z-index: 999;
}
.aui-picker-navitem{
width: 80px;
line-height: 50px;
font-size: 16px;
margin: 0 30px 0 0;
text-align: center;
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.aui-picker-navitem.active{
color: #197DE0;
}
.aui-picker-navborder{
width: 40px;
height: 3px;
background: #197DE0;
border-radius: 5px;
transition: left .15s;
position: absolute;
left: 40px;
bottom: 0;
}
.aui-picker-lists{
width: 100%;
height: 100%;
justify-content: space-around;
white-space: nowrap;
}
.aui-picker-list{
width: 100%;
height: 100%;
overflow: hidden;
overflow-y: scroll;
display: none;
vertical-align: top;
}
.aui-picker-list.active{
display: inline-block;
}
.aui-picker-list-warp{
width: 100%;
height: auto;
box-sizing: border-box;
padding: 15px 0;
display: inline-block;
}
.aui-picker-item{
width: 100%;
height: 50px;
line-height: 50px;
padding: 0 15px;
box-sizing: border-box;
font-size: 15px;
color: #333;
position: relative;
}
.aui-picker-item.active{
color: #197DE0;
}
.aui-picker-item.active::after{
content: '✔';
font-size: 15px;
color: #197DE0;
position: absolute;
top: 0px;
right: 10px;
}
</style>
posted @   蓦然JL  阅读(3362)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
  1. 1 唯一 G.E.M.邓紫棋
  2. 2 他只是经过 白敬亭 魏大勋
  3. 3 Uptown Funk Mark Ronson / Bruno Mars
  4. 4 在你的身边 盛哲
  5. 5 Edge of My Life Manafest
  6. 6 凄美地 郭顶
  7. 7 Wonderful Tonight Boyce Avenue
  8. 8 心如止水 Ice Paper
  9. 9 Sugar Maroon 5
  10. 10 静谧时光 JIAxNING
  11. 11 Right Now (Na Na Na) Aamir
  12. 12 Dangerously Charlie Puth
  13. 13 Someone You Loved Madilyn Paige
  14. 14 Shape of My Heart Boyce Avenue
  15. 15 We Can't Stop Boyce Avenue / Bea Miller
  16. 16 Perfect Boyce Avenue
  17. 17 Love Me Like You Do Boyce Avenue
  18. 18 Thank You Boyce Avenue
  19. 19 Don’t Wanna Know Boyce Avenue / Sarah Hyland
他只是经过 - 白敬亭 魏大勋
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

Not available

访问主页
关注我
关注微博
私信我
返回顶部
点击右上角即可分享
微信分享提示

目录导航