介绍
垂直标签导航,用于在不同的区域之间进行切换。
代码演示
基础使用
使用 v-model 绑定当前选中项的 value,value 必须且唯一。
vue
<template>
<s-sidebar v-model="value">
<s-sidebar-item value="1" label="标签名称" />
<s-sidebar-item value="2" label="标签名称" />
<s-sidebar-item value="3" label="标签名称" />
</s-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref('1')
</script>vue
<template>
<s-sidebar v-model="value">
<s-sidebar-item value="1" label="标签名称" />
<s-sidebar-item value="2" label="标签名称" />
<s-sidebar-item value="3" label="标签名称" />
</s-sidebar>
</template>
<script setup lang="js">
import { ref } from 'vue'
const value = ref('1')
</script>圆角
设置 round 属性会使选中项上下角变圆。
vue
<template>
<s-sidebar v-model="value" round>
<s-sidebar-item value="1" label="标签名称" />
<s-sidebar-item value="2" label="标签名称" />
<s-sidebar-item value="3" label="标签名称" />
</s-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref('1')
</script>vue
<template>
<s-sidebar v-model="value" round>
<s-sidebar-item value="1" label="标签名称" />
<s-sidebar-item value="2" label="标签名称" />
<s-sidebar-item value="3" label="标签名称" />
</s-sidebar>
</template>
<script setup lang="js">
import { ref } from 'vue'
const value = ref('1')
</script>线条
设置 line 属性会使选中项左边显示线条。
vue
<template>
<s-sidebar v-model="value" line>
<s-sidebar-item value="1" label="标签名称" />
<s-sidebar-item value="2" label="标签名称" />
<s-sidebar-item value="3" label="标签名称" />
</s-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref('1')
</script>vue
<template>
<s-sidebar v-model="value" line>
<s-sidebar-item value="1" label="标签名称" />
<s-sidebar-item value="2" label="标签名称" />
<s-sidebar-item value="3" label="标签名称" />
</s-sidebar>
</template>
<script setup lang="js">
import { ref } from 'vue'
const value = ref('1')
</script>禁用
禁用的导航项无法点击。
vue
<template>
<s-sidebar v-model="value">
<s-sidebar-item value="1" label="标签名称" />
<s-sidebar-item value="2" label="标签名称" disabled />
<s-sidebar-item value="3" label="标签名称" />
</s-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref('1')
</script>vue
<template>
<s-sidebar v-model="value">
<s-sidebar-item value="1" label="标签名称" />
<s-sidebar-item value="2" label="标签名称" disabled />
<s-sidebar-item value="3" label="标签名称" />
</s-sidebar>
</template>
<script setup lang="js">
import { ref } from 'vue'
const value = ref('1')
</script>自定义
使用默认插槽自定义内容。
vue
<template>
<s-sidebar v-model="value">
<s-sidebar-item value="1">
<s-badge dot>标签名称</s-badge>
</s-sidebar-item>
<s-sidebar-item value="2">
<s-badge :value="10">标签名称</s-badge>
</s-sidebar-item>
<s-sidebar-item value="3">
<s-badge color="var(--s-color-success)" value="推荐">标签名称</s-badge>
</s-sidebar-item>
</s-sidebar>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const value = ref('1')
</script>vue
<template>
<s-sidebar v-model="value">
<s-sidebar-item value="1">
<s-badge dot>标签名称</s-badge>
</s-sidebar-item>
<s-sidebar-item value="2">
<s-badge :value="10">标签名称</s-badge>
</s-sidebar-item>
<s-sidebar-item value="3">
<s-badge color="var(--s-color-success)" value="推荐">标签名称</s-badge>
</s-sidebar-item>
</s-sidebar>
</template>
<script setup lang="js">
import { ref } from 'vue'
const value = ref('1')
</script>场景应用
场景 1
此场景结合了 Sidebar 和 ScrollSpy 组件,绑定了同一个值进行双向联动。
vue
<template>
<doc-page title="场景1" padding="0">
<s-scroll-spy v-model="value" :style="{ height: scrollViewHeight }">
<div style="display: flex">
<s-sidebar
v-model="value"
round
:style="{
position: 'sticky',
top: 0,
height: scrollViewHeight,
}"
>
<s-sidebar-item v-for="(item, i) in list" :key="i" :value="i" :label="item.label" />
<div style="height: var(--s-safe-bottom); flex: none"></div>
</s-sidebar>
<div style="flex: 1; min-width: 0; margin: 0 10px">
<div v-for="(item, i) in list" :key="i">
<s-scroll-spy-anchor
:name="i"
:style="{
position: 'sticky',
top: 0,
padding: '5px 0',
background: 'var(--s-bg-color-container)',
}"
>
{{ item.label }}
</s-scroll-spy-anchor>
<div>
<div
v-for="(_, i) in item.children"
:key="i"
style="
display: flex;
justify-content: center;
align-items: center;
height: 100px;
margin-bottom: 5px;
background: var(--s-fill-color);
"
>
{{ i }}
</div>
</div>
</div>
</div>
</div>
</s-scroll-spy>
</doc-page>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const scrollViewHeight = `calc(100vh - var(--s-navbar-height) - var(--s-status-bar-height))`
const list = ref(
Array(20)
.fill(0)
.map((_, i) => {
return {
label: '标签' + i,
children: Array(3).fill(0),
}
}),
)
const value = ref(0)
</script>vue
<template>
<doc-page title="场景1" padding="0">
<s-scroll-spy v-model="value" :style="{ height: scrollViewHeight }">
<div style="display: flex">
<s-sidebar
v-model="value"
round
:style="{
position: 'sticky',
top: 0,
height: scrollViewHeight,
}"
>
<s-sidebar-item v-for="(item, i) in list" :key="i" :value="i" :label="item.label" />
<div style="height: var(--s-safe-bottom); flex: none"></div>
</s-sidebar>
<div style="flex: 1; min-width: 0; margin: 0 10px">
<div v-for="(item, i) in list" :key="i">
<s-scroll-spy-anchor
:name="i"
:style="{
position: 'sticky',
top: 0,
padding: '5px 0',
background: 'var(--s-bg-color-container)',
}"
>
{{ item.label }}
</s-scroll-spy-anchor>
<div>
<div
v-for="(_, i) in item.children"
:key="i"
style="
display: flex;
justify-content: center;
align-items: center;
height: 100px;
margin-bottom: 5px;
background: var(--s-fill-color);
"
>
{{ i }}
</div>
</div>
</div>
</div>
</div>
</s-scroll-spy>
</doc-page>
</template>
<script setup lang="js">
import { ref } from 'vue'
const scrollViewHeight = `calc(100vh - var(--s-navbar-height) - var(--s-status-bar-height))`
const list = ref(
Array(20)
.fill(0)
.map((_, i) => {
return {
label: '标签' + i,
children: Array(3).fill(0),
}
}),
)
const value = ref(0)
</script>场景 2
相较于 场景 1,此场景在页面顶部添加了 banner 块和标签栏,且标签栏粘性定位于顶部。
vue
<template>
<doc-page padding="0" title="场景2">
<s-scroll-spy v-model="value" :style="{ height: scrollViewHeight }" :offset="44">
<div
style="
display: flex;
justify-content: center;
align-items: center;
height: 150px;
margin: 10px;
font-size: 32px;
background: var(--s-fill-color);
"
>
Banner
</div>
<div style="position: sticky; top: 0; z-index: 10; background: var(--s-bg-color-container)">
<s-tabs :model-value="1" scrollable :options="tabsOptions"></s-tabs>
</div>
<div style="display: flex">
<s-sidebar
v-model="value"
round
:style="{
position: 'sticky',
top: stickyTop,
height: `calc(${scrollViewHeight} - ${stickyTop})`,
}"
:scroll-into-view-options="{ endOffset: 80 }"
>
<s-sidebar-item v-for="(item, i) in list" :key="i" :value="i" :label="item.label" />
<div style="height: 80px; flex: none"></div>
</s-sidebar>
<div style="flex: 1; min-width: 0; margin: 0 10px">
<div v-for="(item, i) in list" :key="i">
<s-scroll-spy-anchor
:name="i"
:style="{
position: 'sticky',
top: stickyTop,
padding: '5px 0',
background: 'var(--s-bg-color-container)',
}"
>
{{ item.label }}
</s-scroll-spy-anchor>
<div>
<div
v-for="(_, i) in item.children"
:key="i"
style="
display: flex;
justify-content: center;
align-items: center;
height: 100px;
margin-bottom: 5px;
background: var(--s-fill-color);
"
>
{{ i }}
</div>
</div>
</div>
<div style="height: 80px"></div>
</div>
</div>
</s-scroll-spy>
<div
style="
position: fixed;
bottom: 15px;
left: 15px;
right: 15px;
z-index: 10;
height: 50px;
border-radius: 6px;
background: var(--s-bg-color-container);
box-shadow: var(--s-box-shadow-secondary);
"
></div>
</doc-page>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const scrollViewHeight = `calc(100vh - var(--s-navbar-height) - var(--s-status-bar-height))`
const stickyTop = `var(--s-tabs-tab-height)`
const list = ref(
Array(20)
.fill(0)
.map((_, i) => {
return {
label: '标签' + i,
children: Array(3).fill(0),
}
}),
)
const value = ref(0)
const tabsOptions = [
{ label: '标签1', value: 1 },
{ label: '标签2', value: 2 },
{ label: '标签3', value: 3 },
]
</script>vue
<template>
<doc-page padding="0" title="场景2">
<s-scroll-spy v-model="value" :style="{ height: scrollViewHeight }" :offset="44">
<div
style="
display: flex;
justify-content: center;
align-items: center;
height: 150px;
margin: 10px;
font-size: 32px;
background: var(--s-fill-color);
"
>
Banner
</div>
<div style="position: sticky; top: 0; z-index: 10; background: var(--s-bg-color-container)">
<s-tabs :model-value="1" scrollable :options="tabsOptions"></s-tabs>
</div>
<div style="display: flex">
<s-sidebar
v-model="value"
round
:style="{
position: 'sticky',
top: stickyTop,
height: `calc(${scrollViewHeight} - ${stickyTop})`,
}"
:scroll-into-view-options="{ endOffset: 80 }"
>
<s-sidebar-item v-for="(item, i) in list" :key="i" :value="i" :label="item.label" />
<div style="height: 80px; flex: none"></div>
</s-sidebar>
<div style="flex: 1; min-width: 0; margin: 0 10px">
<div v-for="(item, i) in list" :key="i">
<s-scroll-spy-anchor
:name="i"
:style="{
position: 'sticky',
top: stickyTop,
padding: '5px 0',
background: 'var(--s-bg-color-container)',
}"
>
{{ item.label }}
</s-scroll-spy-anchor>
<div>
<div
v-for="(_, i) in item.children"
:key="i"
style="
display: flex;
justify-content: center;
align-items: center;
height: 100px;
margin-bottom: 5px;
background: var(--s-fill-color);
"
>
{{ i }}
</div>
</div>
</div>
<div style="height: 80px"></div>
</div>
</div>
</s-scroll-spy>
<div
style="
position: fixed;
bottom: 15px;
left: 15px;
right: 15px;
z-index: 10;
height: 50px;
border-radius: 6px;
background: var(--s-bg-color-container);
box-shadow: var(--s-box-shadow-secondary);
"
></div>
</doc-page>
</template>
<script setup lang="js">
import { ref } from 'vue'
const scrollViewHeight = `calc(100vh - var(--s-navbar-height) - var(--s-status-bar-height))`
const stickyTop = `var(--s-tabs-tab-height)`
const list = ref(
Array(20)
.fill(0)
.map((_, i) => {
return {
label: '标签' + i,
children: Array(3).fill(0),
}
}),
)
const value = ref(0)
const tabsOptions = [
{
label: '标签1',
value: 1,
},
{
label: '标签2',
value: 2,
},
{
label: '标签3',
value: 3,
},
]
</script>API
SidebarProps
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| model-value | 当前绑定导航项的名称 | string | number | - |
| round | 当前导航项是否显示为圆角 | boolean | false |
| line | 当前导航项是否添加左边线条 | boolean | false |
| scroll-into-view-options | 自定义滚动配置选项 | ScrollIntoViewOptions | - |
SidebarSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 自定义默认内容 | - |
SidebarEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| update:modelValue | 当前导航项改变时触发 | (value: string | number) => void |
| change | 当前导航项改变时触发 | (value: string | number) => void |
SidebarItemProps
| 属性 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| label | 导航项显示的标题内容 | string | - |
| value | 导航项唯一名称,必需 | string | number | - |
| disabled | 是否禁用表单项 | boolean | false |
SidebarItemSlots
| 插槽 | 描述 | 属性 |
|---|---|---|
| default | 自定义默认内容 | - |
SidebarItemEmits
| 事件 | 描述 | 类型 |
|---|---|---|
| click | 点击导航项时触发 | (event: MouseEvent) => void |
主题定制
样式变量
| CSS 变量 | 值 |
|---|---|
--s-sidebar-bg | var(--s-fill-color-tertiary) |
--s-sidebar-width | 92px |
--s-sidebar-item-padding-x | 20px |
--s-sidebar-item-padding-y | 14px |
--s-sidebar-item-font-size | var(--s-font-size-sm) |
--s-sidebar-item-color | var(--s-text-color-secondary) |
--s-sidebar-item-bg-active | var(--s-bg-color-extreme) |
--s-sidebar-item-color-active | var(--s-text-color-emphasis) |
--s-sidebar-item-font-weight-active | var(--s-font-weight-normal) |
--s-sidebar-line-width | 3px |
--s-sidebar-line-height | 12px |
--s-sidebar-line-bg | var(--s-color-primary) |
--s-sidebar-round-size | var(--s-border-radius) |