refactor: 工具栏文件目录结构调整

This commit is contained in:
Hooray Hu 2023-12-30 15:42:37 +08:00
parent 75d815d315
commit 30a303195f
12 changed files with 255 additions and 204 deletions

View File

@ -176,6 +176,21 @@ function handleCopy() {
<div class="divider">
工具栏
</div>
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
<div class="label">
面包屑导航
</div>
<HToggle v-model="settingsStore.settings.toolbar.breadcrumb" />
</div>
<div class="setting-item">
<div class="label">
导航搜索
<HTooltip text="对导航进行快捷搜索">
<SvgIcon name="ri:question-line" />
</HTooltip>
</div>
<HToggle v-model="settingsStore.settings.toolbar.navSearch" />
</div>
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
<div class="label">
全屏
@ -200,15 +215,6 @@ function handleCopy() {
</div>
<HToggle v-model="settingsStore.settings.toolbar.colorScheme" />
</div>
<div v-if="settingsStore.mode === 'pc'" class="divider">
面包屑导航
</div>
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
<div class="label">
是否启用
</div>
<HToggle v-model="settingsStore.settings.toolbar.breadcrumb" />
</div>
<div class="divider">
页面
</div>
@ -221,15 +227,6 @@ function handleCopy() {
<div class="divider">
导航搜索
</div>
<div class="setting-item">
<div class="label">
是否启用
<HTooltip text="对导航进行快捷搜索">
<SvgIcon name="ri:question-line" />
</HTooltip>
</div>
<HToggle v-model="settingsStore.settings.toolbar.navSearch" />
</div>
<div class="setting-item">
<div class="label">
是否启用快捷键

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import Logo from '../Logo/index.vue'
import Tools from '../Tools/index.vue'
import ToolbarRightSide from '../Topbar/Toolbar/rightSide.vue'
import useMenuStore from '@/store/modules/menu'
import useSettingsStore from '@/store/modules/settings'
@ -55,7 +55,7 @@ function handlerMouserScroll(event: WheelEvent) {
</div>
</div>
</div>
<Tools />
<ToolbarRightSide />
</div>
</header>
</Transition>

View File

@ -1,104 +0,0 @@
<script setup lang="ts">
import { useFullscreen } from '@vueuse/core'
import eventBus from '@/utils/eventBus'
import useSettingsStore from '@/store/modules/settings'
import useUserStore from '@/store/modules/user'
defineOptions({
name: 'Tools',
})
const router = useRouter()
const settingsStore = useSettingsStore()
const userStore = useUserStore()
const mainPage = useMainPage()
const { isFullscreen, toggle } = useFullscreen()
function toggleColorScheme(event: MouseEvent) {
const { startViewTransition } = useViewTransition(() => {
settingsStore.setColorScheme(settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark')
})
startViewTransition()?.ready.then(() => {
const x = event.clientX
const y = event.clientY
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y),
)
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
]
document.documentElement.animate(
{
clipPath: settingsStore.settings.app.colorScheme !== 'dark' ? clipPath : clipPath.reverse(),
},
{
duration: 300,
easing: 'ease-out',
pseudoElement: settingsStore.settings.app.colorScheme !== 'dark' ? '::view-transition-new(root)' : '::view-transition-old(root)',
},
)
})
}
const avatarError = ref(false)
watch(() => userStore.avatar, () => {
if (avatarError.value) {
avatarError.value = false
}
})
</script>
<template>
<div class="tools flex items-center gap-4 whitespace-nowrap px-4">
<span v-if="settingsStore.settings.toolbar.navSearch && settingsStore.mode === 'pc'" class="group inline-flex cursor-pointer items-center gap-1 rounded-2 bg-stone-1 px-2 py-1.5 text-dark ring-stone-3 ring-inset transition dark:bg-stone-9 dark:text-white hover:ring-1 dark:ring-stone-7" @click="eventBus.emit('global-search-toggle')">
<SvgIcon name="ri:search-line" />
<span class="text-sm text-stone-5 transition group-hover:text-dark dark:group-hover:text-white">搜索</span>
<HKbd v-if="settingsStore.settings.navSearch.enableHotkeys" class="ml-2">{{ settingsStore.os === 'mac' ? '' : 'Alt' }} S</HKbd>
</span>
<div class="flex items-center empty:hidden">
<span v-if="settingsStore.settings.toolbar.navSearch && settingsStore.mode === 'mobile'" class="item" @click="eventBus.emit('global-search-toggle')">
<SvgIcon name="ri:search-line" />
</span>
<span v-if="settingsStore.mode === 'pc' && settingsStore.settings.toolbar.fullscreen" class="item" @click="toggle">
<SvgIcon :name="isFullscreen ? 'ri:fullscreen-exit-line' : 'ri:fullscreen-line'" />
</span>
<span v-if="settingsStore.settings.toolbar.pageReload" class="item" @click="mainPage.reload()">
<SvgIcon name="iconoir:refresh-double" />
</span>
<span v-if="settingsStore.settings.toolbar.colorScheme" class="item" @click="toggleColorScheme">
<SvgIcon :name="settingsStore.settings.app.colorScheme === 'light' ? 'ri:sun-line' : 'ri:moon-line'" />
</span>
</div>
<HDropdownMenu
:items="[
[
{ label: settingsStore.settings.home.title, handle: () => router.push({ path: settingsStore.settings.home.fullPath }), hide: !settingsStore.settings.home.enable },
{ label: '个人设置', handle: () => router.push({ name: 'personalSetting' }) },
],
[
{ label: '快捷键介绍', handle: () => eventBus.emit('global-hotkeys-intro-toggle'), hide: settingsStore.mode !== 'pc' },
],
[
{ label: '退出登录', handle: () => userStore.logout() },
],
]"
>
<div flex-center cursor-pointer gap-1>
<img v-if="userStore.avatar && !avatarError" :src="userStore.avatar" :onerror="() => (avatarError = true)" class="h-[24px] w-[24px] rounded-full">
<SvgIcon v-else name="carbon:user-avatar-filled-alt" :size="24" class="text-gray-400" />
{{ userStore.account }}
<SvgIcon name="ep:caret-bottom" />
</div>
</HDropdownMenu>
</div>
</template>
<style lang="scss" scoped>
.item {
--at-apply: flex px-2 py-1 cursor-pointer;
}
</style>

View File

@ -0,0 +1,58 @@
<script setup lang="ts">
import { compile } from 'path-to-regexp'
import Breadcrumb from '../../../Breadcrumb/index.vue'
import BreadcrumbItem from '../../../Breadcrumb/item.vue'
import useSettingsStore from '@/store/modules/settings'
const route = useRoute()
const settingsStore = useSettingsStore()
const breadcrumbList = computed(() => {
const breadcrumbList = []
if (settingsStore.settings.home.enable) {
breadcrumbList.push({
path: settingsStore.settings.home.fullPath,
title: settingsStore.settings.home.title,
})
}
if (route.meta.breadcrumbNeste) {
route.meta.breadcrumbNeste.forEach((item) => {
if (item.hide === false) {
breadcrumbList.push({
path: item.path,
title: item.title,
})
}
})
}
return breadcrumbList
})
function pathCompile(path: string) {
const toPath = compile(path)
return toPath(route.params)
}
</script>
<template>
<Breadcrumb v-if="settingsStore.mode === 'pc' && settingsStore.settings.app.routeBaseOn !== 'filesystem'" class="breadcrumb whitespace-nowrap px-2">
<TransitionGroup name="breadcrumb">
<BreadcrumbItem v-for="(item, index) in breadcrumbList" :key="`${index}_${item.path}_${item.title}`" :to="index < breadcrumbList.length - 1 && item.path !== '' ? pathCompile(item.path) : ''">
{{ item.title }}
</BreadcrumbItem>
</TransitionGroup>
</Breadcrumb>
</template>
<style lang="scss" scoped>
//
.breadcrumb-enter-active {
transition: transform 0.3s, opacity 0.3s;
}
.breadcrumb-enter-from {
opacity: 0;
transform: translateX(30px) skewX(-50deg);
}
</style>

View File

@ -0,0 +1,43 @@
<script setup lang="ts">
import useSettingsStore from '@/store/modules/settings'
defineOptions({
name: 'ColorScheme',
})
const settingsStore = useSettingsStore()
function toggleColorScheme(event: MouseEvent) {
const { startViewTransition } = useViewTransition(() => {
settingsStore.setColorScheme(settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark')
})
startViewTransition()?.ready.then(() => {
const x = event.clientX
const y = event.clientY
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y),
)
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
]
document.documentElement.animate(
{
clipPath: settingsStore.settings.app.colorScheme !== 'dark' ? clipPath : clipPath.reverse(),
},
{
duration: 300,
easing: 'ease-out',
pseudoElement: settingsStore.settings.app.colorScheme !== 'dark' ? '::view-transition-new(root)' : '::view-transition-old(root)',
},
)
})
}
</script>
<template>
<span class="flex-center cursor-pointer px-2 py-1" @click="toggleColorScheme">
<SvgIcon :name="settingsStore.settings.app.colorScheme === 'light' ? 'ri:sun-line' : 'ri:moon-line'" />
</span>
</template>

View File

@ -0,0 +1,18 @@
<script setup lang="ts">
import { useFullscreen } from '@vueuse/core'
import useSettingsStore from '@/store/modules/settings'
defineOptions({
name: 'Fullscreen',
})
const settingsStore = useSettingsStore()
const { isFullscreen, toggle } = useFullscreen()
</script>
<template>
<span v-if="settingsStore.mode === 'pc'" class="flex-center cursor-pointer px-2 py-1" @click="toggle">
<SvgIcon :name="isFullscreen ? 'ri:fullscreen-exit-line' : 'ri:fullscreen-line'" />
</span>
</template>

View File

@ -0,0 +1,21 @@
<script setup lang="ts">
import eventBus from '@/utils/eventBus'
import useSettingsStore from '@/store/modules/settings'
defineOptions({
name: 'ToolbarRightSide',
})
const settingsStore = useSettingsStore()
</script>
<template>
<span class="flex-center cursor-pointer px-2 py-1" @click="eventBus.emit('global-search-toggle')">
<SvgIcon v-if="settingsStore.mode === 'mobile'" name="ri:search-line" />
<span v-else class="group inline-flex cursor-pointer items-center gap-1 whitespace-nowrap rounded-2 bg-stone-1 px-2 py-1.5 text-dark ring-stone-3 ring-inset transition dark:bg-stone-9 dark:text-white hover:ring-1 dark:ring-stone-7">
<SvgIcon name="ri:search-line" />
<span class="text-sm text-stone-5 transition group-hover:text-dark dark:group-hover:text-white">搜索</span>
<HKbd v-if="settingsStore.settings.navSearch.enableHotkeys" class="ml-2">{{ settingsStore.os === 'mac' ? '' : 'Alt' }} S</HKbd>
</span>
</span>
</template>

View File

@ -0,0 +1,13 @@
<script setup lang="ts">
defineOptions({
name: 'PageReload',
})
const mainPage = useMainPage()
</script>
<template>
<span class="flex-center cursor-pointer px-2 py-1" @click="mainPage.reload()">
<SvgIcon name="iconoir:refresh-double" />
</span>
</template>

View File

@ -1,98 +1,30 @@
<script setup lang="ts">
import { compile } from 'path-to-regexp'
import Breadcrumb from '../../Breadcrumb/index.vue'
import BreadcrumbItem from '../../Breadcrumb/item.vue'
import Tools from '../../Tools/index.vue'
import LeftSide from './leftSide.vue'
import RightSide from './rightSide.vue'
import useSettingsStore from '@/store/modules/settings'
defineOptions({
name: 'Toolbar',
})
const route = useRoute()
const settingsStore = useSettingsStore()
const enableSubMenuCollapseButton = computed(() => {
return settingsStore.mode === 'mobile' || settingsStore.settings.menu.enableSubMenuCollapseButton
})
const breadcrumbList = computed(() => {
const breadcrumbList = []
if (settingsStore.settings.home.enable) {
breadcrumbList.push({
path: settingsStore.settings.home.fullPath,
title: settingsStore.settings.home.title,
})
}
if (route.meta.breadcrumbNeste) {
route.meta.breadcrumbNeste.forEach((item) => {
if (item.hide === false) {
breadcrumbList.push({
path: item.path,
title: item.title,
})
}
})
}
return breadcrumbList
})
function pathCompile(path: string) {
const toPath = compile(path)
return toPath(route.params)
}
</script>
<template>
<div class="toolbar-container">
<div class="left-box">
<div v-if="enableSubMenuCollapseButton" class="flex-center cursor-pointer px-2 py-1 transition-transform" :class="{ '-rotate-z-180': settingsStore.settings.menu.subMenuCollapse }" @click="settingsStore.toggleSidebarCollapse()">
<SvgIcon name="toolbar-collapse" />
</div>
<Breadcrumb v-if="settingsStore.mode === 'pc' && settingsStore.settings.toolbar.breadcrumb && settingsStore.settings.app.routeBaseOn !== 'filesystem'" class="breadcrumb">
<TransitionGroup name="breadcrumb">
<BreadcrumbItem v-for="(item, index) in breadcrumbList" :key="`${index}_${item.path}_${item.title}`" :to="index < breadcrumbList.length - 1 && item.path !== '' ? pathCompile(item.path) : ''">
{{ item.title }}
</BreadcrumbItem>
</TransitionGroup>
</Breadcrumb>
<div class="toolbar-container flex items-center justify-between">
<div class="h-full flex items-center of-hidden pl-2 pr-16" style="mask-image: linear-gradient(90deg, #000 0%, #000 calc(100% - 50px), transparent);">
<LeftSide />
</div>
<div v-show="['side', 'single'].includes(settingsStore.settings.menu.menuMode)" class="h-full flex items-center px-2">
<RightSide />
</div>
<Tools />
</div>
</template>
<style lang="scss" scoped>
.toolbar-container {
display: flex;
align-items: center;
justify-content: space-between;
height: var(--g-toolbar-height);
background-color: var(--g-container-bg);
transition: background-color 0.3s;
.left-box {
display: flex;
align-items: center;
padding-right: 50px;
padding-left: 10px;
overflow: hidden;
mask-image: linear-gradient(90deg, #000 0%, #000 calc(100% - 50px), transparent);
.breadcrumb {
padding-left: 10px;
white-space: nowrap;
}
}
}
//
.breadcrumb-enter-active {
transition: transform 0.3s, opacity 0.3s;
}
.breadcrumb-enter-from {
opacity: 0;
transform: translateX(30px) skewX(-50deg);
}
</style>

View File

@ -0,0 +1,23 @@
<script setup lang="ts">
import Breadcrumb from './Breadcrumb/index.vue'
import useSettingsStore from '@/store/modules/settings'
defineOptions({
name: 'ToolbarLeftSide',
})
const settingsStore = useSettingsStore()
const enableSubMenuCollapseButton = computed(() => {
return settingsStore.mode === 'mobile' || settingsStore.settings.menu.enableSubMenuCollapseButton
})
</script>
<template>
<div class="flex items-center">
<div v-if="enableSubMenuCollapseButton" class="flex-center cursor-pointer px-2 py-1 transition-transform" :class="{ '-rotate-z-180': settingsStore.settings.menu.subMenuCollapse }" @click="settingsStore.toggleSidebarCollapse()">
<SvgIcon name="toolbar-collapse" />
</div>
<Breadcrumb v-if="settingsStore.settings.toolbar.breadcrumb" />
</div>
</template>

View File

@ -0,0 +1,54 @@
<script setup lang="ts">
import NavSearch from './NavSearch/index.vue'
import Fullscreen from './Fullscreen/index.vue'
import PageReload from './PageReload/index.vue'
import ColorScheme from './ColorScheme/index.vue'
import eventBus from '@/utils/eventBus'
import useSettingsStore from '@/store/modules/settings'
import useUserStore from '@/store/modules/user'
defineOptions({
name: 'Tools',
})
const router = useRouter()
const settingsStore = useSettingsStore()
const userStore = useUserStore()
const avatarError = ref(false)
watch(() => userStore.avatar, () => {
if (avatarError.value) {
avatarError.value = false
}
})
</script>
<template>
<div class="flex items-center">
<NavSearch v-if="settingsStore.settings.toolbar.navSearch" />
<Fullscreen v-if="settingsStore.settings.toolbar.fullscreen" />
<PageReload v-if="settingsStore.settings.toolbar.pageReload" />
<ColorScheme v-if="settingsStore.settings.toolbar.colorScheme" />
<HDropdownMenu
:items="[
[
{ label: settingsStore.settings.home.title, handle: () => router.push({ path: settingsStore.settings.home.fullPath }), hide: !settingsStore.settings.home.enable },
],
[
{ label: '快捷键介绍', handle: () => eventBus.emit('global-hotkeys-intro-toggle'), hide: settingsStore.mode !== 'pc' },
],
[
{ label: '退出登录', handle: () => userStore.logout() },
],
]" class="flex-center cursor-pointer px-2"
>
<div class="flex-center gap-1">
<img v-if="userStore.avatar && !avatarError" :src="userStore.avatar" :onerror="() => (avatarError = true)" class="h-[24px] w-[24px] rounded-full">
<SvgIcon v-else name="carbon:user-avatar-filled-alt" :size="24" class="text-gray-400" />
{{ userStore.account }}
<SvgIcon name="ep:caret-bottom" />
</div>
</HDropdownMenu>
</div>
</template>

View File

@ -222,10 +222,6 @@ header:not(.header-leave-active) + .wrapper {
.main-container {
.topbar-container {
top: var(--g-header-height);
:deep(.tools) {
display: none;
}
}
}
}