ec-admin/src/components/ty-tabs.vue
liuyun c96151d2ae refactor: convert raw CSS to Tailwind @apply + search collapseCount=3
- ty-search-form 折叠默认数 2 → 3
- login/register/forgot/home/ty-header/ty-sidebar/ty-mixbar/ty-tabs/ty-footer/ty-setting-panel/icon/default 布局裸 CSS 转 @apply
- 修复背景色工具类误写(bg-base→bg-bg-base、bg-page→bg-bg-page)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-11 13:18:45 +08:00

130 lines
3.4 KiB
Vue

<template>
<div v-if="tabs.length" class="ty-tabs">
<div
v-for="tab in tabs"
:key="tab.path"
class="ty-tabs__item"
:class="{ 'is-active': tab.path === activePath }"
@click="emit('click', tab.path)"
@contextmenu.prevent="openMenu($event, tab)"
>
<span class="ty-tabs__title">{{ tab.title }}</span>
<span v-if="tab.closable" class="ty-tabs__close" @click.stop="emit('remove', tab.path)">
<icon type="iconify" icon="mdi:close" />
</span>
</div>
<div
v-if="menu.visible"
class="ty-tabs__context-menu"
:style="{ left: `${menu.x}px`, top: `${menu.y}px` }"
@click.stop
>
<div class="ty-tabs__context-item" @click="onMenu('close-others')">关闭其他</div>
<div class="ty-tabs__context-item" @click="onMenu('close-left')">关闭左侧</div>
<div class="ty-tabs__context-item" @click="onMenu('close-right')">关闭右侧</div>
<div class="ty-tabs__context-item" @click="onMenu('close-all')">关闭全部</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, reactive } from 'vue'
import type { TabItem } from '@/stores/modules/tabs'
defineOptions({ name: 'TyTabs' })
defineProps<{
/** 打开的页签。 */
tabs: TabItem[]
/** 当前激活的路径。 */
activePath: string
}>()
const emit = defineEmits<{
(e: 'click', path: string): void
(e: 'remove', path: string): void
(e: 'close-others', path: string): void
(e: 'close-left', path: string): void
(e: 'close-right', path: string): void
(e: 'close-all'): void
}>()
const menu = reactive({ visible: false, x: 0, y: 0, path: '' })
function openMenu(e: MouseEvent, tab: TabItem): void {
menu.x = e.clientX
menu.y = e.clientY
menu.path = tab.path
menu.visible = true
}
function onMenu(action: 'close-others' | 'close-left' | 'close-right' | 'close-all'): void {
menu.visible = false
switch (action) {
case 'close-all':
emit('close-all')
break
case 'close-others':
emit('close-others', menu.path)
break
case 'close-left':
emit('close-left', menu.path)
break
case 'close-right':
emit('close-right', menu.path)
break
}
}
function closeMenu(): void {
menu.visible = false
}
onMounted(() => document.addEventListener('click', closeMenu))
onBeforeUnmount(() => document.removeEventListener('click', closeMenu))
</script>
<style scoped lang="scss">
.ty-tabs {
@apply flex items-center gap-1 overflow-x-auto px-2 h-9 bg-bg-base border-b border-solid;
border-color: var(--wm-border-color-light);
&__item {
@apply flex flex-shrink-0 cursor-pointer items-center gap-1 h-7 px-2.5 rounded-base text-sm text-text-regular;
&:hover {
@apply bg-bg-page;
}
&.is-active {
color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
}
}
&__close {
@apply flex items-center justify-center w-4 h-4 rounded-full text-xs;
&:hover {
@apply text-white;
background: var(--wm-color-text-placeholder);
}
}
&__context-menu {
@apply fixed z-50 min-w-[110px] py-1 bg-bg-base rounded-base border border-solid;
border-color: var(--wm-border-color-light);
box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
}
&__context-item {
@apply cursor-pointer py-1.5 px-3.5 text-sm text-text-regular;
&:hover {
@apply bg-bg-page;
}
}
}
</style>