介绍
垂直标签导航,用于在不同的区域之间进行切换。
引入
js
import Sidebar from 'sard-uniapp/components/sidebar/sidebar.vue'
import SidebarItem from 'sard-uniapp/components/sidebar-item/sidebar-item.vue'代码演示
基础使用
使用 v-model:current 绑定当前选中项的 name,name 必须且唯一。
vue
<template>
<sar-sidebar v-model:current="current">
<sar-sidebar-item name="1" title="标签名称" />
<sar-sidebar-item name="2" title="标签名称" />
<sar-sidebar-item name="3" title="标签名称" />
</sar-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const current = ref('1')
</script>vue
<template>
<sar-sidebar v-model:current="current">
<sar-sidebar-item name="1" title="标签名称" />
<sar-sidebar-item name="2" title="标签名称" />
<sar-sidebar-item name="3" title="标签名称" />
</sar-sidebar>
</template>
<script setup lang="js">
import { ref } from "vue";
const current = ref("1");
</script>圆角
设置 round 属性会使选中项上下角变圆。
vue
<template>
<sar-sidebar v-model:current="current" round>
<sar-sidebar-item name="1" title="标签名称" />
<sar-sidebar-item name="2" title="标签名称" />
<sar-sidebar-item name="3" title="标签名称" />
</sar-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const current = ref('1')
</script>vue
<template>
<sar-sidebar v-model:current="current" round>
<sar-sidebar-item name="1" title="标签名称" />
<sar-sidebar-item name="2" title="标签名称" />
<sar-sidebar-item name="3" title="标签名称" />
</sar-sidebar>
</template>
<script setup lang="js">
import { ref } from "vue";
const current = ref("1");
</script>线条
设置 line 属性会使选中项左边显示线条。
vue
<template>
<sar-sidebar v-model:current="current" line>
<sar-sidebar-item name="1" title="标签名称" />
<sar-sidebar-item name="2" title="标签名称" />
<sar-sidebar-item name="3" title="标签名称" />
</sar-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const current = ref('1')
</script>vue
<template>
<sar-sidebar v-model:current="current" line>
<sar-sidebar-item name="1" title="标签名称" />
<sar-sidebar-item name="2" title="标签名称" />
<sar-sidebar-item name="3" title="标签名称" />
</sar-sidebar>
</template>
<script setup lang="js">
import { ref } from "vue";
const current = ref("1");
</script>禁用
禁用的导航项无法点击。
vue
<template>
<sar-sidebar v-model:current="current">
<sar-sidebar-item name="1" title="标签名称" />
<sar-sidebar-item name="2" disabled title="标签名称" />
<sar-sidebar-item name="3" title="标签名称" />
</sar-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const current = ref('1')
</script>vue
<template>
<sar-sidebar v-model:current="current">
<sar-sidebar-item name="1" title="标签名称" />
<sar-sidebar-item name="2" disabled title="标签名称" />
<sar-sidebar-item name="3" title="标签名称" />
</sar-sidebar>
</template>
<script setup lang="js">
import { ref } from "vue";
const current = ref("1");
</script>自定义
使用默认插槽自定义内容。
vue
<template>
<sar-sidebar v-model:current="current">
<sar-sidebar-item name="1">
<sar-badge dot>标签名称</sar-badge>
</sar-sidebar-item>
<sar-sidebar-item name="2">
<sar-badge :value="10">标签名称</sar-badge>
</sar-sidebar-item>
<sar-sidebar-item name="3">
<sar-badge color="var(--sar-green)" value="推荐">标签名称</sar-badge>
</sar-sidebar-item>
</sar-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const current = ref('1')
</script>vue
<template>
<sar-sidebar v-model:current="current">
<sar-sidebar-item name="1">
<sar-badge dot>标签名称</sar-badge>
</sar-sidebar-item>
<sar-sidebar-item name="2">
<sar-badge :value="10">标签名称</sar-badge>
</sar-sidebar-item>
<sar-sidebar-item name="3">
<sar-badge color="var(--sar-green)" value="推荐">标签名称</sar-badge>
</sar-sidebar-item>
</sar-sidebar>
</template>
<script setup lang="js">
import { ref } from "vue";
const current = ref("1");
</script>场景应用
场景 1
此场景结合了 Sidebar 和 ScrollSpy 组件,绑定了同一个 current 属性进行双向联动。
vue
<template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis title="场景1" padding="0">
<sar-scroll-spy
v-model:current="current"
:root-style="{ height: scrollViewHeight }"
>
<view style="display: flex">
<sar-sidebar
v-model:current="current"
round
:root-style="{
position: 'sticky',
top: 0,
height: scrollViewHeight,
}"
>
<sar-sidebar-item
v-for="(item, i) in list"
:key="i"
:name="i"
:title="item.title"
/>
<view style="height: env(safe-area-inset-bottom); flex: none"></view>
</sar-sidebar>
<view style="flex: 1; min-width: 0; margin: 0 20rpx">
<view v-for="(item, i) in list" :key="i">
<sar-scroll-spy-anchor
:name="i"
:root-style="{
position: 'sticky',
top: 0,
padding: '10rpx 0',
background: 'var(--sar-emphasis-bg)',
}"
>
{{ item.title }}
</sar-scroll-spy-anchor>
<view>
<view
v-for="(_, i) in item.children"
:key="i"
style="
display: flex;
justify-content: center;
align-items: center;
height: 200rpx;
margin-bottom: 10rpx;
background: var(--sar-tertiary-bg);
"
>
{{ i }}
</view>
</view>
</view>
</view>
</view>
</sar-scroll-spy>
</doc-page>
</template>
<script setup lang="ts">
import { useCurrentPageLock, usePageTopPopup } from 'sard-uniapp'
import { onBackPress } from '@dcloudio/uni-app'
import { getWindowInfo } from 'sard-uniapp'
import { ref } from 'vue'
const statusBarHeight = getWindowInfo().statusBarHeight + 'px'
const navbarHeight = `calc(${statusBarHeight} + var(--sar-navbar-height))`
const scrollViewHeight = `calc(100vh - ${navbarHeight})`
const list = ref(
Array(20)
.fill(0)
.map((_, i) => {
return {
title: '标签' + i,
children: Array(3).fill(0),
}
}),
)
const current = ref(0)
const { isLocked } = useCurrentPageLock()
const { shouldStopBack, hidePopup } = usePageTopPopup()
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup()
return true
}
})
</script>vue
<template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis title="场景1" padding="0">
<sar-scroll-spy
v-model:current="current"
:root-style="{ height: scrollViewHeight }"
>
<view style="display: flex">
<sar-sidebar
v-model:current="current"
round
:root-style="{
position: 'sticky',
top: 0,
height: scrollViewHeight,
}"
>
<sar-sidebar-item
v-for="(item, i) in list"
:key="i"
:name="i"
:title="item.title"
/>
<view style="height: env(safe-area-inset-bottom); flex: none"></view>
</sar-sidebar>
<view style="flex: 1; min-width: 0; margin: 0 20rpx">
<view v-for="(item, i) in list" :key="i">
<sar-scroll-spy-anchor
:name="i"
:root-style="{
position: 'sticky',
top: 0,
padding: '10rpx 0',
background: 'var(--sar-emphasis-bg)',
}"
>
{{ item.title }}
</sar-scroll-spy-anchor>
<view>
<view
v-for="(_, i) in item.children"
:key="i"
style="
display: flex;
justify-content: center;
align-items: center;
height: 200rpx;
margin-bottom: 10rpx;
background: var(--sar-tertiary-bg);
"
>
{{ i }}
</view>
</view>
</view>
</view>
</view>
</sar-scroll-spy>
</doc-page>
</template>
<script setup lang="js">
import { useCurrentPageLock, usePageTopPopup } from "sard-uniapp";
import { onBackPress } from "@dcloudio/uni-app";
import { getWindowInfo } from "sard-uniapp";
import { ref } from "vue";
const statusBarHeight = getWindowInfo().statusBarHeight + "px";
const navbarHeight = `calc(${statusBarHeight} + var(--sar-navbar-height))`;
const scrollViewHeight = `calc(100vh - ${navbarHeight})`;
const list = ref(
Array(20).fill(0).map((_, i) => {
return {
title: "\u6807\u7B7E" + i,
children: Array(3).fill(0)
};
})
);
const current = ref(0);
const { isLocked } = useCurrentPageLock();
const { shouldStopBack, hidePopup } = usePageTopPopup();
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup();
return true;
}
});
</script>场景 2
相较于 场景 1,此场景在页面顶部添加了 banner 块和标签栏,且标签栏粘性定位于顶部。
vue
<template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis padding="0" title="场景2">
<sar-scroll-spy
v-model:current="current"
:root-style="{ height: scrollViewHeight }"
:offset="44"
>
<view
style="
display: flex;
justify-content: center;
align-items: center;
height: 150px;
margin: 10px;
font-size: 64rpx;
background: var(--sar-tertiary-bg);
"
>
Banner
</view>
<view
style="
position: sticky;
top: 0;
z-index: 10;
background: var(--sar-emphasis-bg);
"
>
<sar-tabs :current="0" scrollable :list="tabList"></sar-tabs>
</view>
<view style="display: flex">
<sar-sidebar
v-model:current="current"
round
:root-style="{
position: 'sticky',
top: stickyTop,
height: `calc(${scrollViewHeight} - ${stickyTop})`,
}"
:scroll-into-view-options="{ endOffset: 80 }"
>
<sar-sidebar-item
v-for="(item, i) in list"
:key="i"
:name="i"
:title="item.title"
/>
<view style="height: 160rpx; flex: none"></view>
</sar-sidebar>
<view style="flex: 1; min-width: 0; margin: 0 20rpx">
<view v-for="(item, i) in list" :key="i">
<sar-scroll-spy-anchor
:name="i"
:root-style="{
position: 'sticky',
top: stickyTop,
padding: '10rpx 0',
background: 'var(--sar-emphasis-bg)',
}"
>
{{ item.title }}
</sar-scroll-spy-anchor>
<view>
<view
v-for="(_, i) in item.children"
:key="i"
style="
display: flex;
justify-content: center;
align-items: center;
height: 200rpx;
margin-bottom: 10rpx;
background: var(--sar-tertiary-bg);
"
>
{{ i }}
</view>
</view>
</view>
<view style="height: 160rpx"></view>
</view>
</view>
</sar-scroll-spy>
<view
style="
position: fixed;
bottom: 30rpx;
left: 30rpx;
right: 30rpx;
z-index: 10;
height: 100rpx;
border-radius: 12rpx;
background: var(--sar-emphasis-bg);
box-shadow: var(--sar-shadow-xl);
"
></view>
</doc-page>
</template>
<script setup lang="ts">
import { useCurrentPageLock, usePageTopPopup } from 'sard-uniapp'
import { onBackPress } from '@dcloudio/uni-app'
import { getWindowInfo } from 'sard-uniapp'
import { ref } from 'vue'
const statusBarHeight = getWindowInfo().statusBarHeight + 'px'
const navbarHeight = `calc(${statusBarHeight} + var(--sar-navbar-height))`
const scrollViewHeight = `calc(100vh - ${navbarHeight})`
const stickyTop = `var(--sar-tabs-tab-height)`
const list = ref(
Array(20)
.fill(0)
.map((_, i) => {
return {
title: '标签' + i,
children: Array(3).fill(0),
}
}),
)
const current = ref(0)
const tabList = [{ title: '标签0' }, { title: '标签1' }, { title: '标签2' }]
const { isLocked } = useCurrentPageLock()
const { shouldStopBack, hidePopup } = usePageTopPopup()
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup()
return true
}
})
</script>vue
<template>
<page-meta :page-style="isLocked ? 'overflow: hidden' : ''"></page-meta>
<doc-page emphasis padding="0" title="场景2">
<sar-scroll-spy
v-model:current="current"
:root-style="{ height: scrollViewHeight }"
:offset="44"
>
<view
style="
display: flex;
justify-content: center;
align-items: center;
height: 150px;
margin: 10px;
font-size: 64rpx;
background: var(--sar-tertiary-bg);
"
>
Banner
</view>
<view
style="
position: sticky;
top: 0;
z-index: 10;
background: var(--sar-emphasis-bg);
"
>
<sar-tabs :current="0" scrollable :list="tabList"></sar-tabs>
</view>
<view style="display: flex">
<sar-sidebar
v-model:current="current"
round
:root-style="{
position: 'sticky',
top: stickyTop,
height: `calc(${scrollViewHeight} - ${stickyTop})`,
}"
:scroll-into-view-options="{ endOffset: 80 }"
>
<sar-sidebar-item
v-for="(item, i) in list"
:key="i"
:name="i"
:title="item.title"
/>
<view style="height: 160rpx; flex: none"></view>
</sar-sidebar>
<view style="flex: 1; min-width: 0; margin: 0 20rpx">
<view v-for="(item, i) in list" :key="i">
<sar-scroll-spy-anchor
:name="i"
:root-style="{
position: 'sticky',
top: stickyTop,
padding: '10rpx 0',
background: 'var(--sar-emphasis-bg)',
}"
>
{{ item.title }}
</sar-scroll-spy-anchor>
<view>
<view
v-for="(_, i) in item.children"
:key="i"
style="
display: flex;
justify-content: center;
align-items: center;
height: 200rpx;
margin-bottom: 10rpx;
background: var(--sar-tertiary-bg);
"
>
{{ i }}
</view>
</view>
</view>
<view style="height: 160rpx"></view>
</view>
</view>
</sar-scroll-spy>
<view
style="
position: fixed;
bottom: 30rpx;
left: 30rpx;
right: 30rpx;
z-index: 10;
height: 100rpx;
border-radius: 12rpx;
background: var(--sar-emphasis-bg);
box-shadow: var(--sar-shadow-xl);
"
></view>
</doc-page>
</template>
<script setup lang="js">
import { useCurrentPageLock, usePageTopPopup } from "sard-uniapp";
import { onBackPress } from "@dcloudio/uni-app";
import { getWindowInfo } from "sard-uniapp";
import { ref } from "vue";
const statusBarHeight = getWindowInfo().statusBarHeight + "px";
const navbarHeight = `calc(${statusBarHeight} + var(--sar-navbar-height))`;
const scrollViewHeight = `calc(100vh - ${navbarHeight})`;
const stickyTop = `var(--sar-tabs-tab-height)`;
const list = ref(
Array(20).fill(0).map((_, i) => {
return {
title: "\u6807\u7B7E" + i,
children: Array(3).fill(0)
};
})
);
const current = ref(0);
const tabList = [{ title: "\u6807\u7B7E0" }, { title: "\u6807\u7B7E1" }, { title: "\u6807\u7B7E2" }];
const { isLocked } = useCurrentPageLock();
const { shouldStopBack, hidePopup } = usePageTopPopup();
onBackPress(() => {
if (shouldStopBack.value) {
hidePopup();
return true;
}
});
</script>API
SidebarProps
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| root-class | 组件根元素类名 | string | - |
| root-style | 组件根元素样式 | StyleValue | - |
| current | 当前绑定导航项的名称 | string | number | - |
| round | 当前导航项是否显示为圆角 | boolean | false |
| line | 当前导航项是否添加左边线条 | boolean | false |
| scroll-into-view-options | 自定义滚动配置选项 | ScrollIntoViewOptions | {position: 'nearest', startOffset: 0, endOffset: 0} |
SidebarSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 自定义默认内容 | - |
SidebarEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| update:current | 当前导航项改变时触发 | (name: string | number) => void |
| change | 当前导航项改变时触发 | (name: string | number) => void |
SidebarItemProps
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| root-class | 组件根元素类名 | string | - |
| root-style | 组件根元素样式 | StyleValue | - |
| title | 导航项显示的标题内容 | string | - |
| name | 导航项唯一名称,必需 | string | number | - |
| disabled | 是否禁用表单项 | boolean | false |
SidebarItemSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 自定义默认内容 | - |
SidebarItemEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| click | 点击导航项时触发 | (event: any) => void |
主题定制
CSS 变量
scss
page,
.sar-portal {
--sar-sidebar-bg: var(--sar-body-bg);
--sar-sidebar-width: 184rpx;
--sar-sidebar-item-padding-x: 40rpx;
--sar-sidebar-item-padding-y: 28rpx;
--sar-sidebar-item-font-size: var(--sar-text-sm);
--sar-sidebar-item-color: var(--sar-secondary-color);
--sar-sidebar-item-active-bg: var(--sar-emphasis-bg);
--sar-sidebar-item-active-color: var(--sar-emphasis-color);
--sar-sidebar-item-active-font-weight: var(--sar-font-normal);
--sar-sidebar-line-width: 6rpx;
--sar-sidebar-line-height: 24rpx;
--sar-sidebar-line-bg: var(--sar-primary);
--sar-sidebar-round-size: 12rpx;
}