Skip to content

电梯导航、联动菜单 Elevator

  • 电梯导航、联动菜单组件。

兼容性

  • 兼容:✅
  • 不兼容:❌
  • 未知:❔
H5安卓苹果微信支付宝百度抖音QQ快手飞书京东
小红书鸿蒙360华为快应用快应用联盟

插件市场地址

DCloud 插件市场地址

组件介绍

  • 共包含 5 个组件:

    • pure-elevator:最外层根组件。
    • pure-elevator-floor:楼层组件,对应左侧选项卡内容。
    • pure-elevator-footer:脚组件,用来放置底部加载状态等内容。
    • pure-elevator-header:楼层标题组件,使用二级分类功能时,用来放置二级分类。
    • pure-elevator-group:组组件,使用二级分类功能时,用来放置二级分类内容。
  • 各组件结构如下:

vue
<template>
	<!-- 根组件 -->
	<!--
		* list: 数据列表,必传
		* value: 左侧选项卡激活项的值,必传
		* subValue: 子分类激活项的值,可选
		* @tabClick:左侧选项卡点击事件
		* @tabChange: 左侧选项卡激活项改变事件,由用户触摸滚动右侧内容触发的改变事件
		* @subTabChange: 二级分类激活项改变事件,由用户触摸滚动右侧内容触发的改变事件
		* @scrolltolower: 上拉触底事件
		-->
	<pure-elevator
		:list="list"
		:value="tabValue"
		:subValue="subTabValue"
		@tabClick="handleTabClick"
		@tabChange="handleTabChange"
		@subTabChange="handleSubTabChange"
		@scrolltolower="handleScrollToLower"
	>
		<!-- 楼层组件 -->
		<!--
		* value: 楼层的唯一标识,必传
		* floor: 楼层数据,必传
		-->
		<pure-elevator-floor :value="floor.id" :floor="floor" v-for="floor in list" :key="floor.id">
			<!-- 楼层组件头部,用来放置二级菜单 -->
			<!-- floorValue: 楼层的唯一标识,必传 -->
			<pure-elevator-header :floorValue="floor.id">
				<!-- 演示,此处可以放置二级分类 tabs -->
				<pure-elevator-tabs :value="subTabValue" @tabClick="handleSubTabClick">
					<pure-tabs-item v-for="subTab in floor.children" :key="subTab.id" :value="subTab.id" :item="subTab"></pure-tabs-item>
				</pure-elevator-tabs>
			</pure-elevator-header>

			<!-- 组组件,用来放置楼层内的二级分类分组内容 -->
			<!--
			* value: 分组的唯一标识,必传
			* group: 分组数据,必传
			* floorValue: 分组所在楼层的唯一标识,必传
			-->
			<pure-elevator-group v-for="group in floor.children" :key="floor.id" :value="group.id" :group="group" @floorValue="floor.id">
				<!-- 演示,此处放置分组内容 -->
				<view v-for="item in group.children" :key="item.id">
					{{ item.name }}
				</view>
			</pure-elevator-group>
		</pure-elevator-floor>

		<!-- 楼脚组件,一般用来放置加载状态 -->
		<pure-elevator-footer>
			<!-- 演示,此处放置加载状态 -->
			<pure-loadmore :status="loadmoreStatus"></pure-loadmore>
		</pure-elevator-footer>
	</pure-elevator>
</template>

基础使用

  • 基础使用示例。
vue
<template>
	<pure-elevator :list="list" :value="tabValue" @tabClick="handleTabClick" @tabChange="handleTabChange">
		<!-- 自定义左侧 Tabs -->
		<!-- 注意: 抖音小程序和飞书小程序必须使用自定义模式,这是因为在抖音和飞书中,组件套组件时,provide/inject不生效 -->
		<template #tabs>
			<!-- 这里建议用一个 view 组件进行包裹,因为在实际测试中,快手小程序如果不再包裹一层,会导致 provide/inject 的数据丢失 -->
			<view>
				<pure-tabs :value="tabValue" @tabClick="handleTabClick" vertical>
					<pure-tabs-item v-for="(item, index) in list" :key="index" :value="item.id" :item="item" :disabled="item.disabled">
						<text class="tab">{{ item.name }}</text>
					</pure-tabs-item>
				</pure-tabs>
			</view>
		</template>
		<pure-elevator-floor v-for="floor in list" :key="floor.id" :value="floor.id" :floor="floor">
			<view class="floor">
				<view class="title">{{ floor.name }}</view>
				<view class="rooms">
					<view class="room" v-for="room in floor.products" :key="room.id">{{ room.name }}
					</view>
				</view>
			</view>
		</pure-elevator-floor>
	</pure-elevator>
</template>

<script setup>
import { ref } from "vue";
import { onReady } from "@dcloudio/uni-app";

// ####################################################################################################
// 基础使用
const list = ref([]);
const tabValue = ref(2);

// 初始化数据
onReady(() => {
	initList();
});

// 左侧标签点击事件
function handleTabClick(item) {
	// 更新对应下标
	tabValue.value = item.id;
}

// 初始化数据
function initList() {
	list.value = [
		{
			id: 1,
			name: "手机数码",
			products: [
				{ id: 1, name: "iPhone 14 Pro Max" },
				{ id: 2, name: "华为 Mate 50" },
				{ id: 3, name: "小米 13 Ultra" },
				{ id: 4, name: "OPPO Find X6" },
				{ id: 5, name: "vivo X90 Pro" },
				{ id: 6, name: "三星 Galaxy S23" },
				{ id: 7, name: "一加 11" },
				{ id: 8, name: "荣耀 Magic5" },
				{ id: 9, name: "红米 K60" },
				{ id: 10, name: "realme GT Neo5" },
				{ id: 11, name: "魅族 20" },
				{ id: 12, name: "努比亚 Z50" },
				{ id: 13, name: "索尼 Xperia 1 V" },
				{ id: 14, name: "黑鲨 5 Pro" },
				{ id: 15, name: "联想拯救者 Y90" },
				{ id: 16, name: "华硕 ROG Phone 6" },
				{ id: 17, name: "Google Pixel 7" },
				{ id: 18, name: "诺基亚 X30" },
				{ id: 19, name: "摩托罗拉 Edge 40" },
				{ id: 20, name: "中兴 Axon 40 Ultra" }
			]
		},
		// ...
	];
}

// 左侧标签切换事件
// 主要用于自定义 tabs 用来切换选中项
function handleTabChange(value) {
	// 更新对应下标
	tabValue.value = value;
}
</script>

异步加载数据

  • 异步加载数据示例。
vue
<template>
	<pure-elevator
		:list="asyncList"
		:value="asyncListTabValue"
		@tabClick="handleAsyncListTabClick"
		@tabChange="handleAsyncListTabChange"
		@scrolltolower="handleAsyncListScrollToLower"
	>
		<!-- 在京东和QQ小程序中,需要使用view标签包裹内容,不要给 view 添加任何内外边距,否则会滚动不准确 -->
		<view>
			<pure-elevator-floor
				v-for="floor in asyncList"
				:key="floor.id"
				:value="floor.id"
				:floor="floor"
			>
				<view class="floor" v-if="floor.products?.length">
					<view class="title">{{ floor.name }}</view>
					<view class="rooms">
						<view class="room" v-for="room in floor.products" :key="room.id">{{ room.name }}
						</view>
					</view>
				</view>
			</pure-elevator-floor>

			<!-- 加载数据的状态提示信息放到 Footer 组件中 -->
			<pure-elevator-footer>
				<view class="loamore">
					<pure-loadmore :status="asyncListLoadMoreStatus"></pure-loadmore>
				</view>
			</pure-elevator-footer>
		</view>
	</pure-elevator>
</template>

<script setup>
import { ref } from "vue";
import { onReady } from "@dcloudio/uni-app";

// ####################################################################################################
// 异步加载数据演示
const asyncList = ref([]);
const asyncListTabValue = ref(2);
const asyncListLoadMoreStatus = ref("more");
const toUpper = ref(false);

// 初始化数据
onReady(() => {
	// 先初始化标签和默认激活项数据
	initAsyncList();
});

// 始化标签和默认激活项数据
function initAsyncList() {
	let _asyncList = [];
	for (let i = 0; i < _list.length; i++) {
		_asyncList.push({
			id: i,
			name: _list[i].name,
			products: []
		});
		if (i === asyncListTabValue.value) {
			_asyncList[i].products = _list[i].products;
		}
	}
	asyncList.value = _asyncList;
}

// 左侧标签点击事件
function handleAsyncListTabClick(tab) {
	// loading 一下
	uni.showLoading({
		title: "加载中...",
		mask: true
	});

	// 模拟异步加载数据
	setTimeout(() => {
		// 隐藏 loading
		uni.hideLoading();

		// 找到下标
		const index = asyncList.value.findIndex((item) => tab.id === item.id);

		// 设置数据
		// 只是简单演示,并没有判断数据是否加载过等判断
		asyncList.value[index].products = _list[index].products;

		// 更新绑定值
		asyncListTabValue.value = tab.id;
	}, 2500);
}

// 滚动切换下标事件
// newValue: 新的值
function handleAsyncListTabChange(newValue) {
	// 在这里实时更新用户滚动后左侧激活项的最新值
	asyncListTabValue.value = newValue;
}

// 上拉加载
function handleAsyncListScrollToLower() {
	// 没有更多了
	if (asyncListLoadMoreStatus.value === "nomore" || asyncListLoadMoreStatus.value === 'loading') return;

	// 更新状态
	asyncListLoadMoreStatus.value = "loading";



	// 模拟异步加载数据
	setTimeout(() => {
		// 找到下标
		const index = asyncList.value.findIndex((item) => asyncListTabValue.value === item.id);

		// 设置数据
		// 只是简单演示,并没有判断数据是否加载过等判断
		asyncList.value[index + 1].products = _list[index + 1].products;

		// 更新激活项的值
		// 在这里更新值,会将新的激活项对应内容滚动到最顶部
		// 如果不更新,只会追加数据,不会滚动,根据个人喜好决定是否更新
		if (toUpper.value) asyncListTabValue.value = asyncList.value[index + 1].id;

		// 更新加载状态
		if (index + 1 >= _list.length) {
			asyncListLoadMoreStatus.value = "nomore";
		} else {
			asyncListLoadMoreStatus.value = "more";
		}
	}, 2500);
}
</script>

二级分类

  • 二级分类使用演示。

提示

抖音小程序和飞书小程序不支持二级分类。 这是因为组件套组件时 provide 和 inject 会失效。 抖音和飞书的老问题了,至今还不修复~~~

vue
<template>
	<pure-elevator
		:list="subList"
		:value="subListFirstTabValue"
		:subValue="subListSecondTabValue"
		subListKey="products"
		@tabClick="handleSubListTabClick"
		@tabChange="handleSubListTabChange"
		@subTabChange="handleSubListSubTabChange"
	>
		<pure-elevator-floor
			v-for="floor in subList"
			:key="floor.id"
			:value="floor.id"
			:floor="floor"
		>
			<!-- Floor Header -->
			<pure-elevator-header :floorValue="floor.id">
				<view class="floor-tabs">
					<!-- 同样使用 pure-tabs 组件实现的子分类 -->
					<pure-tabs :value="subListSecondTabValue" @tabClick="handleSubListSubTabClick">
						<pure-tabs-item
							v-for="(subTab, subTabIndex) in floor.products"
							:key="subTab.id"
							:value="subTab.id"
							:item="subTab"
							:disabled="subTab.disabled"
						>
							<text class="subtab">{{ subTab.name }}</text>
						</pure-tabs-item>
					</pure-tabs>
				</view>
			</pure-elevator-header>

			<!-- Groups -->
			<pure-elevator-group
				v-for="group in floor.products"
				:key="group.id"
				:value="group.id"
				:group="group"
				:floorValue="floor.id"
			>
				<!-- 装修分组标题 -->
				<view class="group-title">{{ group.name }}</view>

				<!-- 装修房间 -->
				<view class="group-rooms">
					<view class="group-room" v-for="room in group.goods" :key="room.id">{{room.name }}</view>
				</view>
			</pure-elevator-group>
		</pure-elevator-floor>
	</pure-elevator>
</template>

<script setup>
import { ref } from "vue";
import { onReady } from "@dcloudio/uni-app";

// ####################################################################################################
// 二级分类使用
const PureElevatorRef = ref(); // 定义一个组件 ref
const subList = ref([]);
const subListFirstTabValue = ref(1);
const subListSecondTabValue = ref(1); // 初始化所有子选项卡的下标

// 初始化数据
onReady(() => {
	initSubList();
});

// 初始化数据
function initSubList() {
	const _list = [];
	for (let i = 0; i < 18; i++) {
		const item = {
			id: i + 1,
			name: `分类${i + 1}`,
			products: []
		}
		const jLength = 6;
		for (let j = 0; j < jLength; j++) {
			const product = {
				id: `${i + 1} _ ${j + 1}`,
				name: `#分类${i + 1}#子类${j + 1}`,
				goods: []
			}
			for (let k = 0; k < 5; k++) {
				const child = {
					id: `${i + 1} _ ${j + 1}_${k + 1}`,
					name: `#分类${i + 1}#子类${j + 1}#商品${k + 1}`
				}
				product.goods.push(child);
			}
			item.products.push(product);
		}
		_list.push(item);
	}
	subList.value = _list;
}

// 一级菜单点击事件
function handleSubListTabClick(item) {
	subListFirstTabValue.value = item.id;
}

// 二级菜单点击事件
function handleSubListSubTabClick(item) {
	subListSecondTabValue.value = item.id;
}

// 一级分类的值更新事件
function handleSubListTabChange(value) {
	subListFirstTabValue.value = value;
}

// 子分类的值更新事件
function handleSubListSubTabChange(value) {
	subListSecondTabValue.value = value;
}
</script>

Props

pure-elevator

属性名说明类型默认值可选值版本
list数据列表Array[]-+1.0.0
value激活项的值,注意:需要和列表对应字段值的类型相同[Number, String]null-+1.0.0
valueKeyvalue 字段名Stringid-+1.0.0
nameKey显示文本字段的字段名Stringid-+1.0.0
subValue子类激活项的值,重要提示: 需保证唯一性,不能有重复值[Number, String]null-+1.0.0
subValueKeysubValue 字段名Stringid-+1.0.0
subListKey子列表的字段名Stringchildren-+1.0.0
scrollTop点击切换时楼层滚动到距离滚动容器顶部的距离,整数值表示 px,小数值表示相对于滚动容器高度的比例Number0-+1.0.0
offsetTop楼层距离滚动容器顶部多少距离更新选中项,整数值表示 px,小数值表示相对于滚动容器高度的比例Number0-+1.0.0
subScrollTop分组距离滚动容器顶部多少距离更新选中项,整数值表示 px,小数值表示相对于滚动容器高度的比例Number0-+1.0.0
subOffsetTop点击切换时分组滚动到距离滚动容器顶部的距离,整数值表示 px,小数值表示相对于滚动容器高度的比例Number0-+1.0.0
animation是否使用滚动过渡动画Booleanfalse-+1.0.0
showScrollbar是否显示滚动条Booleanfalse-+1.0.0
upperThreshold距顶部/左边多远时(单位px),触发 scrolltoupper 事件,实测快手小程序 scroll-view 无法触发此事件,可能时快手的BUG[Number, String]50-+1.0.0
lowerThreshold距底部/右边多远时(单位px),触发 scrolltolower 事件,实测快手小程序 scroll-view 无法触发此事件,不是组件问题,但时用代码模拟实现了该事件[Number, String]50-+1.0.0
refresherEnabled开启自定义下拉刷新Booleanfalse-+1.0.0
refresherThreshold设置自定义下拉刷新阈值Number45-+1.0.0
refresherDefaultStyle设置自定义下拉刷新默认样式,支持设置 blackwhitenonenone 表示不使用默认样式Stringblack-+1.0.0
refresherBackground设置自定义下拉刷新区域背景颜色String#FFF-+1.0.0
refresherTriggered设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发Booleanfalse-+1.0.0

提示

scroll-view 组件的兼容性参考官方地址

pure-elevator-floor

属性名说明类型默认值可选值版本
value值,对应 pure-elevatorvalue[Number, String]null-+1.0.0
floor楼层数据,对应 pure-elevatorlist 的列表 item[Array, Object][]-+1.0.0

pure-elevator-group

属性名说明类型默认值可选值版本
value值,唯一标识[Number, String]null-+1.0.0
group分组数据[Array, Object][]-+1.0.0
floorValue分组所在楼层的值[Number, String]null-+1.0.0

pure-elevator-header

属性名说明类型默认值可选值版本
floorValue所在楼层的值[Number, String]null-+1.0.0
sticky是否吸附到顶部Booleantrue-+1.0.0

Event

pure-elevator

事件名说明回调参数版本
tabClick左侧选项卡点击事件item: 选项卡数据+1.0.0
tabChange左侧选项卡切换事件value: 选项卡的值+1.0.0
subTabChange子选项卡切换事件value: 选项卡的值+1.0.0
scrollUpper下拉触顶事件-+1.0.0
scrollLower上拉触底事件-+1.0.0
refresherPulling自定义下拉刷新控件被下拉-+1.0.0
refresherRefresh自定义下拉刷新被触发-+1.0.0
refresherRestore自定义下拉刷新被复位-+1.0.0
refresherAbort自定义下拉刷新被中止-+1.0.0

Slots

pure-elevator

名称说明参数版本
tabs左侧选项卡,可以在此处使用 pure-tabs-item 组件自定义选项卡-+1.0.0
default默认,右侧区域-+1.0.0

pure-elevator-floor

名称说明参数版本
default默认,楼层内容-+1.0.0

pure-elevator-header

名称说明参数版本
default默认,楼层标题-+1.0.0

pure-elevator-group

名称说明参数版本
default默认,分组内容-+1.0.0
名称说明参数版本
default默认,右侧最底部内容-+1.0.0

基于 MIT 许可发布