mirror of
https://gitee.com/fantastic-admin/basic.git
synced 2024-12-04 04:57:53 +08:00
refactor: 工具栏文件目录结构调整
This commit is contained in:
parent
75d815d315
commit
30a303195f
@ -176,6 +176,21 @@ function handleCopy() {
|
|||||||
<div class="divider">
|
<div class="divider">
|
||||||
工具栏
|
工具栏
|
||||||
</div>
|
</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 v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
全屏
|
全屏
|
||||||
@ -200,15 +215,6 @@ function handleCopy() {
|
|||||||
</div>
|
</div>
|
||||||
<HToggle v-model="settingsStore.settings.toolbar.colorScheme" />
|
<HToggle v-model="settingsStore.settings.toolbar.colorScheme" />
|
||||||
</div>
|
</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 class="divider">
|
||||||
页面
|
页面
|
||||||
</div>
|
</div>
|
||||||
@ -221,15 +227,6 @@ function handleCopy() {
|
|||||||
<div class="divider">
|
<div class="divider">
|
||||||
导航搜索
|
导航搜索
|
||||||
</div>
|
</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="setting-item">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
是否启用快捷键
|
是否启用快捷键
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Logo from '../Logo/index.vue'
|
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 useMenuStore from '@/store/modules/menu'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ function handlerMouserScroll(event: WheelEvent) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Tools />
|
<ToolbarRightSide />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
@ -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>
|
|
58
src/layouts/components/Topbar/Toolbar/Breadcrumb/index.vue
Normal file
58
src/layouts/components/Topbar/Toolbar/Breadcrumb/index.vue
Normal 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>
|
43
src/layouts/components/Topbar/Toolbar/ColorScheme/index.vue
Normal file
43
src/layouts/components/Topbar/Toolbar/ColorScheme/index.vue
Normal 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>
|
18
src/layouts/components/Topbar/Toolbar/Fullscreen/index.vue
Normal file
18
src/layouts/components/Topbar/Toolbar/Fullscreen/index.vue
Normal 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>
|
21
src/layouts/components/Topbar/Toolbar/NavSearch/index.vue
Normal file
21
src/layouts/components/Topbar/Toolbar/NavSearch/index.vue
Normal 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>
|
13
src/layouts/components/Topbar/Toolbar/PageReload/index.vue
Normal file
13
src/layouts/components/Topbar/Toolbar/PageReload/index.vue
Normal 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>
|
@ -1,98 +1,30 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { compile } from 'path-to-regexp'
|
import LeftSide from './leftSide.vue'
|
||||||
import Breadcrumb from '../../Breadcrumb/index.vue'
|
import RightSide from './rightSide.vue'
|
||||||
import BreadcrumbItem from '../../Breadcrumb/item.vue'
|
|
||||||
import Tools from '../../Tools/index.vue'
|
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Toolbar',
|
name: 'Toolbar',
|
||||||
})
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const settingsStore = useSettingsStore()
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="toolbar-container">
|
<div class="toolbar-container flex items-center justify-between">
|
||||||
<div class="left-box">
|
<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);">
|
||||||
<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()">
|
<LeftSide />
|
||||||
<SvgIcon name="toolbar-collapse" />
|
</div>
|
||||||
</div>
|
<div v-show="['side', 'single'].includes(settingsStore.settings.menu.menuMode)" class="h-full flex items-center px-2">
|
||||||
<Breadcrumb v-if="settingsStore.mode === 'pc' && settingsStore.settings.toolbar.breadcrumb && settingsStore.settings.app.routeBaseOn !== 'filesystem'" class="breadcrumb">
|
<RightSide />
|
||||||
<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>
|
</div>
|
||||||
<Tools />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.toolbar-container {
|
.toolbar-container {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: var(--g-toolbar-height);
|
height: var(--g-toolbar-height);
|
||||||
background-color: var(--g-container-bg);
|
background-color: var(--g-container-bg);
|
||||||
transition: background-color 0.3s;
|
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>
|
</style>
|
||||||
|
23
src/layouts/components/Topbar/Toolbar/leftSide.vue
Normal file
23
src/layouts/components/Topbar/Toolbar/leftSide.vue
Normal 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>
|
54
src/layouts/components/Topbar/Toolbar/rightSide.vue
Normal file
54
src/layouts/components/Topbar/Toolbar/rightSide.vue
Normal 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>
|
@ -222,10 +222,6 @@ header:not(.header-leave-active) + .wrapper {
|
|||||||
.main-container {
|
.main-container {
|
||||||
.topbar-container {
|
.topbar-container {
|
||||||
top: var(--g-header-height);
|
top: var(--g-header-height);
|
||||||
|
|
||||||
:deep(.tools) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user