【uniapp】 实现移动端自定义下拉层级筛选功能组件的封装和调用

筛选弹窗

 需求:

(1)当前菜单只有  “一级标题”  的情况

点击一级标题时,展示选中效果,关闭筛选弹窗,加载课程列表

(2)当前菜单有  “一级标题和二级标题”  的情况

点击一级标题时,展示选中效果并展示二级标题列表

点击二级标题时,展示选中效果,关闭筛选弹窗,加载课程列表

(2)当前菜单有  “一级标题、二级标题和三级标题”  的情况

点击一级标题时,展示选中效果并展示二级标题列表

点击二级标题时,展示选中效果并展示三级标题列表

点击三级标题时,展示选中效果,关闭筛选弹窗,加载课程列表

解决思路:
1、当点击一级标题时,获取点击的id,传给需要id参数的接口

2、当点击二级标题时,找到一级下的二级标题id,传给需要id参数的接口

3、当点击三级标题时,找到一级下的二级下的三级标题id,传给需要id参数的接口

一、筛选​​​​​​​弹窗组件 MenuLeft.vue

调用页面传递过来的 menuList数组数据

   "menuList": [
        {
            "Id": 1,
            "Name": "奉节第一中学",
            "Children": [
                {
                    "Id": 2,
                    "Name": "初中",
                    "Children": null
                },
                {
                    "Id": 12,
                    "Name": "高中",
                    "Children": null
                }
            ]
        },
        {
            "Id": 22,
            "Name": "奉节第二中学",
            "Children": [
                {
                    "Id": 23,
                    "Name": "初中",
                    "Children": null
                },
                {
                    "Id": 33,
                    "Name": "高中",
                    "Children": null
                }
            ]
        }
    ]

组件封装详情

<template>
	<view class="search_box">
		<view class="search_text" @click="isShowCilck" :class="[person.isDialogShow?'search_text_active':'']">
			{{prop.defaultValue?prop.defaultValue:prop.popupTitle}}
			<i v-if="person.isDialogShow" :class="['iconfont', 'icon-Frame5']" style="margin-left:10rpx;font-size: 30rpx;"/>
			<i v-if="!prop.defaultValue&&!person.isDialogShow" :class="['iconfont', 'icon-Frame-51']" style="margin-left:10rpx; font-size:30rpx;"/>
			<icon v-if="prop.defaultValue" @click.stop="clearCilck" type="clear" size="14" color="#ddd"/>
		</view>
		<view class="search_dialog" v-if="person.isDialogShow" @click.stop="person.isDialogShow=false">
			<div class="dialog_box">
				<view class="sorcll_left">
					<view class="dialog_item" v-if="menusList&&menusList.length>0">
						<view class="dialog_padding" v-for="(menu,meIndex) of menusList" :key="meIndex" @click.stop="menusChange(menu,meIndex)"
						:class="person.menusActive===meIndex?'dialog_left_active':''">
							{{menu.text}}
							<view class="sorcll_center" v-if="person.menusActive===meIndex">
								<view class="dialog_item" v-if="menu.children&&menu.children.length>0">
									<view class="dialog_padding" v-for="(child,chIndex) of menu.children" :key="chIndex"
									:class="person.childActive===chIndex?'dialog_center_active':''" @click.stop="childChange(child,chIndex)">
										{{child.text}}
										<view class="sorcll_right" v-if="person.childActive===chIndex">
											<view class="dialog_item" v-if="child.children&&child.children.length>0">
												<view class="dialog_padding" v-for="(grand,graIndex) of child.children" :key="graIndex"
												:class="person.grandActive===graIndex?'dialog_right_active':''" @click.stop="grandChange(grand,graIndex)">
													{{grand.text}}
												</view>
											</view>
										</view>
									</view>
								</view>
							</view>
						</view>
					</view>
				</view>
			</div>
		</view>
	</view>
</template> 

<script lang="ts" setup>
import { reactive,watch,defineEmits } from 'vue'

let person:any=reactive({
	isDialogShow:false,
	menusActive:null,
	childActive:null,
	grandActive:null,
	activeItem:{}, // 选中的文字
	seltitle:''
})

// 获取传递的参数
type TProps = {
	popupTitle:string,
	menusList:Array<any>,
	defaultValue:string,
	isDialogShow:boolean
}
const prop = withDefaults(defineProps<TProps>(), {})

// 显示弹窗
const isShowCilck=()=>{
	person.isDialogShow=!person.isDialogShow
}

const emit = defineEmits(['oneMenu','twoMenu','threeMenu','clearText'])

// 清空
const clearCilck=()=>{
	prop.defaultValue=''
	person.menusActive=null
	person.childActive=null
	person.grandActive=null
	emit('clearText',person.activeItem)
}
// 一级
const menusChange=(item:any,index:number)=>{
	person.menusActive=index
	// 是否在第一级就关闭弹窗
	if(item.children===null){
		person.isDialogShow=false
	}
	person.activeItem=item
	emit('oneMenu',person.activeItem)
}
// 二级
const childChange=(item:any,index:number)=>{
	person.childActive=index
	person.isDialogShow=false
	emit('twoMenu',person.activeItem,item)
}
// 三级
const grandChange=(item:any,index:number)=>{
	person.grandActive=index
	emit('threeMenu',item)
}

watch(()=>prop.isDialogShow,(newval)=>{
	person.isDialogShow=newval
})
</script>

<style lang="scss" scoped>
.search_box{
	margin: 0 40rpx;
	.search_text{
		color: #4F4F4F;
		font-size: 28rpx;
		display: flex;
		align-items: center;
		image{
			width: 28rpx;
			height: 16rpx;
			margin-left: 10rpx;
		}
		icon{
			margin-left: 10rpx;
		}
	}
	.search_text_active{
		color: #6B86FF !important;
	}
	.search_dialog{
		font-size: 28rpx;
		position: absolute;
		top: 170rpx;
		left: 0;
		width: 100%;
		height: 100vh;
		background: rgba(0,0,0,.2);
		.dialog_box{
			background: #FBFBFB;
			height: calc(100vh - 700rpx);
			border-radius:0rpx 0rpx 40rpx 40rpx;
			text-align: center;
			display: flex;
			width: 100%;
			position: relative;
			.sorcll_left{
				width: 33.3%;
				background: #F3F3F3;
			}
			.sorcll_center{
				position: absolute;
				top: 0;
				left: 33.3%;
				width: 33.3%;
				background: rgba(241, 241, 241, 0.41);
			}
			.sorcll_right{
				position: absolute;
				top: 0;
				left: 100%;
				width: 100%;
			}
			.dialog_item{
				height: calc(100vh - 700rpx);
				overflow-y: scroll;
				color: #4F4F4F;
			}
			.dialog_padding{
				padding: 20rpx 0;
			}
			.dialog_left_active{
				background: #EDF0FF !important;
				color:  #6B86FF !important;
			}
			.dialog_center_active{
				background: #fff !important;
				color: #6B86FF !important;
			}
			.dialog_right_active{
				background: #EDF0FF !important;
				color:  #666 !important;
			}
		}
	}
}
</style>

二、页面调用

<template>
<view class="top">
	<head headText="录播"></head>
	<view class="search" v-if="person.itemMenu&&person.itemMenu.length>0">
		<search v-if="person.itemMenu&&person.itemMenu.length>0" @clearText="leftClear" :isDialogShow="person.selectIsShow1" @oneMenu="oneMenuLeft" @twoMenu="twoMenuLeft" :defaultValue="person.selectValue1" :menusList="person.itemMenu" popupTitle="请选择学校/年级"/>
	</view>
</view>
<view class="list" v-if="person.recordList&&person.recordList.length>0">
	<view class="item" v-for="(record,reIndex) of person.recordList" :key="reIndex" @click="toDetails(record)">
		<image class="uni_image" v-if="record.CoverUrl" :src="record.CoverUrl"></image>
		<image class="uni_image" v-else src="@/static/lizi_pic.png"></image>
		<view class="item_right">
			<view class="item_title">{{record.Name}}</view>
			<view class="item_techer">主讲:{{record.TeacherName}}</view>
			<view class="item_bottom">
				<label><i :class="['iconfont', 'icon-Frame-11']" style="color:#FF8A1F;margin-right: 10rpx;"/> 课时:{{record.ClassHour}}</label>
				<label><i :class="['iconfont', 'icon-Frame1']" style="color:#AF7EFF;margin-right: 10rpx;"/> 时长:{{record.SumTime}}分钟</label>
			</view>
		</view>
	</view>
</view>
<view v-else class="empty_box">
	<image src="@/static/null_icon.png" mode=""></image>
</view>
</template>

<script  lang="ts" setup>
import { reactive } from 'vue'
import { getRecordList,getRecordMenu } from '@/api/record';
let person:any=reactive({
	// 录播列表
	recordList:[],
	itemMenu:[],
	gradeList:[],
	selectValue1:'',
	selectValue2:'',
	selectIsShow1: false,
	selectIsShow2: false
})

// 获取menu接口
const GetRecordMenu= async (Id: number)=> {
	if(Id === 0){
		let result = await api(Id)
		person.itemMenu = result
		if(person.itemMenu&&person.itemMenu.length>0){
			const perent=person.itemMenu[0].text
			if(person.itemMenu[0].children&&person.itemMenu[0].children.length>0){
				person.selectValue1=perent+'/'+person.itemMenu[0].children[0].text
				getData(person.itemMenu[0].children[0].value)
			}else{
				person.selectValue1=perent
				getData(person.itemMenu[0].value)
			}
		}
	}
}
const api = (Id:number)=>{
	return new Promise((resolve,reject)=>{
		getRecordMenu({ ParentId: Id }).then((res:any)=>{
			let {Code,Data}=res.data
			if(Code===200){
				let a = JSON.parse(JSON.stringify(Data).replace(/Name/g, 'text').replace(/Id/g, 'value').replace(/Children/g,'children'))
				resolve(a)
			}
		})
	})
}

// 跳转至详情页
let toDetails=(item:any)=>{
	uni.navigateTo({
		url: `/pages/recording/recordDetails?NodeId=${item.Id}&Id=${item.Chapter[0].Id}`
	});
}
// 获取录播列表
let GetRecordList = (id:number) => {
	getRecordList({ Id: id }).then((res:any)=>{
		let {Code,Data}=res.data
		if(Code===200){
			person.recordList=Data
		}
	})
}

const getData=(node:number) =>{
	GetRecordMenu(node)
	GetRecordList(node)
}
getData(0)

// 左侧
// 清空
const leftClear=()=>{
	GetRecordList(0)
	person.selectValue1=''
}
// 一级
const oneMenuLeft=(node:any)=>{
	person.selectValue1=node.text
	getData(node.value)
}
// 二级
const twoMenuLeft=(node:any,node1:any)=>{
	if(node1.value){
		person.selectValue1=node.text+'/'+node1.text
     	getData(node1.value)
	}
}

</script>
<style lang="scss" scoped>
page{
	background: #F5F5F5;
}
</style>

       希望我的愚见能够帮助你哦~,若有不足之处,还望指出,你们有更好的解决方法,欢迎大家在评论区下方留言支持,大家一起相互学习参考呀~

  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UniApp中,可以使用`<select>`标签来创建下拉选择框。下面是对UniApp下拉选择框的介绍: 1. `<select>`标签:`<select>`标签用于创建下拉选择框,可以包含多个`<option>`标签作为选项。 2. `<option>`标签:`<option>`标签用于定义下拉选择框的选项。可以设置`value`属性来指定选项的值,同时在`<option>`标签内添加文本内容作为选项的显示文本。 3. 绑定数据:可以通过`v-model`指令将下拉选择框与数据进行双向绑定,实现选择值的获取和设置。 4. 事件处理:可以使用`@change`事件来监听下拉选择框的值变化,从而执行相应的逻辑操作。 下面是一个示例代码,展示了如何在UniApp中创建一个简单的下拉选择框: ```html <template> <view> <select v-model="selectedValue" @change="handleChange"> <option value="option1">选项1</option> <option value="option2">选项2</option> <option value="option3">选项3</option> </select> <text>选择的值:{{ selectedValue }}</text> </view> </template> <script> export default { data() { return { selectedValue: 'option1' }; }, methods: { handleChange(event) { console.log('选择的值:', event.target.value); } } }; </script> ``` 在上述示例中,通过`v-model`指令将`selectedValue`与下拉选择框进行双向绑定,选中的值会自动更新到`selectedValue`中。同时,通过`@change`事件监听下拉选择框的值变化,当值发生变化时,会触发`handleChange`方法,并打印选择的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值