20260824172723

This commit is contained in:
oneao committed 2026-08-24 17:27:24 +08:00
1 parent 791d1e01d7
commit 97aea665f9
10 files changed
+443 -723

No files matched your search

@@ -1,5 +1,6 @@
<script setup>
import { computed, Fragment, isVNode, nextTick, reactive, ref, useId, useSlots, watch } from 'vue'
import Scrollbar from '@/components/ui/scrollbar/scrollbar.vue'
/* ---------- props / emits / slots ---------- */
const props = defineProps({
@@ -11,6 +12,14 @@ const props = defineProps({
default: 'top',
validator: (value) => ['top', 'left', 'right'].includes(value),
},
/** 样式类型:line 下划线指示条,pill 胶囊式标签 */
type: {
type: String,
default: 'line',
validator: (value) => ['line', 'pill'].includes(value),
},
/** 仅渲染标签栏(不渲染内容区):用于纯导航场景,内容由外部 v-show 控制 */
navOnly: { type: Boolean, default: false },
/** 标签列表在标签栏内居中显示(top 水平居中,left/right 垂直居中) */
centered: { type: Boolean, default: false },
/** 整组禁用 */
@@ -230,46 +239,52 @@ function onTablistKeydown(event) {
<template>
<div
class="tabs"
:class="[`tabs-${placement}`, { 'tabs-disabled': disabled, 'tabs-centered': centered }]"
:class="[
`tabs-${placement}`,
`tabs-${type}`,
{ 'tabs-disabled': disabled, 'tabs-centered': centered, 'tabs-nav-only': navOnly },
]"
>
<div class="tabs-nav">
<div v-if="slots.leftExtra" class="tabs-nav-extra">
<slot name="leftExtra" />
</div>
<div
class="tabs-nav-list"
role="tablist"
:aria-orientation="placement === 'top' ? 'horizontal' : 'vertical'"
@keydown="onTablistKeydown"
>
<button
v-for="(pane, index) in mergedPanes"
:key="pane.key"
:ref="(el) => setTabRef(pane.key, el)"
type="button"
class="tabs-tab"
:class="{
'tabs-tab-active': currentKey === pane.key,
'tabs-tab-disabled': pane.disabled,
}"
role="tab"
:id="`${baseId}-tab-${index}`"
:aria-selected="currentKey === pane.key"
:aria-controls="`${baseId}-panel-${index}`"
:tabindex="currentKey === pane.key ? 0 : -1"
:disabled="disabled || pane.disabled"
@click="(event) => onTabClick(pane, event)"
<Scrollbar class="tabs-nav-scroll">
<div
class="tabs-nav-list"
role="tablist"
:aria-orientation="placement === 'top' ? 'horizontal' : 'vertical'"
@keydown="onTablistKeydown"
>
<component :is="pane.labelRender" v-if="pane.labelRender" />
<span v-else-if="pane.labelText !== undefined">{{ pane.labelText }}</span>
</button>
</div>
<button
v-for="(pane, index) in mergedPanes"
:key="pane.key"
:ref="(el) => setTabRef(pane.key, el)"
type="button"
class="tabs-tab"
:class="{
'tabs-tab-active': currentKey === pane.key,
'tabs-tab-disabled': pane.disabled,
}"
role="tab"
:id="`${baseId}-tab-${index}`"
:aria-selected="currentKey === pane.key"
:aria-controls="`${baseId}-panel-${index}`"
:tabindex="currentKey === pane.key ? 0 : -1"
:disabled="disabled || pane.disabled"
@click="(event) => onTabClick(pane, event)"
>
<component :is="pane.labelRender" v-if="pane.labelRender" />
<span v-else-if="pane.labelText !== undefined">{{ pane.labelText }}</span>
</button>
</div>
</Scrollbar>
<div v-if="slots.rightExtra" class="tabs-nav-extra">
<slot name="rightExtra" />
</div>
</div>
<div class="tabs-content">
<div v-if="!navOnly" class="tabs-content">
<template v-for="(pane, index) in mergedPanes" :key="pane.key">
<div
v-if="renderedKeys.has(pane.key)"