zoukankan      html  css  js  c++  java
  • uniapp自定义picker城市多级联动组件

    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{
    		 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{
    		 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{
    		 100%;
    		min-height: 50px;
    		position: relative;
    		z-index: 999;
    		background: #F2F2F2;
    		border-radius: 15px 15px 0 0;
    	}
    	.aui-picker-header::after{
    		content: '';
    		 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{
    		 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{
    		 100%;
    		height: -webkit-calc(100% - 100px);
    		height: calc(100% - 100px);
    	}
    	.aui-picker-nav{
    		 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: '';
    		 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{
    		 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{
    		 40px;
    		height: 3px;
    		background: #197DE0;
    		border-radius: 5px;
    		transition: left .15s;
    		position: absolute;
    		left: 40px;
    		bottom: 0;
    	}
    	.aui-picker-lists{
    		 100%;
    		height: 100%;
    		justify-content: space-around;
    		white-space: nowrap;
    	}
    	.aui-picker-list{
    		 100%;
    		height: 100%;
    		overflow: hidden;
    		overflow-y: scroll;
    		display: none;
    		vertical-align: top;
    	}
    	.aui-picker-list.active{
    		display: inline-block;
    	}
    	.aui-picker-list-warp{
    		 100%;
    		height: auto;
    		box-sizing: border-box;
    		padding: 15px 0;
    		display: inline-block;
    	}
    	.aui-picker-item{
    		 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>
    
    
  • 相关阅读:
    项目中对axios进行二次封装
    vue对象数组数据变化,页面不渲染
    深拷贝和浅拷贝
    你说的都队-凡事预则立
    你说的都队——项目系统设计与数据库设计
    2020软件工程作业——团队04
    2020软件工程作业——团队03
    2020软件工程作业——团队02
    你说的都队——团队展示
    TCP协议
  • 原文地址:https://www.cnblogs.com/aui-js/p/14047692.html
Copyright © 2011-2022 走看看