uniapp自定义级联多选下拉框

       工作时遇到的级联多选下拉框需求,由于寻找多方的uniapp第三方库都没找到合适的ui组件,所以用css+js自己写了一个。废话不多说,先看效果,然后直接上代码(因为业务需求单一,所以配置项直接写在组件内,需要改动的小伙伴请自己动手哦!)。

效果如下

选择效果
显示效果

代码如下

<template>
	<view class="select">
		<view class="value-box" @click="openOptions">
			<view v-if="value.length!=0" class="item-list">
				<view class="item font-active-color">
					<view v-for="item in list" class="item-value">
						<view>{{item}}</view>
						<uni-icons type="clear" color="gray" @click.native.stop="del(item)"></uni-icons>
					</view>
				</view>
			</view>
			<view v-else class="item font-init-color">请输入日期</view>
			<uni-icons type="arrowup" v-if="status==0" class="icon"></uni-icons>
			<uni-icons type="arrowdown" class="icon" v-else></uni-icons>

		</view>
		<view class="box" v-if="status==1">
			<view class="opration">
				<view class="cancel" @click="cancel">取消</view>
				<view class="confirm" @click="confirm">确定</view>
			</view>
			<view class="data-options">
				<view class="left-content">
					<view v-for="(item,index) in leftList" class="year-box"
						:class="yearIndex==index ? 'active':'default'" @click="getIdx(item,index)">
						{{item.year}}
					</view>
				</view>
				<view class="right-content">
					<view v-for="item in rightList" class="month-box" :class="item.status==1 ? 'active':'default'"
						@click="changeStatus(item)">{{item.month}}</view>
				</view>
			</view>
		</view>
	</view>
</template>


<script>
	export default {
		data() {
			return {
				status: 0,
				leftList: [], //下拉列表右边
				rightList: [], //下拉列表左边
				yearIndex: 0,
				timeList: [], //整体下拉列表
				list: [], //组件内的数据
			}
		},
		props: {
			//传入的数据
			value: {
				type: Array,
				default: () => []
			}
		},
		watch: {
			list() {
				this.reset();
			}
		},
		mounted() {
			this.list = this.value;
			this.initTimeList();
		},
		methods: {
			/**
			 * 删除选中的值
			 * @param item 元素值
			 */
			del(item) {
				this.list.forEach((e, index) => {
					if (e == item) {
						this.list.splice(index, 1);
					}
				})
				this.$emit('input', this.list);
			},
			/**
			 * 选中下拉框里的值赋值
			 */
			confirm() {

				let result = [];

				this.timeList.forEach(item => {
					if (item.status == true) {
						result.push(item.key);
					}
				})
				this.list.forEach(v=>{
					if(v<this.timeList[0].key){
						result.unshift(v)
					}
				})
				this.list = result;
				this.status = false;
				this.$emit('input', this.list);
			},
			/**
			 * 关闭下拉框
			 */
			cancel() {
				this.status = false;
			},
			/**
			 * 激活当前年份
			 * @param item 元素 @param index 索引
			 */
			getIdx(item, index) {
				this.rightList = [];
				this.yearIndex = index;
				this.timeList.forEach(v => {
					if (v.year == item.year) {
						this.rightList.push(v);
					}
				})

			},
			/**
			 * 初始化下拉框
			 */
			initTimeList() {
				this.leftList = [];
				let year = new Date().getFullYear();
				for (let i = 0; i < 10; i++) {
					let obj = {};
					obj.year = year - 10 + i
					this.leftList.push(obj);
					for (let j = 0; j < 12; j++) {
						let obj = {};
						if (j + 1 < 10) {

							obj.month = '0' + Number(j + 1);
						} else {
							obj.month = j + 1;
						}
						obj.status = false;
						obj.year = year - 10 + i;
						if (j + 1 < 10) {
							obj.key = obj.year + '/0' + Number(j + 1)
						} else {
							obj.key = obj.year + '/' + Number(j + 1)
						}
						this.timeList.push(obj);
					}
				}
				if (this.list.length != 0) {
					this.timeList.forEach(item => {
						this.list.forEach(v => {
							if (v == item.key) {
								item.status = true;
							}
						})
					})
				}

			},
			/**
			 * @param {Object} item 选中后修改状态
			 */
			changeStatus(item) {
				item.status = !item.status;
			},
			/**
			 * 打开下拉框
			 */
			openOptions() {
				this.status = !this.status;
				this.reset();
				this.yearIndex = 0;
				this.rightList = this.timeList.slice(0, 12);

			},

			/**
			 * 重新赋值选中的状态
			 */
			reset() {
				this.timeList.forEach(v => {
					v.status = 0;
				})
				this.list.forEach(item => {
					this.timeList.forEach(v => {
						if (item == v.key) {
							v.status = 1;
						}
					})
				})
			}

		}
	}
</script>


<style lang="scss" scoped>
	.select {
		position: relative;

		.value-box {
			width: 100%;
			height: 64rpx;
			border: 1rpx solid gainsboro;
			line-height: 64rpx;
			display: flex;

			.item-list {
				width: 90%;

				display: flex;
				overflow: hidden;

				.font-active-color {
					color: black;
					display: flex;

					.item-value {
						border-radius: 8rpx;
						display: flex;
						align-items: center;
						margin-top: 8rpx;
						height: 48rpx;
						background-color: pink;
						margin-right: 10rpx;

					}
				}
			}

			.font-init-color {
				color: gray;
			}


			.item {
				margin: 0 10rpx;
			}

			.icon {
				margin-left: auto;
				margin-right: 10rpx;
			}
		}

		.box {
			position: absolute;
			background-color: white;
			margin-top: 8rpx;
			width: 100%;

			border: 1rpx solid gainsboro;
			z-index: 99;


			.opration {
				.cancel {
					margin-top: 20rpx;
					margin-left: 10rpx;
				}

				.confirm {
					margin-top: 20rpx;
					margin-right: 10rpx;
					color: deepskyblue;

				}

				display: flex;
				justify-content: space-between;
				height: 64rpx;
				width: 100%;
			}

			.data-options {
				width: 100%;
				display: flex;
				height: 400rpx;

				.left-content {

					text-align: center;
					width: 50%;
					overflow: scroll;
					margin-top: 20rpx;

					.active {
						color: lightblue;
						font-weight: 600;
					}

					.default {
						color: black;
					}

					.year-box {
						margin: 20rpx 0rpx;
					}
				}

				.right-content {

					text-align: center;
					width: 50%;
					overflow: scroll;
					margin-top: 20rpx;

					.active {
						color: lightblue;
						font-weight: 600;
					}

					.default {
						color: black;
					}

					.month-box {
						margin: 20rpx 0rpx;
					}
				}
			}

		}

	}
</style>

总结

         当时做的比较早,写js代码的时候好像看出一个逻辑错误,但是我的业务逻辑用居然是没问题的,所以没深究(我很懒的~)。本例主要起到一个抛转引玉的作用,需要适应不同业务的小伙伴可以适实际情况修改。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐