优化细节
This commit is contained in:
@@ -57,6 +57,7 @@
|
|||||||
@tab-close="onTabClose"
|
@tab-close="onTabClose"
|
||||||
@close-other="onCloseOther"
|
@close-other="onCloseOther"
|
||||||
@close-all="onCloseAll"
|
@close-all="onCloseAll"
|
||||||
|
@refresh="onRefresh"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 内容展示区 (内部路由渲染) -->
|
<!-- 内容展示区 (内部路由渲染) -->
|
||||||
|
|||||||
@@ -1,36 +1,122 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="tags">
|
<view class="tags">
|
||||||
<scroll-view class="tags-scroll" scroll-x="true" show-scrollbar="false">
|
<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
|
<view
|
||||||
v-for="t in tabs"
|
v-for="t in tabs"
|
||||||
:key="t.id"
|
:key="t.id"
|
||||||
class="tag"
|
class="tag"
|
||||||
:class="{ active: activeTabId === t.id }"
|
: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>
|
<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>
|
<text class="tag-close-text">×</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</transition-group>
|
||||||
</scroll-view>
|
</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>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import type { TabItem } from '../types.uts'
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
|
import type { TabItem } from '../store/adminNavStore.uts'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
tabs: TabItem[]
|
tabs: TabItem[]
|
||||||
activeTabId: string
|
activeTabId: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e:'tab-click', tab: TabItem): void
|
(e:'tab-click', tab: TabItem): void
|
||||||
(e:'tab-close', tabId: string): 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>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -41,6 +127,7 @@ defineEmits<{
|
|||||||
display:flex;
|
display:flex;
|
||||||
flex-direction:row;
|
flex-direction:row;
|
||||||
align-items:center;
|
align-items:center;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.tags-scroll{ width: 100%; height: 44px; }
|
.tags-scroll{ width: 100%; height: 44px; }
|
||||||
.tags-row{
|
.tags-row{
|
||||||
@@ -61,6 +148,11 @@ defineEmits<{
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items:center;
|
align-items:center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.tag:hover {
|
||||||
|
background: #f9fafb;
|
||||||
}
|
}
|
||||||
.tag.active{
|
.tag.active{
|
||||||
border-color:#1677ff;
|
border-color:#1677ff;
|
||||||
@@ -76,5 +168,80 @@ defineEmits<{
|
|||||||
align-items:center;
|
align-items:center;
|
||||||
justify-content: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; }
|
.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>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user