refactor(layouts): 重构布局系统组件代码块顺序
- 调整所有布局组件为template→script→style顺序 - 包括主布局、头部、侧边栏、标签页等组件 - 涉及leftMenu、topMenu、mixMenu等多种布局模式 - 统一布局组件结构,提升代码一致性
This commit is contained in:
parent
a48cb3738d
commit
a6c2a3cc5b
@ -1,6 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-back-top :bottom="80" :visibility-height="300">
|
||||
<n-tooltip placement="left" trigger="hover">
|
||||
@ -13,3 +10,6 @@
|
||||
</n-tooltip>
|
||||
</n-back-top>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
@ -1,9 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import type { LayoutMode } from '@/store/app'
|
||||
|
||||
const value = defineModel<LayoutMode>('value', { required: true })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-center gap-4">
|
||||
<n-tooltip placement="bottom" trigger="hover">
|
||||
@ -57,6 +51,12 @@ const value = defineModel<LayoutMode>('value', { required: true })
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { LayoutMode } from '@/store/app'
|
||||
|
||||
const value = defineModel<LayoutMode>('value', { required: true })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.grid{
|
||||
height: 60px;
|
||||
|
||||
@ -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>
|
||||
<n-scrollbar style="height: 400px">
|
||||
<n-list hoverable clickable>
|
||||
@ -42,4 +30,16 @@ interface Emits {
|
||||
</n-scrollbar>
|
||||
</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>
|
||||
|
||||
@ -1,9 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-tooltip placement="bottom" trigger="hover">
|
||||
<template #trigger>
|
||||
@ -16,3 +10,9 @@ const appStore = useAppStore()
|
||||
<span>{{ $t('app.setting') }}</span>
|
||||
</n-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
@ -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">
|
||||
import { useAppStore } from '@/store'
|
||||
import { coiMsgBox, coiMsgSuccess } from '@/utils/coi'
|
||||
@ -76,69 +142,3 @@ async function resetSetting() {
|
||||
}
|
||||
}
|
||||
</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>
|
||||
|
||||
@ -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>
|
||||
<TransitionGroup v-if="appStore.showBreadcrumb" name="list" tag="ul" style="display: flex; gap:1em;">
|
||||
<n-el
|
||||
@ -27,7 +16,18 @@ const appStore = useAppStore()
|
||||
</TransitionGroup>
|
||||
</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 {
|
||||
content: '/';
|
||||
padding-right:0.6em;
|
||||
|
||||
@ -1,9 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-tooltip placement="bottom" trigger="hover">
|
||||
<template #trigger>
|
||||
@ -16,4 +10,10 @@ const appStore = useAppStore()
|
||||
</n-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@ -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">
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
@ -13,15 +25,3 @@ useMagicKeys({
|
||||
},
|
||||
})
|
||||
</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>
|
||||
|
||||
@ -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">
|
||||
import { group } from 'radash'
|
||||
import { coiMsgSuccess } from '@/utils/coi'
|
||||
@ -95,50 +141,4 @@ const groupMessage = computed(() => {
|
||||
})
|
||||
</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>
|
||||
|
||||
@ -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">
|
||||
import { useBoolean } from '@/hooks'
|
||||
import { useRouteStore } from '@/store'
|
||||
@ -140,78 +215,3 @@ function handleMouseEnter(index: number) {
|
||||
selectedIndex.value = index
|
||||
}
|
||||
</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>
|
||||
|
||||
@ -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>
|
||||
<div
|
||||
class="logo-container h-60px cursor-pointer p-x-3"
|
||||
@ -26,6 +17,15 @@ const name = import.meta.env.VITE_APP_NAME
|
||||
</div>
|
||||
</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>
|
||||
.logo-container {
|
||||
display: flex;
|
||||
|
||||
@ -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">
|
||||
import type { MenuInst } from 'naive-ui'
|
||||
import { useAppStore, useRouteStore } from '@/store'
|
||||
@ -15,14 +26,3 @@ watch(
|
||||
{ immediate: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-menu
|
||||
ref="menuInstRef"
|
||||
:collapsed="appStore.collapsed"
|
||||
:indent="20"
|
||||
:collapsed-width="64"
|
||||
:options="routeStore.menus"
|
||||
:value="routeStore.activeMenu"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -1,9 +1,3 @@
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-tooltip placement="bottom" trigger="hover">
|
||||
<template #trigger>
|
||||
@ -15,3 +9,9 @@ const appStore = useAppStore()
|
||||
<span>{{ $t('app.togglContentFullScreen') }}</span>
|
||||
</n-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
@ -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">
|
||||
import { useTabStore } from '@/store'
|
||||
import { renderIcon } from '@/utils'
|
||||
@ -20,22 +36,6 @@ function handleDropTabs(key: string, option: any) {
|
||||
}
|
||||
</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>
|
||||
|
||||
@ -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">
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
@ -14,15 +25,4 @@ function handleReload() {
|
||||
}
|
||||
</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>
|
||||
|
||||
@ -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">
|
||||
import type { RouteLocationNormalized } from 'vue-router'
|
||||
import { useAppStore, useTabStore } from '@/store'
|
||||
@ -104,41 +139,6 @@ function onClickoutside() {
|
||||
}
|
||||
</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>
|
||||
.ghost {
|
||||
opacity: 0.5;
|
||||
|
||||
@ -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>
|
||||
<n-el
|
||||
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>
|
||||
</n-el>
|
||||
</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>
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
<template>
|
||||
<SettingDrawer />
|
||||
<component :is="layoutMap[appStore.layoutMode]" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store/app'
|
||||
import { SettingDrawer } from './components'
|
||||
@ -12,8 +17,3 @@ const layoutMap = {
|
||||
mixMenu,
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SettingDrawer />
|
||||
<component :is="layoutMap[appStore.layoutMode]" />
|
||||
</template>
|
||||
|
||||
@ -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>
|
||||
<n-layout
|
||||
has-sider
|
||||
@ -99,3 +80,22 @@ const appStore = useAppStore()
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</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>
|
||||
|
||||
@ -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>
|
||||
<n-layout
|
||||
has-sider
|
||||
@ -157,3 +90,70 @@ function updateTopMenu(key: string) {
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</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>
|
||||
|
||||
@ -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>
|
||||
<n-layout class="wh-full" embedded>
|
||||
<n-layout
|
||||
@ -64,3 +47,20 @@ const appStore = useAppStore()
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user