自定义多级联动选择器指南(uni-app)

  • 多端支持:可以运行在H5、APP、微信小程序还是支付宝小程序,都可以轻松使用改组件。
  • 自定义配置:您可以根据需要配置选择器的级数,使其适应不同的数据结构和用例。
  • 无限级联:此组件支持无限级联选择,使您能够创建具有复杂数据结构的选择器。
参数类型描述默认值必选
titlestring标题‘’
layernumber控制几级联动1
dataarray数据:[{name: ‘’, id: ‘’, children: [{name: ‘’, id: ‘’}]}][]

在这里插入图片描述

接口返回数据结构:

[{id: 47, name: "工厂1", parentId: 0, type: 0},{id: 48, name: "区域1", parentId: 47, type: 0},{id: 19, name: "设备1", parentId: 48, type: 1}
]

处理后数据结构:

[{ id: 47, name: "工厂1", parentId: 0, type: 0,children: [{ id: 48, name: "区域1", parentId: 47, type: 0,children: [{ id: 19, name: "设备1", parentId: 48, type: 1 }] }] 
}]

引用示例:

<template><view class="container"><view @click="bindDevice">选择设备</view><cascade-picker ref="picker" :title="cascadePicker.title" :layer="cascadePicker.layer" :data="cascadePicker.data" @callback="pickerCallback"></cascade-picker></view>
</template>
<script>
import cascadePicker from '@/components/cascade-picker/cascade-picker.vue';
import {handleTree} from "@/utils/tree";
export default {components: {cascadePicker},data() {return {deviceArr: [],deviceId: '',cascadePicker: {title: 'picker标题',layer: null,data: []}}},onLoad() {this.getDeviceSimpleList()},methods: {// 获取设备列表getDeviceSimpleList() {getDeviceSimpleList().then(res => {this.deviceArr = handleTree(res.data)})},// 打开设备选择器bindDevice() {const _this = this;_this.cascadePicker.data = _this.deviceArr;_this.$refs.picker.open().then(function() {console.log('打开成功');});},// picker多级联动回调pickerCallback(e) {const {data} = e;const lastItem = data[data.length - 1];this.deviceId = lastItem.id;}}
}
</script>

@/utils/tree文件中的handleTree方法

/*** 构造树型结构数据* @param {*} data 数据源* @param {*} id id字段 默认 'id'* @param {*} parentId 父节点字段 默认 'parentId'* @param {*} children 孩子节点字段 默认 'children'* @param {*} rootId 根Id 默认 0*/
export function handleTree(data, id = 'id', parentId = 'parentId', children = 'children', rootId = 0) {rootId = rootId || Math.min.apply(Math, data.map(item => {return item[parentId]})) || 0//对源数据深度克隆const cloneData = JSON.parse(JSON.stringify(data))//循环所有项const treeData = cloneData.filter(father => {let branchArr = cloneData.filter(child => {//返回每一项的子级数组return father[id] === child[parentId]});branchArr.length > 0 ? father.children = branchArr : '';//返回第一层return father[parentId] === rootId;});return treeData !== '' ? treeData : data;
}

cascade-picker组件完整代码:

components文件夹下创建cascade-picker文件夹,然后在此文件夹下创建cascade-picker.vue - 多级联动组件

<template name="cascade-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-header-icon" @click.stop="close">取消</view><view class="aui-picker-title" v-if="title">{{ title }}</view><view class="aui-picker-header-icon aui-picker-confirm" @click.stop="_confirm">确认</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":data-type="item.type":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":data-type="item.type":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: 'cascade-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: 35, // 导航栏的边框左侧位置result: [],touchConfig: {index: -1,pindex: -1,style: {color: '#214579',background: '#EFEFEF'}},selectedData: [] // 用于回显数据的属性}},watch: {data() {const _this = this;const data = _this.data;_this.items = _this._flatten(data, '0')}},methods: {// 打开open() {const _this = this;_this.reset(); //打开时重置pickerreturn 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 = 35;_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 type = e.currentTarget.dataset.type;const _arr = [];_this.result[_this.navCurrentIndex] = {id: id, name: name, pid: pid, type: type};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, type: item.type});});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 { //无下级数据且最后一级数据的type为1时,则可以确认关闭_this._confirm();}},_confirm() {const _this = this;const lastItem = _this.result[_this.result.length - 1];if (lastItem && lastItem.type === 1) {_this.close().then(() => {_this.$emit("callback", {status: 0, data: _this.result});});} else {uni.$u.toast('请选择设备')}},//递归遍历——将树形结构数据转化为数组格式_flatten(tree, pid) {return tree.reduce((arr, {id, name, type, children = []}) =>arr.concat([{id, name, pid, type}], 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 lang="scss" scoped>
/* ====================多级联动弹窗=====================*/
.aui-picker {width: 100vw;height: 100vh;//opacity: 0;position: fixed;top: 0;left: 0;z-index: 999;/* display: none; */
}// 遮罩层
.aui-mask {width: 100%;height: 100%;background: rgba(0, 0, 0, .3);position: absolute;left: 0;top: 0;z-index: 999;
}.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: 0;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;display: flex;justify-content: space-between;align-items: center;&-icon {font-size: 15px;color: #aaa;padding: 0 15px;}.aui-picker-confirm {height: 50px;line-height: 50px;text-align: center;font-size: 15px;color: $custom-content-color;padding: 0 15px;}
}.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-content {width: 100%;height: -webkit-calc(100% - 100px);height: calc(100% - 100px);
}.aui-picker-nav {width: 100%;height: 50px;text-align: left;padding: 0 15px;margin: 0 0 1px 0;justify-content: flex-start;white-space: nowrap;box-sizing: border-box;position: relative;overflow-x: scroll;overflow-y: hidden;
}.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: $custom-content-color;
}.aui-picker-navborder {width: 40px;height: 3px;background: $custom-content-color;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: $custom-content-color;
}.aui-picker-item.active::after {content: '✔';font-size: 15px;color: $custom-content-color;position: absolute;top: 0px;right: 10px;
}</style>
总结

自定义多级联动选择器组件将有助于您在uni-app中创建灵活的选择器,以满足各种不同平台和级数的需求。如果有任何问题、反馈或需要进一步的帮助,请不要犹豫,在下面的评论中提出。期待听到您的声音,以便改进和完善这个组件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/56359.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类和对象(完结)

文章目录 一对构造函数的补充1初始化链表2必须在初始化链表定义的情况3对于在类中成员变量初始化的总结4总结二类型转换1格式2规则三static成员1规则四友元1定义2 两种例子五匿名对象1格式2特殊情况 一对构造函数的补充 1初始化链表 结构&#xff1a;类名&#xff08;参数&…

数字化转型:解决项目管理困境的新路径

在当今这个飞速发展的数字化时代&#xff0c;企业如同在汹涌波涛中航行的船只&#xff0c;承受着前所未有的变革压力。而作为企业运作核心环节之一的项目管理&#xff0c;同样面临着巨大的挑战。 传统项目管理模式中的种种问题&#xff0c;犹如顽固的礁石&#xff0c;阻碍着项目…

Shiro认证 -- (Authentication)

Apache Shiro是一个功能强大的Java安全框架&#xff0c;提供了身份验证&#xff08;Authentication&#xff09;、授权&#xff08;Authorization&#xff09;、加密&#xff08;Cryptography&#xff09;、会话管理&#xff08;Session Management&#xff09;、与Web集成、缓…

Linux执行source /etc/profile命令报错:权限不够问(已解决)

1.问题 明明以root账号登录Linux系统&#xff0c;在终端执行命令source /etc/profile时 显示权限不够 如下图&#xff1a; 2.问题原因 可能在编辑 /etc/profile 这个文件时不小心把开头的 井号 ‘#’ 给删除了 如图&#xff1a; 这里一定要有# 3.解决办法 进入/etc/pro…

扫雷(C 语言)

目录 一、游戏设计分析二、各个步骤的代码实现1. 游戏菜单界面的实现2. 游戏初始化3. 开始扫雷 三、完整代码四、总结 一、游戏设计分析 本次设计的扫雷游戏是展示一个 9 * 9 的棋盘&#xff0c;然后输入坐标进行判断&#xff0c;若是雷&#xff0c;则游戏结束&#xff0c;否则…

字节内部整理的软件测试面试题(含文档)

常见的面试题汇总 1、你做了几年的测试、自动化测试&#xff0c;说一下 selenium 的原理是什么&#xff1f; 我做了五年的测试&#xff0c;1年的自动化测试&#xff1b; selenium 它是用 http 协议来连接 webdriver &#xff0c;客户端可以使用 Java 或者 Python 各种编程语言…

【网络安全】未加密的F5 BIG-IP Cookie存在严重漏洞将被攻击者利用

文章目录 未加密的F5 BIG-IP Cookie存在严重漏洞将被攻击者利用F5 会话 Cookie推荐阅读 未加密的F5 BIG-IP Cookie存在严重漏洞将被攻击者利用 网络安全和基础设施安全局发布最新警告称&#xff0c;已观察到威胁行为者滥用未加密的持久性F5 BIG-IP Cookie来识别并针对目标网络…

电能质量的危害主要是哪些?我们该如何应对电能质量故障所带来的损失?

电能质量治理在现代配电系统中的必要性日益凸显。随着可再生能源和智能电网技术的广泛应用&#xff0c;电力系统面临着频率波动、谐波污染和电压不稳定等问题。这些问题不仅影响了电力的可靠性和安全性&#xff0c;还可能导致设备损坏和能效降低。因此&#xff0c;实施电能质量…

Pagehelper获取total错误

前言 在使用若依框架的pagehelper时&#xff0c;给分页表设置数据的时候前端只收到了分页的那一页的数据&#xff0c;总记录数不符合要求 我想要的效果如下&#xff0c;可以实现分页&#xff0c;和显示总记录数 但是实际情况为 但是我的数据库有11条记录&#xff0c;他这里明…

QCY开放式耳机值得买吗?南卡、QCY、韶音开放式耳机最全测评!

​开放式耳机最近还挺火的&#xff0c;因为相对于传统的入耳式耳机来说&#xff0c;它佩戴起来更舒适&#xff0c;也更卫生&#xff0c;更加适配运动场景&#xff0c;现在不少的健身或者运动博主都选择了开放式耳机&#xff0c;那么作为一个同样喜欢跑步的数码博主&#xff0c;…

开源一个C缓存库

1 简介 在当下的视频点播应用场景下&#xff0c;端侧对视频缓存的需求可谓刚需&#xff0c;一方面可以为公司节省流量成本&#xff0c;一方面也可以提升用户的播放体验&#xff0c;有一石二鸟之效。 近期&#xff0c;本人用C写了一个缓存库&#xff0c;支持iOS/Android/harmony…

java项目之信息化在线教学平台的设计与实现(源码+文档)

项目简介 信息化在线教学平台的设计与实现实现了以下功能&#xff1a; 信息化在线教学平台的设计与实现的主要使用者管理员功能有个人中心&#xff0c;学生信息管理&#xff0c;教师信息管理&#xff0c;教学信息管理&#xff0c;学生成绩管理&#xff0c;留言板管理&#xf…

番外篇 | 史上最全的关于CV的一些经典注意力机制代码汇总

前言:Hello大家好,我是小哥谈。注意力是人类认知系统的核心部分,它允许我们在各种感官输入中筛选和专注于特定信息。这一能力帮助我们处理海量的信息,关注重要的事物,而不会被次要的事物淹没。受到人类认知系统的启发,计算机科学家开发了注意力机制,这种机制模仿人类的这…

《语音识别芯片选型全攻略》

《语音识别芯片选型全攻略》 一、语音识别芯片性能评估&#xff08;一&#xff09;主控芯片性能评估&#xff08;二&#xff09;接口需求分析&#xff08;三&#xff09;可靠性评估&#xff08;四&#xff09;生产工艺考量&#xff08;五&#xff09;湿敏等级判断 二、语音识别…

倍福TwinCAT程序中遇到的bug

文章目录 问题描述&#xff1a;TwinCAT嵌入式控制器CX5140在上电启动后&#xff0c;X001网口接网线通讯灯不亮&#xff0c;软件扫描不到硬件网口 解决方法&#xff1a;硬件断电重启后&#xff0c;X001网口恢复正常 问题描述&#xff1a;TwinCAT软件点击激活配置后&#xff0c;…

汽车免拆诊断案例 | 2022款大众捷达VS5车行驶中挡位偶尔会锁在D3挡

故障现象  一辆2022款大众捷达VS5汽车&#xff0c;搭载EA211发动机和手自一体变速器&#xff0c;累计行驶里程约为4.5万km。该车行驶中挡位偶尔会锁在D3挡&#xff0c;车速最高约50 km/h&#xff0c;且组合仪表上的发动机故障灯和EPC灯异常点亮。 故障诊断  用故障检测仪检…

SQL语句查询

SQL语句查询 查询产生一个虚拟表 看到的是表形式显示的结果&#xff0c;但结果并不真正存储 每次执行查询只是从数据表中提取数据&#xff0c;并按照表的形式显示出来 查询语法 SELECT <列名> FROM <表名> [WHERE <查询条件表达式>] SELECT …

【python书籍-附电子版】Python入门零基础必看书籍,python编程入门教程指南,从入门到精通,这几本书太牛了!!

今天为大家推荐的“Python 编程三剑客”是新手小白学习编程的不二之选&#xff01;这三本书分别从不同的角度&#xff0c;对 Python 编程进行了详细的解析。 &#xff08;领取方式见文末&#xff09; 一《Python编程&#xff1a;从入门到实践》 第一本为你打下坚实的基础&am…

详解安卓和IOS的唤起APP的机制,包括第三方平台的唤起方法比如微信

网页唤起APP是一种常见的跨平台交互方式&#xff0c;它允许用户从网页直接跳转到移动应用程序。 这种技术广泛应用于各种场景&#xff0c;比如让用户在浏览器中点击链接后直接打开某个应用&#xff0c;或者从网页引导用户下载安装应用。实现这一功能主要依赖于URL Scheme、Univ…

基于Java+SpringBoot+Vue的网上购物商城系统研发

基于JavaSpringBootVue的网上购物商城系统研发 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345…