refactor(layouts): 重构布局系统组件代码块顺序

- 调整所有布局组件为template→script→style顺序
- 包括主布局、头部、侧边栏、标签页等组件
- 涉及leftMenu、topMenu、mixMenu等多种布局模式
- 统一布局组件结构,提升代码一致性
This commit is contained in:
Leo 2025-07-07 00:17:12 +08:00
parent a48cb3738d
commit a6c2a3cc5b
21 changed files with 454 additions and 454 deletions

View File

@ -1,6 +1,3 @@
<script setup lang="ts">
</script>
<template> <template>
<n-back-top :bottom="80" :visibility-height="300"> <n-back-top :bottom="80" :visibility-height="300">
<n-tooltip placement="left" trigger="hover"> <n-tooltip placement="left" trigger="hover">
@ -13,3 +10,6 @@
</n-tooltip> </n-tooltip>
</n-back-top> </n-back-top>
</template> </template>
<script setup lang="ts">
</script>

View File

@ -1,9 +1,3 @@
<script setup lang="ts">
import type { LayoutMode } from '@/store/app'
const value = defineModel<LayoutMode>('value', { required: true })
</script>
<template> <template>
<div class="flex-center gap-4"> <div class="flex-center gap-4">
<n-tooltip placement="bottom" trigger="hover"> <n-tooltip placement="bottom" trigger="hover">
@ -57,6 +51,12 @@ const value = defineModel<LayoutMode>('value', { required: true })
</div> </div>
</template> </template>
<script setup lang="ts">
import type { LayoutMode } from '@/store/app'
const value = defineModel<LayoutMode>('value', { required: true })
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.grid{ .grid{
height: 60px; height: 60px;

View File

@ -1,15 +1,3 @@
<script setup lang="ts">
interface Props {
list?: Entity.Message[]
}
const { list } = defineProps<Props>()
const emit = defineEmits<Emits>()
interface Emits {
(e: 'read', val: number): void
}
</script>
<template> <template>
<n-scrollbar style="height: 400px"> <n-scrollbar style="height: 400px">
<n-list hoverable clickable> <n-list hoverable clickable>
@ -42,4 +30,16 @@ interface Emits {
</n-scrollbar> </n-scrollbar>
</template> </template>
<script setup lang="ts">
interface Props {
list?: Entity.Message[]
}
const { list } = defineProps<Props>()
const emit = defineEmits<Emits>()
interface Emits {
(e: 'read', val: number): void
}
</script>
<style scoped></style> <style scoped></style>

View File

@ -1,9 +1,3 @@
<script setup lang="ts">
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>
<template> <template>
<n-tooltip placement="bottom" trigger="hover"> <n-tooltip placement="bottom" trigger="hover">
<template #trigger> <template #trigger>
@ -16,3 +10,9 @@ const appStore = useAppStore()
<span>{{ $t('app.setting') }}</span> <span>{{ $t('app.setting') }}</span>
</n-tooltip> </n-tooltip>
</template> </template>
<script setup lang="ts">
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>

View File

@ -1,3 +1,69 @@
<template>
<n-drawer v-model:show="appStore.showSetting" :width="360">
<n-drawer-content :title="t('app.systemSetting')" closable>
<n-space vertical>
<n-divider>{{ $t('app.layoutSetting') }}</n-divider>
<LayoutSelector v-model:value="appStore.layoutMode" />
<n-divider>{{ $t('app.themeSetting') }}</n-divider>
<n-space justify="space-between">
{{ $t('app.colorWeak') }}
<n-switch :value="appStore.colorWeak" @update:value="appStore.toggleColorWeak" />
</n-space>
<n-space justify="space-between">
{{ $t('app.blackAndWhite') }}
<n-switch :value="appStore.grayMode" @update:value="appStore.toggleGrayMode" />
</n-space>
<n-space align="center" justify="space-between">
{{ $t('app.themeColor') }}
<n-color-picker
v-model:value="appStore.primaryColor" class="w-10em" :swatches="palette"
@update:value="appStore.setPrimaryColor"
/>
</n-space>
<n-space align="center" justify="space-between">
{{ $t('app.pageTransition') }}
<n-select
v-model:value="appStore.transitionAnimation" class="w-10em"
:options="transitionSelectorOptions" @update:value="appStore.reloadPage"
/>
</n-space>
<n-divider>{{ $t('app.interfaceDisplay') }}</n-divider>
<n-space justify="space-between">
{{ $t('app.logoDisplay') }}
<n-switch v-model:value="appStore.showLogo" />
</n-space>
<n-space justify="space-between">
{{ $t('app.topProgress') }}
<n-switch v-model:value="appStore.showProgress" />
</n-space>
<n-space justify="space-between">
{{ $t('app.multitab') }}
<n-switch v-model:value="appStore.showTabs" />
</n-space>
<n-space justify="space-between">
{{ $t('app.bottomCopyright') }}
<n-switch v-model:value="appStore.showFooter" />
</n-space>
<n-space justify="space-between">
{{ $t('app.breadcrumb') }}
<n-switch v-model:value="appStore.showBreadcrumb" />
</n-space>
<n-space justify="space-between">
{{ $t('app.BreadcrumbIcon') }}
<n-switch v-model:value="appStore.showBreadcrumbIcon" />
</n-space>
</n-space>
<template #footer>
<n-button type="error" @click="resetSetting">
{{ $t('app.reset') }}
</n-button>
</template>
</n-drawer-content>
</n-drawer>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { useAppStore } from '@/store' import { useAppStore } from '@/store'
import { coiMsgBox, coiMsgSuccess } from '@/utils/coi' import { coiMsgBox, coiMsgSuccess } from '@/utils/coi'
@ -76,69 +142,3 @@ async function resetSetting() {
} }
} }
</script> </script>
<template>
<n-drawer v-model:show="appStore.showSetting" :width="360">
<n-drawer-content :title="t('app.systemSetting')" closable>
<n-space vertical>
<n-divider>{{ $t('app.layoutSetting') }}</n-divider>
<LayoutSelector v-model:value="appStore.layoutMode" />
<n-divider>{{ $t('app.themeSetting') }}</n-divider>
<n-space justify="space-between">
{{ $t('app.colorWeak') }}
<n-switch :value="appStore.colorWeak" @update:value="appStore.toggleColorWeak" />
</n-space>
<n-space justify="space-between">
{{ $t('app.blackAndWhite') }}
<n-switch :value="appStore.grayMode" @update:value="appStore.toggleGrayMode" />
</n-space>
<n-space align="center" justify="space-between">
{{ $t('app.themeColor') }}
<n-color-picker
v-model:value="appStore.primaryColor" class="w-10em" :swatches="palette"
@update:value="appStore.setPrimaryColor"
/>
</n-space>
<n-space align="center" justify="space-between">
{{ $t('app.pageTransition') }}
<n-select
v-model:value="appStore.transitionAnimation" class="w-10em"
:options="transitionSelectorOptions" @update:value="appStore.reloadPage"
/>
</n-space>
<n-divider>{{ $t('app.interfaceDisplay') }}</n-divider>
<n-space justify="space-between">
{{ $t('app.logoDisplay') }}
<n-switch v-model:value="appStore.showLogo" />
</n-space>
<n-space justify="space-between">
{{ $t('app.topProgress') }}
<n-switch v-model:value="appStore.showProgress" />
</n-space>
<n-space justify="space-between">
{{ $t('app.multitab') }}
<n-switch v-model:value="appStore.showTabs" />
</n-space>
<n-space justify="space-between">
{{ $t('app.bottomCopyright') }}
<n-switch v-model:value="appStore.showFooter" />
</n-space>
<n-space justify="space-between">
{{ $t('app.breadcrumb') }}
<n-switch v-model:value="appStore.showBreadcrumb" />
</n-space>
<n-space justify="space-between">
{{ $t('app.BreadcrumbIcon') }}
<n-switch v-model:value="appStore.showBreadcrumbIcon" />
</n-space>
</n-space>
<template #footer>
<n-button type="error" @click="resetSetting">
{{ $t('app.reset') }}
</n-button>
</template>
</n-drawer-content>
</n-drawer>
</template>

View File

@ -1,14 +1,3 @@
<script setup lang="ts">
import { useAppStore } from '@/store'
const router = useRouter()
const route = useRoute()
const routes = computed(() => {
return route.matched
})
const appStore = useAppStore()
</script>
<template> <template>
<TransitionGroup v-if="appStore.showBreadcrumb" name="list" tag="ul" style="display: flex; gap:1em;"> <TransitionGroup v-if="appStore.showBreadcrumb" name="list" tag="ul" style="display: flex; gap:1em;">
<n-el <n-el
@ -27,7 +16,18 @@ const appStore = useAppStore()
</TransitionGroup> </TransitionGroup>
</template> </template>
<style lang="scss"> <script setup lang="ts">
import { useAppStore } from '@/store'
const router = useRouter()
const route = useRoute()
const routes = computed(() => {
return route.matched
})
const appStore = useAppStore()
</script>
<style lang="scss" scoped>
.split:not(:first-child)::before { .split:not(:first-child)::before {
content: '/'; content: '/';
padding-right:0.6em; padding-right:0.6em;

View File

@ -1,9 +1,3 @@
<script setup lang="ts">
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>
<template> <template>
<n-tooltip placement="bottom" trigger="hover"> <n-tooltip placement="bottom" trigger="hover">
<template #trigger> <template #trigger>
@ -16,4 +10,10 @@ const appStore = useAppStore()
</n-tooltip> </n-tooltip>
</template> </template>
<script setup lang="ts">
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>
<style scoped></style> <style scoped></style>

View File

@ -1,3 +1,15 @@
<template>
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<CommonWrapper @click="appStore.toggleFullScreen">
<icon-park-outline-off-screen v-if="appStore.fullScreen" />
<icon-park-outline-full-screen v-else />
</CommonWrapper>
</template>
<span>{{ $t('app.toggleFullScreen') }}</span>
</n-tooltip>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { useAppStore } from '@/store' import { useAppStore } from '@/store'
@ -13,15 +25,3 @@ useMagicKeys({
}, },
}) })
</script> </script>
<template>
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<CommonWrapper @click="appStore.toggleFullScreen">
<icon-park-outline-off-screen v-if="appStore.fullScreen" />
<icon-park-outline-full-screen v-else />
</CommonWrapper>
</template>
<span>{{ $t('app.toggleFullScreen') }}</span>
</n-tooltip>
</template>

View File

@ -1,3 +1,49 @@
<template>
<n-popover placement="bottom" trigger="click" arrow-point-to-center class="!p-0">
<template #trigger>
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<CommonWrapper>
<n-badge :value="massageCount" :max="99" style="color: unset">
<icon-park-outline-remind />
</n-badge>
</CommonWrapper>
</template>
<span>{{ $t('app.notificationsTips') }}</span>
</n-tooltip>
</template>
<n-tabs v-model:value="currentTab" type="line" animated justify-content="space-evenly" class="w-390px">
<n-tab-pane :name="0">
<template #tab>
<n-space class="w-130px" justify="center">
{{ $t('app.notifications') }}
<n-badge type="info" :value="groupMessage[0]?.filter(i => !i.isRead).length" :max="99" />
</n-space>
</template>
<NoticeList :list="groupMessage[0]" @read="handleRead" />
</n-tab-pane>
<n-tab-pane :name="1">
<template #tab>
<n-space class="w-130px" justify="center">
{{ $t('app.messages') }}
<n-badge type="warning" :value="groupMessage[1]?.filter(i => !i.isRead).length" :max="99" />
</n-space>
</template>
<NoticeList :list="groupMessage[1]" @read="handleRead" />
</n-tab-pane>
<n-tab-pane :name="2">
<template #tab>
<n-space class="w-130px" justify="center">
{{ $t('app.todos') }}
<n-badge type="error" :value="groupMessage[2]?.filter(i => !i.isRead).length" :max="99" />
</n-space>
</template>
<NoticeList :list="groupMessage[2]" @read="handleRead" />
</n-tab-pane>
</n-tabs>
</n-popover>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { group } from 'radash' import { group } from 'radash'
import { coiMsgSuccess } from '@/utils/coi' import { coiMsgSuccess } from '@/utils/coi'
@ -95,50 +141,4 @@ const groupMessage = computed(() => {
}) })
</script> </script>
<template>
<n-popover placement="bottom" trigger="click" arrow-point-to-center class="!p-0">
<template #trigger>
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<CommonWrapper>
<n-badge :value="massageCount" :max="99" style="color: unset">
<icon-park-outline-remind />
</n-badge>
</CommonWrapper>
</template>
<span>{{ $t('app.notificationsTips') }}</span>
</n-tooltip>
</template>
<n-tabs v-model:value="currentTab" type="line" animated justify-content="space-evenly" class="w-390px">
<n-tab-pane :name="0">
<template #tab>
<n-space class="w-130px" justify="center">
{{ $t('app.notifications') }}
<n-badge type="info" :value="groupMessage[0]?.filter(i => !i.isRead).length" :max="99" />
</n-space>
</template>
<NoticeList :list="groupMessage[0]" @read="handleRead" />
</n-tab-pane>
<n-tab-pane :name="1">
<template #tab>
<n-space class="w-130px" justify="center">
{{ $t('app.messages') }}
<n-badge type="warning" :value="groupMessage[1]?.filter(i => !i.isRead).length" :max="99" />
</n-space>
</template>
<NoticeList :list="groupMessage[1]" @read="handleRead" />
</n-tab-pane>
<n-tab-pane :name="2">
<template #tab>
<n-space class="w-130px" justify="center">
{{ $t('app.todos') }}
<n-badge type="error" :value="groupMessage[2]?.filter(i => !i.isRead).length" :max="99" />
</n-space>
</template>
<NoticeList :list="groupMessage[2]" @read="handleRead" />
</n-tab-pane>
</n-tabs>
</n-popover>
</template>
<style scoped></style> <style scoped></style>

View File

@ -1,3 +1,78 @@
<template>
<CommonWrapper @click="openModal">
<icon-park-outline-search /><n-tag round size="small" class="font-mono cursor-pointer">
CtrlK
</n-tag>
</CommonWrapper>
<n-modal
v-model:show="showModal"
class="w-560px fixed top-60px inset-x-0"
size="small"
preset="card"
:segmented="{
content: true,
footer: true,
}"
:closable="false"
@after-leave="handleClose"
>
<template #header>
<n-input v-model:value="searchValue" :placeholder="$t('app.searchPlaceholder')" clearable size="large" @input="handleInputChange">
<template #prefix>
<n-icon>
<icon-park-outline-search />
</n-icon>
</template>
</n-input>
</template>
<n-scrollbar ref="scrollbarRef" class="h-450px">
<ul
v-if="options.length"
class="flex flex-col gap-8px p-8px p-r-3"
>
<n-el
v-for="(option, index) in options"
:key="option.value" tag="li" role="option"
class="cursor-pointer shadow h-62px"
:class="{ 'text-[var(--base-color)] bg-[var(--primary-color-hover)]': index === selectedIndex }"
@click="handleSelect(option.value)"
@mouseenter="handleMouseEnter(index)"
@mousemove="setKeyboardFalse"
>
<div class="grid grid-rows-2 grid-cols-[40px_1fr_30px] h-full p-2">
<div class="row-span-2 place-self-center">
<nova-icon :icon="option.icon" />
</div>
<span>{{ option.label }}</span>
<icon-park-outline-right class="row-span-2 place-self-center" />
<span class="op-70">{{ option.value }}</span>
</div>
</n-el>
</ul>
<n-empty v-else size="large" class="h-450px flex-center" />
</n-scrollbar>
<template #footer>
<n-flex>
<div class="flex-y-center gap-1">
<svg width="15" height="15" aria-label="Enter key" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3" /></g></svg>
<span>{{ $t('common.choose') }}</span>
</div>
<div class="flex-y-center gap-1">
<svg width="15" height="15" aria-label="Arrow down" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 3.5v8M10.5 8.5l-3 3-3-3" /></g></svg>
<svg width="15" height="15" aria-label="Arrow up" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 11.5v-8M10.5 6.5l-3-3-3 3" /></g></svg>
<span>{{ $t('common.navigate') }}</span>
</div>
<div class="flex-y-center gap-1">
<svg width="15" height="15" aria-label="Escape key" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956" /></g></svg>
<span>{{ $t('common.close') }}</span>
</div>
</n-flex>
</template>
</n-modal>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { useBoolean } from '@/hooks' import { useBoolean } from '@/hooks'
import { useRouteStore } from '@/store' import { useRouteStore } from '@/store'
@ -140,78 +215,3 @@ function handleMouseEnter(index: number) {
selectedIndex.value = index selectedIndex.value = index
} }
</script> </script>
<template>
<CommonWrapper @click="openModal">
<icon-park-outline-search /><n-tag round size="small" class="font-mono cursor-pointer">
CtrlK
</n-tag>
</CommonWrapper>
<n-modal
v-model:show="showModal"
class="w-560px fixed top-60px inset-x-0"
size="small"
preset="card"
:segmented="{
content: true,
footer: true,
}"
:closable="false"
@after-leave="handleClose"
>
<template #header>
<n-input v-model:value="searchValue" :placeholder="$t('app.searchPlaceholder')" clearable size="large" @input="handleInputChange">
<template #prefix>
<n-icon>
<icon-park-outline-search />
</n-icon>
</template>
</n-input>
</template>
<n-scrollbar ref="scrollbarRef" class="h-450px">
<ul
v-if="options.length"
class="flex flex-col gap-8px p-8px p-r-3"
>
<n-el
v-for="(option, index) in options"
:key="option.value" tag="li" role="option"
class="cursor-pointer shadow h-62px"
:class="{ 'text-[var(--base-color)] bg-[var(--primary-color-hover)]': index === selectedIndex }"
@click="handleSelect(option.value)"
@mouseenter="handleMouseEnter(index)"
@mousemove="setKeyboardFalse"
>
<div class="grid grid-rows-2 grid-cols-[40px_1fr_30px] h-full p-2">
<div class="row-span-2 place-self-center">
<nova-icon :icon="option.icon" />
</div>
<span>{{ option.label }}</span>
<icon-park-outline-right class="row-span-2 place-self-center" />
<span class="op-70">{{ option.value }}</span>
</div>
</n-el>
</ul>
<n-empty v-else size="large" class="h-450px flex-center" />
</n-scrollbar>
<template #footer>
<n-flex>
<div class="flex-y-center gap-1">
<svg width="15" height="15" aria-label="Enter key" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3" /></g></svg>
<span>{{ $t('common.choose') }}</span>
</div>
<div class="flex-y-center gap-1">
<svg width="15" height="15" aria-label="Arrow down" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 3.5v8M10.5 8.5l-3 3-3-3" /></g></svg>
<svg width="15" height="15" aria-label="Arrow up" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M7.5 11.5v-8M10.5 6.5l-3-3-3 3" /></g></svg>
<span>{{ $t('common.navigate') }}</span>
</div>
<div class="flex-y-center gap-1">
<svg width="15" height="15" aria-label="Escape key" role="img"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2"><path d="M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956" /></g></svg>
<span>{{ $t('common.close') }}</span>
</div>
</n-flex>
</template>
</n-modal>
</template>

View File

@ -1,12 +1,3 @@
<script setup lang="ts">
import { useAppStore } from '@/store'
const router = useRouter()
const appStore = useAppStore()
const name = import.meta.env.VITE_APP_NAME
</script>
<template> <template>
<div <div
class="logo-container h-60px cursor-pointer p-x-3" class="logo-container h-60px cursor-pointer p-x-3"
@ -26,6 +17,15 @@ const name = import.meta.env.VITE_APP_NAME
</div> </div>
</template> </template>
<script setup lang="ts">
import { useAppStore } from '@/store'
const router = useRouter()
const appStore = useAppStore()
const name = import.meta.env.VITE_APP_NAME
</script>
<style scoped> <style scoped>
.logo-container { .logo-container {
display: flex; display: flex;

View File

@ -1,3 +1,14 @@
<template>
<n-menu
ref="menuInstRef"
:collapsed="appStore.collapsed"
:indent="20"
:collapsed-width="64"
:options="routeStore.menus"
:value="routeStore.activeMenu"
/>
</template>
<script setup lang="ts"> <script setup lang="ts">
import type { MenuInst } from 'naive-ui' import type { MenuInst } from 'naive-ui'
import { useAppStore, useRouteStore } from '@/store' import { useAppStore, useRouteStore } from '@/store'
@ -15,14 +26,3 @@ watch(
{ immediate: true }, { immediate: true },
) )
</script> </script>
<template>
<n-menu
ref="menuInstRef"
:collapsed="appStore.collapsed"
:indent="20"
:collapsed-width="64"
:options="routeStore.menus"
:value="routeStore.activeMenu"
/>
</template>

View File

@ -1,9 +1,3 @@
<script setup lang="ts">
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>
<template> <template>
<n-tooltip placement="bottom" trigger="hover"> <n-tooltip placement="bottom" trigger="hover">
<template #trigger> <template #trigger>
@ -15,3 +9,9 @@ const appStore = useAppStore()
<span>{{ $t('app.togglContentFullScreen') }}</span> <span>{{ $t('app.togglContentFullScreen') }}</span>
</n-tooltip> </n-tooltip>
</template> </template>
<script setup lang="ts">
import { useAppStore } from '@/store'
const appStore = useAppStore()
</script>

View File

@ -1,3 +1,19 @@
<template>
<n-dropdown
:options="tabStore.allTabs"
:render-label="renderDropTabsLabel"
:render-icon="renderDropTabsIcon"
trigger="click"
size="small"
key-field="fullPath"
@select="handleDropTabs"
>
<CommonWrapper>
<icon-park-outline-application-menu />
</CommonWrapper>
</n-dropdown>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { useTabStore } from '@/store' import { useTabStore } from '@/store'
import { renderIcon } from '@/utils' import { renderIcon } from '@/utils'
@ -20,22 +36,6 @@ function handleDropTabs(key: string, option: any) {
} }
</script> </script>
<template>
<n-dropdown
:options="tabStore.allTabs"
:render-label="renderDropTabsLabel"
:render-icon="renderDropTabsIcon"
trigger="click"
size="small"
key-field="fullPath"
@select="handleDropTabs"
>
<CommonWrapper>
<icon-park-outline-application-menu />
</CommonWrapper>
</n-dropdown>
</template>
<style scoped> <style scoped>
</style> </style>

View File

@ -1,3 +1,14 @@
<template>
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<CommonWrapper @click="handleReload">
<icon-park-outline-refresh :class="{ 'animate-spin': loading }" />
</CommonWrapper>
</template>
<span>{{ $t('common.reload') }}</span>
</n-tooltip>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { useAppStore } from '@/store' import { useAppStore } from '@/store'
@ -14,15 +25,4 @@ function handleReload() {
} }
</script> </script>
<template>
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<CommonWrapper @click="handleReload">
<icon-park-outline-refresh :class="{ 'animate-spin': loading }" />
</CommonWrapper>
</template>
<span>{{ $t('common.reload') }}</span>
</n-tooltip>
</template>
<style scoped></style> <style scoped></style>

View File

@ -1,3 +1,38 @@
<template>
<n-scrollbar ref="scrollbar" class="relative flex h-full tab-bar-scroller-wrapper" content-class="pr-34 tab-bar-scroller-content" :x-scrollable="true" @wheel="onWheel">
<div class="p-l-2 flex wh-full relative">
<div class="flex items-end">
<TabBarItem
v-for="item in tabStore.pinTabs" :key="item.fullPath" :value="tabStore.currentTabPath" :route="item"
@click="handleTab(item)"
/>
</div>
<div class="flex items-end flex-1">
<TabBarItem
v-for="item in tabStore.tabs"
:key="item.fullPath"
:value="tabStore.currentTabPath"
:route="item"
closable
:data-tab-path="item.fullPath"
@close="tabStore.closeTab"
@click="handleTab(item)"
@contextmenu="handleContextMenu($event, item)"
/>
<n-dropdown
placement="bottom-start" trigger="manual" :x="x" :y="y" :options="options" :show="showDropdown"
:on-clickoutside="onClickoutside" @select="handleSelect"
/>
</div>
</div>
<n-el class="absolute right-0 top-0 flex items-center gap-1 bg-[var(--card-color)] h-full">
<Reload />
<ContentFullScreen />
<DropTabs />
</n-el>
</n-scrollbar>
</template>
<script setup lang="ts"> <script setup lang="ts">
import type { RouteLocationNormalized } from 'vue-router' import type { RouteLocationNormalized } from 'vue-router'
import { useAppStore, useTabStore } from '@/store' import { useAppStore, useTabStore } from '@/store'
@ -104,41 +139,6 @@ function onClickoutside() {
} }
</script> </script>
<template>
<n-scrollbar ref="scrollbar" class="relative flex h-full tab-bar-scroller-wrapper" content-class="pr-34 tab-bar-scroller-content" :x-scrollable="true" @wheel="onWheel">
<div class="p-l-2 flex wh-full relative">
<div class="flex items-end">
<TabBarItem
v-for="item in tabStore.pinTabs" :key="item.fullPath" :value="tabStore.currentTabPath" :route="item"
@click="handleTab(item)"
/>
</div>
<div class="flex items-end flex-1">
<TabBarItem
v-for="item in tabStore.tabs"
:key="item.fullPath"
:value="tabStore.currentTabPath"
:route="item"
closable
:data-tab-path="item.fullPath"
@close="tabStore.closeTab"
@click="handleTab(item)"
@contextmenu="handleContextMenu($event, item)"
/>
<n-dropdown
placement="bottom-start" trigger="manual" :x="x" :y="y" :options="options" :show="showDropdown"
:on-clickoutside="onClickoutside" @select="handleSelect"
/>
</div>
</div>
<n-el class="absolute right-0 top-0 flex items-center gap-1 bg-[var(--card-color)] h-full">
<Reload />
<ContentFullScreen />
<DropTabs />
</n-el>
</n-scrollbar>
</template>
<style scoped> <style scoped>
.ghost { .ghost {
opacity: 0.5; opacity: 0.5;

View File

@ -1,17 +1,3 @@
<script setup lang="ts">
import type { RouteLocationNormalized } from 'vue-router'
const { route, value, closable = false } = defineProps<{
route: RouteLocationNormalized
value: string
closable?: boolean
}>()
const emit = defineEmits<{
close: [string]
}>()
</script>
<template> <template>
<n-el <n-el
class="cursor-pointer p-x-4 p-y-2 m-x-2px b b-[--divider-color] b-b-[#0000] rounded-[--border-radius]" class="cursor-pointer p-x-4 p-y-2 m-x-2px b b-[--divider-color] b-b-[#0000] rounded-[--border-radius]"
@ -39,3 +25,17 @@ const emit = defineEmits<{
</div> </div>
</n-el> </n-el>
</template> </template>
<script setup lang="ts">
import type { RouteLocationNormalized } from 'vue-router'
const { route, value, closable = false } = defineProps<{
route: RouteLocationNormalized
value: string
closable?: boolean
}>()
const emit = defineEmits<{
close: [string]
}>()
</script>

View File

@ -1,3 +1,8 @@
<template>
<SettingDrawer />
<component :is="layoutMap[appStore.layoutMode]" />
</template>
<script setup lang="ts"> <script setup lang="ts">
import { useAppStore } from '@/store/app' import { useAppStore } from '@/store/app'
import { SettingDrawer } from './components' import { SettingDrawer } from './components'
@ -12,8 +17,3 @@ const layoutMap = {
mixMenu, mixMenu,
} }
</script> </script>
<template>
<SettingDrawer />
<component :is="layoutMap[appStore.layoutMode]" />
</template>

View File

@ -1,22 +1,3 @@
<script lang="ts" setup>
import { useAppStore, useRouteStore } from '@/store'
import {
BackTop,
Breadcrumb,
CollapaseButton,
FullScreen,
Logo,
Menu,
Notices,
Search,
Setting,
TabBar,
} from './components'
const routeStore = useRouteStore()
const appStore = useAppStore()
</script>
<template> <template>
<n-layout <n-layout
has-sider has-sider
@ -99,3 +80,22 @@ const appStore = useAppStore()
</n-layout> </n-layout>
</n-layout> </n-layout>
</template> </template>
<script lang="ts" setup>
import { useAppStore, useRouteStore } from '@/store'
import {
BackTop,
Breadcrumb,
CollapaseButton,
FullScreen,
Logo,
Menu,
Notices,
Search,
Setting,
TabBar,
} from './components'
const routeStore = useRouteStore()
const appStore = useAppStore()
</script>

View File

@ -1,70 +1,3 @@
<script lang="ts" setup>
import type { MenuInst, MenuOption } from 'naive-ui'
import { useAppStore, useRouteStore } from '@/store'
import {
BackTop,
CollapaseButton,
FullScreen,
Logo,
Notices,
Search,
Setting,
TabBar,
} from './components'
const routeStore = useRouteStore()
const appStore = useAppStore()
const pageRoute = useRoute()
const router = useRouter()
const menuInstRef = ref<MenuInst | null>(null)
watch(
() => pageRoute.path,
() => {
menuInstRef.value?.showOption(routeStore.activeMenu as string)
},
{ immediate: true },
)
const topMenu = ref<MenuOption[]>([])
const activeTopMenu = ref<string>('')
function handleTopMenu(rowMenu: MenuOption[]) {
topMenu.value = rowMenu.map((i) => {
const { icon, label, key } = i
return {
icon,
label,
key,
}
})
}
onMounted(() => {
handleTopMenu(routeStore.menus)
//
const currentMenuKey = pageRoute.matched[1].path
handleSideMenu(currentMenuKey)
activeTopMenu.value = currentMenuKey
})
const sideMenu = ref<MenuOption[]>([])
function handleSideMenu(key: string) {
const routeMenu = routeStore.menus as MenuOption[]
const targetMenu = routeMenu.find(i => i.key === key)
if (targetMenu) {
sideMenu.value = targetMenu.children ? targetMenu.children : [targetMenu]
}
}
function updateTopMenu(key: string) {
handleSideMenu(key)
activeTopMenu.value = key
router.push(key)
}
</script>
<template> <template>
<n-layout <n-layout
has-sider has-sider
@ -157,3 +90,70 @@ function updateTopMenu(key: string) {
</n-layout> </n-layout>
</n-layout> </n-layout>
</template> </template>
<script lang="ts" setup>
import type { MenuInst, MenuOption } from 'naive-ui'
import { useAppStore, useRouteStore } from '@/store'
import {
BackTop,
CollapaseButton,
FullScreen,
Logo,
Notices,
Search,
Setting,
TabBar,
} from './components'
const routeStore = useRouteStore()
const appStore = useAppStore()
const pageRoute = useRoute()
const router = useRouter()
const menuInstRef = ref<MenuInst | null>(null)
watch(
() => pageRoute.path,
() => {
menuInstRef.value?.showOption(routeStore.activeMenu as string)
},
{ immediate: true },
)
const topMenu = ref<MenuOption[]>([])
const activeTopMenu = ref<string>('')
function handleTopMenu(rowMenu: MenuOption[]) {
topMenu.value = rowMenu.map((i) => {
const { icon, label, key } = i
return {
icon,
label,
key,
}
})
}
onMounted(() => {
handleTopMenu(routeStore.menus)
//
const currentMenuKey = pageRoute.matched[1].path
handleSideMenu(currentMenuKey)
activeTopMenu.value = currentMenuKey
})
const sideMenu = ref<MenuOption[]>([])
function handleSideMenu(key: string) {
const routeMenu = routeStore.menus as MenuOption[]
const targetMenu = routeMenu.find(i => i.key === key)
if (targetMenu) {
sideMenu.value = targetMenu.children ? targetMenu.children : [targetMenu]
}
}
function updateTopMenu(key: string) {
handleSideMenu(key)
activeTopMenu.value = key
router.push(key)
}
</script>

View File

@ -1,20 +1,3 @@
<script lang="ts" setup>
import { useAppStore, useRouteStore } from '@/store'
import {
BackTop,
FullScreen,
Logo,
Menu,
Notices,
Search,
Setting,
TabBar,
} from './components'
const routeStore = useRouteStore()
const appStore = useAppStore()
</script>
<template> <template>
<n-layout class="wh-full" embedded> <n-layout class="wh-full" embedded>
<n-layout <n-layout
@ -64,3 +47,20 @@ const appStore = useAppStore()
</n-layout> </n-layout>
</n-layout> </n-layout>
</template> </template>
<script lang="ts" setup>
import { useAppStore, useRouteStore } from '@/store'
import {
BackTop,
FullScreen,
Logo,
Menu,
Notices,
Search,
Setting,
TabBar,
} from './components'
const routeStore = useRouteStore()
const appStore = useAppStore()
</script>