refactor(projects): 重构browser tab初步

This commit is contained in:
Soybean
2021-09-23 19:47:40 +08:00
parent 22f8f3f014
commit 51b8603545
10 changed files with 370 additions and 206 deletions

View File

@ -0,0 +1,108 @@
<template>
<div
class="relative flex-y-center h-34px -mr-20px cursor-pointer"
:class="{ 'z-10': isHover, 'z-11': isActive }"
@mouseenter="handleMouseOnTab('enter')"
@mouseleave="handleMouseOnTab('leave')"
>
<div class="relative w-10px h-full">
<div class="absolute-lt w-full h-full rounded-br-8px overflow-hidden bg-white z-3"></div>
<div
class="absolute-rb w-full h-10px z-2 bg-opacity-10"
:class="{ 'bg-primary': isActive, 'bg-black': !isActive && isHover }"
></div>
</div>
<div
class="flex-y-center h-full rounded-t-8px bg-opacity-10"
:class="{ 'bg-primary': isActive, 'bg-black': !isActive && isHover }"
>
<div class="w-16px">
<n-divider v-if="!isHover && !isActive" :vertical="true" class="!bg-gray-300" />
</div>
<span :class="[closable ? 'pr-24px' : 'pr-16px']">
<slot></slot>
</span>
<icon-close v-if="closable" :is-primary="isActive" @click="handleClose" />
<div v-if="closable" class="w-8px"></div>
</div>
<div class="relative w-10px h-full">
<div class="absolute-lt w-full h-full rounded-bl-8px overflow-hidden bg-white z-3"></div>
<div
class="absolute-lb w-full h-10px bg-opacity-10 z-2"
:class="{ 'bg-primary': isActive, 'bg-black': !isActive && isHover }"
></div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { NDivider } from 'naive-ui';
import { useBoolean } from '@/hooks';
import { IconClose } from '../../common';
const props = defineProps({
currentIndex: {
type: Number,
required: true
},
activeIndex: {
type: Number,
required: true
},
hoverIndex: {
type: Number,
default: NaN
},
closable: {
type: Boolean,
default: true
}
});
const emit = defineEmits(['close', 'update:hoverIndex']);
const { bool: isHover, setTrue, setFalse } = useBoolean();
const isActive = computed(() => props.currentIndex === props.activeIndex);
const hoveredIndex = ref(props.hoverIndex);
function setHoverIndex(index: number) {
hoveredIndex.value = index;
}
function resetHoverIndex() {
hoveredIndex.value = NaN;
}
function handleMouseOnTab(mode: 'enter' | 'leave') {
if (mode === 'enter') {
setTrue();
setHoverIndex(props.currentIndex);
} else {
setFalse();
resetHoverIndex();
}
}
function handleClose(e: MouseEvent) {
e.stopPropagation();
emit('close');
}
watch(
() => props.hoverIndex,
newValue => {
setHoverIndex(newValue);
}
);
watch(hoveredIndex, newValue => {
emit('update:hoverIndex', newValue);
});
watch(
() => props.activeIndex,
() => {
resetHoverIndex();
}
);
</script>
<style scoped></style>

View File

@ -0,0 +1,6 @@
<template>
<div></div>
</template>
<script setup lang="ts"></script>
<style scoped></style>

View File

@ -1,4 +1,5 @@
import BrowserTabItem from './BrowserTabItem.vue';
import LeftTabRadius from './LeftTabRadius.vue';
import RightTabRadius from './RightTabRadius.vue';
export { LeftTabRadius, RightTabRadius };
export { BrowserTabItem, LeftTabRadius, RightTabRadius };

View File

@ -0,0 +1,117 @@
<template>
<div
class="
relative
inline-flex-center
h-30px
px-32px
transition-background
duration-400
ease-in-out
bg-opacity-10
cursor-pointer
"
:class="{ 'text-primary bg-primary z-3': active, 'bg-black z-2': isHover && !active }"
@mouseenter="handleMouseOnTab('enter')"
@mouseleave="handleMouseOnTab('leave')"
>
<span>
<slot></slot>
</span>
<div
v-if="closable"
class="transition-width duration-400 ease-in-out overflow-hidden"
:class="[isHover ? 'w-18px' : 'w-0']"
>
<icon-close :is-primary="active" @click="handleClose" />
</div>
<left-tab-radius
class="transition-opacity duration-400 ease-in-out"
:class="[showRadius ? 'opacity-100' : 'opacity-0']"
:is-primary="active"
:is-hover="isHover"
:is-left-hover="isLeftHover"
/>
<right-tab-radius
class="transition-opacity duration-400 ease-out"
:class="[showRadius ? 'opacity-100' : 'opacity-0']"
:is-primary="active"
:is-hover="isHover"
:is-right-hover="isRightHover"
/>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { useBoolean } from '@/hooks';
import { IconClose } from '../common';
import { LeftTabRadius, RightTabRadius } from './components';
const props = defineProps({
currentIndex: {
type: Number,
required: true
},
activeIndex: {
type: Number,
required: true
},
hoverIndex: {
type: Number,
default: NaN
},
closable: {
type: Boolean,
default: true
}
});
const emit = defineEmits(['close', 'update:hoverIndex']);
const { bool: isHover, setTrue, setFalse } = useBoolean();
const hoveredIndex = ref(props.hoverIndex);
function setHoverIndex(index: number) {
hoveredIndex.value = index;
}
function resetHoverIndex() {
hoveredIndex.value = NaN;
}
const active = computed(() => props.currentIndex === props.activeIndex);
const showRadius = computed(() => isHover.value || active.value);
const isLeftHover = computed(() => active.value && props.activeIndex === hoveredIndex.value + 1);
const isRightHover = computed(() => active.value && props.activeIndex === hoveredIndex.value - 1);
function handleMouseOnTab(mode: 'enter' | 'leave') {
if (mode === 'enter') {
setTrue();
setHoverIndex(props.currentIndex);
} else {
setFalse();
resetHoverIndex();
}
}
function handleClose(e: MouseEvent) {
e.stopPropagation();
emit('close');
}
watch(
() => props.hoverIndex,
newValue => {
setHoverIndex(newValue);
}
);
watch(hoveredIndex, newValue => {
emit('update:hoverIndex', newValue);
});
watch(
() => props.activeIndex,
() => {
resetHoverIndex();
}
);
</script>
<style scoped></style>

View File

@ -1,117 +1,29 @@
<template>
<div
class="
relative
inline-flex-center
h-30px
px-32px
transition-background
duration-400
ease-in-out
bg-opacity-10
cursor-pointer
"
:class="{ 'text-primary bg-primary z-3': active, 'bg-black z-2': isHover && !active }"
@mouseenter="handleMouseOnTab('enter')"
@mouseleave="handleMouseOnTab('leave')"
>
<span>
<slot></slot>
</span>
<div
v-if="closable"
class="transition-width duration-400 ease-in-out overflow-hidden"
:class="[isHover ? 'w-18px' : 'w-0']"
<div class="flex items-end h-full">
<browser-tab-item
v-for="(item, index) in app.multiTab.routes"
:key="item.path"
v-model:hover-index="hoverIndex"
:current-index="index"
:active-index="app.activeMultiTabIndex"
:closable="item.name !== ROUTE_HOME.name"
@click="handleClickTab(item.fullPath)"
@close="removeMultiTab(item.fullPath)"
>
<icon-close :is-primary="active" @click="handleClose" />
</div>
<left-tab-radius
class="transition-opacity duration-400 ease-in-out"
:class="[showRadius ? 'opacity-100' : 'opacity-0']"
:is-primary="active"
:is-hover="isHover"
:is-left-hover="isLeftHover"
/>
<right-tab-radius
class="transition-opacity duration-400 ease-out"
:class="[showRadius ? 'opacity-100' : 'opacity-0']"
:is-primary="active"
:is-hover="isHover"
:is-right-hover="isRightHover"
/>
{{ item.meta?.title }}
</browser-tab-item>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { useBoolean } from '@/hooks';
import { IconClose } from '../common';
import { LeftTabRadius, RightTabRadius } from './components';
<script setup lang="ts">
import { ref } from 'vue';
import { useAppStore } from '@/store';
import { ROUTE_HOME } from '@/router';
import { BrowserTabItem } from './components';
const props = defineProps({
currentIndex: {
type: Number,
required: true
},
activeIndex: {
type: Number,
required: true
},
hoverIndex: {
type: Number,
default: NaN
},
closable: {
type: Boolean,
default: true
}
});
const emit = defineEmits(['close', 'update:hoverIndex']);
const app = useAppStore();
const { removeMultiTab, handleClickTab } = useAppStore();
const { bool: isHover, setTrue, setFalse } = useBoolean();
const hoveredIndex = ref(props.hoverIndex);
function setHoverIndex(index: number) {
hoveredIndex.value = index;
}
function resetHoverIndex() {
hoveredIndex.value = NaN;
}
const active = computed(() => props.currentIndex === props.activeIndex);
const showRadius = computed(() => isHover.value || active.value);
const isLeftHover = computed(() => active.value && props.activeIndex === hoveredIndex.value + 1);
const isRightHover = computed(() => active.value && props.activeIndex === hoveredIndex.value - 1);
function handleMouseOnTab(mode: 'enter' | 'leave') {
if (mode === 'enter') {
setTrue();
setHoverIndex(props.currentIndex);
} else {
setFalse();
resetHoverIndex();
}
}
function handleClose(e: MouseEvent) {
e.stopPropagation();
emit('close');
}
watch(
() => props.hoverIndex,
newValue => {
setHoverIndex(newValue);
}
);
watch(hoveredIndex, newValue => {
emit('update:hoverIndex', newValue);
});
watch(
() => props.activeIndex,
() => {
resetHoverIndex();
}
);
const hoverIndex = ref(NaN);
</script>
<style scoped></style>

View File

@ -1,7 +1,7 @@
<template>
<div
class="relative flex-center w-18px h-18px text-14px"
:class="{ 'text-primary': isPrimary }"
:class="[isPrimary ? 'text-primary' : 'text-gray-400']"
@mouseenter="setTrue"
@mouseleave="setFalse"
>

View File

@ -21,21 +21,7 @@
{{ item.meta?.title }}
</button-tab>
</n-space>
<n-space v-if="theme.multiTabStyle.mode === 'browser'" :align="'flex-end'" :size="0" class="h-full px-16px">
<browser-tab
v-for="(item, index) in app.multiTab.routes"
:key="item.path"
v-model:hover-index="hoverIndex"
:current-index="index"
:active-index="app.activeMultiTabIndex"
:closable="item.name !== ROUTE_HOME.name"
@click="handleClickTab(item.fullPath)"
@close="removeMultiTab(item.fullPath)"
@contextmenu="handleContextMenu($event, item.fullPath)"
>
{{ item.meta?.title }}
</browser-tab>
</n-space>
<browser-tab />
<reload-button />
<context-menu
:visible="dropdownVisible"

View File

@ -14,6 +14,8 @@ const app = useAppStore();
const theme = useThemeStore();
const title = useAppTitle();
const showTitle = computed(() => !app.menu.collapsed || !theme.isVerticalNav);
const showTitle = computed(
() => !theme.isVerticalNav || (!app.menu.collapsed && theme.navStyle.mode !== 'vertical-mix')
);
</script>
<style scoped></style>