优化细节

This commit is contained in:
2026-02-06 10:42:35 +08:00
parent 9eaf5c1a64
commit d6940b51e1
2 changed files with 174 additions and 6 deletions

View File

@@ -57,6 +57,7 @@
@tab-close="onTabClose"
@close-other="onCloseOther"
@close-all="onCloseAll"
@refresh="onRefresh"
/>
<!-- 内容展示区 (内部路由渲染) -->

View File

@@ -1,36 +1,122 @@
<template>
<view class="tags">
<scroll-view class="tags-scroll" scroll-x="true" show-scrollbar="false">
<view class="tags-row">
<transition-group name="tag-list" tag="view" class="tags-row">
<view
v-for="t in tabs"
:key="t.id"
class="tag"
:class="{ active: activeTabId === t.id }"
@click="$emit('tab-click', t)"
@click="onTabClick(t)"
@contextmenu.prevent="openContextMenu($event, t)"
>
<text class="tag-text">{{ t.title }}</text>
<view class="tag-close" @click.stop="$emit('tab-close', t.id)">
<view v-if="!t.isAffix" class="tag-close" @click.stop="$emit('tab-close', t.id)">
<text class="tag-close-text">×</text>
</view>
</view>
</view>
</transition-group>
</scroll-view>
<!-- 右键菜单 (带动画) -->
<transition name="menu-fade">
<view
v-if="menuVisible"
class="context-menu"
:style="{ top: menuY + 'px', left: menuX + 'px' }"
@click.stop=""
>
<view class="menu-item" @click="handleAction('refresh')">
<text class="menu-icon">↻</text>
<text class="menu-text">刷新</text>
</view>
<view class="menu-item" v-if="!selectedTab?.isAffix" @click="handleAction('close')">
<text class="menu-icon">×</text>
<text class="menu-text">关闭</text>
</view>
<view class="menu-item" @click="handleAction('close-other')">
<text class="menu-icon">↸</text>
<text class="menu-text">关闭其他</text>
</view>
<view class="menu-item" @click="handleAction('close-all')">
<text class="menu-icon">⊘</text>
<text class="menu-text">全部关闭</text>
</view>
</view>
</transition>
</view>
</template>
<script setup lang="uts">
import type { TabItem } from '../types.uts'
import { ref, onMounted, onUnmounted } from 'vue'
import type { TabItem } from '../store/adminNavStore.uts'
defineProps<{
tabs: TabItem[]
activeTabId: string
}>()
defineEmits<{
const emit = defineEmits<{
(e:'tab-click', tab: TabItem): void
(e:'tab-close', tabId: string): void
(e:'close-other', tabId: string): void
(e:'close-all'): void
(e:'refresh'): void
}>()
// 右键菜单状态
const menuVisible = ref(false)
const menuX = ref(0)
const menuY = ref(0)
const selectedTab = ref<TabItem | null>(null)
function onTabClick(tab: TabItem) {
closeMenu()
emit('tab-click', tab)
}
function openContextMenu(e: MouseEvent, tab: TabItem) {
selectedTab.value = tab
menuX.value = e.clientX
menuY.value = e.clientY
// 边缘检测
if (menuX.value + 100 > window.innerWidth) {
menuX.value = window.innerWidth - 110
}
menuVisible.value = true
}
function closeMenu() {
menuVisible.value = false
}
function handleAction(type: string) {
if (!selectedTab.value) return
const id = selectedTab.value!.id
if (type === 'refresh') {
emit('refresh')
} else if (type === 'close') {
emit('tab-close', id)
} else if (type === 'close-other') {
emit('close-other', id)
} else if (type === 'close-all') {
emit('close-all')
}
closeMenu()
}
onMounted(() => {
window.addEventListener('click', closeMenu)
})
onUnmounted(() => {
window.removeEventListener('click', closeMenu)
})
</script>
<style>
@@ -41,6 +127,7 @@ defineEmits<{
display:flex;
flex-direction:row;
align-items:center;
position: relative;
}
.tags-scroll{ width: 100%; height: 44px; }
.tags-row{
@@ -61,6 +148,11 @@ defineEmits<{
flex-direction: row;
align-items:center;
gap: 8px;
cursor: pointer;
transition: all 0.2s;
}
.tag:hover {
background: #f9fafb;
}
.tag.active{
border-color:#1677ff;
@@ -76,5 +168,80 @@ defineEmits<{
align-items:center;
justify-content:center;
}
.tag-close:hover {
background: rgba(0,0,0,0.1);
}
.tag-close-text{ font-size:14px; color:#6b7280; line-height:14px; }
/* 右键菜单样式 */
.context-menu {
position: fixed;
z-index: 9999;
background: #fff;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
padding: 5px 0;
border-radius: 4px;
min-width: 100px;
border: 1px solid #ebeef5;
}
.menu-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 8px 16px;
cursor: pointer;
transition: background 0.2s;
}
.menu-item:hover {
background: #f5f7fa;
}
.menu-icon {
font-size: 14px;
margin-right: 8px;
color: #606266;
width: 14px;
text-align: center;
}
.menu-text {
font-size: 13px;
color: #606266;
}
/* 标签列表动画 - 平滑平移和缩放 */
.tag-list-enter-active,
.tag-list-leave-active {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.tag-list-enter-from {
opacity: 0;
transform: translateX(-20px) scale(0.9);
}
.tag-list-leave-to {
opacity: 0;
transform: translateY(10px) scale(0.8);
}
/* 确保列表项在移动过程中位置平滑切换 */
.tag-list-move {
transition: transform 0.3s ease;
}
/* 右键菜单动画 - 类似 CRMEB 的缩放渐入 */
.menu-fade-enter-active,
.menu-fade-leave-active {
transition: all 0.2s cubic-bezier(0.25, 0.8, 0.5, 1);
transform-origin: top left;
}
.menu-fade-enter-from,
.menu-fade-leave-to {
opacity: 0;
transform: scale(0.8) translateY(-10px);
}
</style>