mirror of
https://github.com/chansee97/nova-admin.git
synced 2025-04-05 04:22:49 +08:00
feat: add multi layout
This commit is contained in:
parent
3d29e0df5b
commit
4d27fe76d6
@ -52,7 +52,10 @@
|
|||||||
"closeLeft": "Close left",
|
"closeLeft": "Close left",
|
||||||
"closeRight": "Close right",
|
"closeRight": "Close right",
|
||||||
"backHome": "Back to the homepage",
|
"backHome": "Back to the homepage",
|
||||||
"getRouteError": "Failed to obtain route, please try again later."
|
"getRouteError": "Failed to obtain route, please try again later.",
|
||||||
|
"layoutSetting": "Layout settings",
|
||||||
|
"leftMenu": "Left menu",
|
||||||
|
"topMenu": "Top menu"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"signInTitle": "Login",
|
"signInTitle": "Login",
|
||||||
|
@ -52,11 +52,12 @@
|
|||||||
"closeRight": "关闭右侧",
|
"closeRight": "关闭右侧",
|
||||||
"closeAll": "全部关闭",
|
"closeAll": "全部关闭",
|
||||||
"backHome": "回到首页",
|
"backHome": "回到首页",
|
||||||
"getRouteError": "获取路由失败,请稍后再试"
|
"getRouteError": "获取路由失败,请稍后再试",
|
||||||
|
"layoutSetting": "布局设置",
|
||||||
|
"leftMenu": "左侧菜单",
|
||||||
|
"topMenu": "顶部菜单"
|
||||||
},
|
},
|
||||||
"http": {
|
"http": {
|
||||||
"defaultTip": "请求错误",
|
|
||||||
"400": "请求出现语法错误",
|
"400": "请求出现语法错误",
|
||||||
"401": "用户未授权",
|
"401": "用户未授权",
|
||||||
"403": "服务器拒绝访问",
|
"403": "服务器拒绝访问",
|
||||||
@ -68,7 +69,8 @@
|
|||||||
"502": "错误网关",
|
"502": "错误网关",
|
||||||
"503": "服务不可用",
|
"503": "服务不可用",
|
||||||
"504": "网关超时",
|
"504": "网关超时",
|
||||||
"505": "http版本不支持该请求"
|
"505": "http版本不支持该请求",
|
||||||
|
"defaultTip": "请求错误"
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"iconSelector": {
|
"iconSelector": {
|
||||||
|
56
src/layouts/components/common/LayoutSelector.vue
Normal file
56
src/layouts/components/common/LayoutSelector.vue
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<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">
|
||||||
|
<template #trigger>
|
||||||
|
<n-el
|
||||||
|
:class="{
|
||||||
|
'outline outline-2': value === 'leftMenu',
|
||||||
|
}"
|
||||||
|
class="grid grid-cols-[20%_1fr] grid-rows-[20%_1fr] outline-[var(--primary-color)] hover:(outline outline-2)"
|
||||||
|
@click="value = 'leftMenu'"
|
||||||
|
>
|
||||||
|
<div class="bg-[var(--primary-color)] row-span-2" />
|
||||||
|
<div class="bg-[var(--primary-color-suppl)]" />
|
||||||
|
<div class="bg-[var(--divider-color)]" />
|
||||||
|
</n-el>
|
||||||
|
</template>
|
||||||
|
<span> {{ $t('app.leftMenu') }} </span>
|
||||||
|
</n-tooltip>
|
||||||
|
|
||||||
|
<n-tooltip placement="bottom" trigger="hover">
|
||||||
|
<template #trigger>
|
||||||
|
<n-el
|
||||||
|
:class="{
|
||||||
|
'outline outline-2': value === 'topMenu',
|
||||||
|
}"
|
||||||
|
class="grid grid-rows-[30%_1fr] outline-[var(--primary-color)] hover:(outline outline-2)"
|
||||||
|
@click="value = 'topMenu'"
|
||||||
|
>
|
||||||
|
<div class="bg-[var(--primary-color)]" />
|
||||||
|
<div class="bg-[var(--divider-color)]" />
|
||||||
|
</n-el>
|
||||||
|
</template>
|
||||||
|
<span> {{ $t('app.topMenu') }} </span>
|
||||||
|
</n-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.grid{
|
||||||
|
height: 60px;
|
||||||
|
width: 86px;
|
||||||
|
gap:0.4em;
|
||||||
|
padding: 0.4em;
|
||||||
|
box-shadow: var(--box-shadow-1);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
.grid > div{
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import LayoutSelector from '../common/LayoutSelector.vue'
|
||||||
import { useAppStore } from '@/store'
|
import { useAppStore } from '@/store'
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
@ -85,6 +86,8 @@ function resetSetting() {
|
|||||||
<n-drawer v-model:show="drawerActive" :width="360">
|
<n-drawer v-model:show="drawerActive" :width="360">
|
||||||
<n-drawer-content :title="t('app.systemSetting')" closable>
|
<n-drawer-content :title="t('app.systemSetting')" closable>
|
||||||
<n-space vertical>
|
<n-space vertical>
|
||||||
|
<n-divider>{{ $t('app.layoutSetting') }}</n-divider>
|
||||||
|
<LayoutSelector v-model:value="appStore.layoutMode" />
|
||||||
<n-divider>{{ $t('app.themeSetting') }}</n-divider>
|
<n-divider>{{ $t('app.themeSetting') }}</n-divider>
|
||||||
<n-space justify="space-between">
|
<n-space justify="space-between">
|
||||||
{{ $t('app.colorWeak') }}
|
{{ $t('app.colorWeak') }}
|
||||||
@ -97,14 +100,16 @@ function resetSetting() {
|
|||||||
<n-space align="center" justify="space-between">
|
<n-space align="center" justify="space-between">
|
||||||
{{ $t('app.themeColor') }}
|
{{ $t('app.themeColor') }}
|
||||||
<n-color-picker
|
<n-color-picker
|
||||||
v-model:value="appStore.primaryColor"
|
v-model:value="appStore.primaryColor" class="w-10em" :swatches="palette"
|
||||||
class="w-10em" :swatches="palette"
|
|
||||||
@update:value="appStore.setPrimaryColor"
|
@update:value="appStore.setPrimaryColor"
|
||||||
/>
|
/>
|
||||||
</n-space>
|
</n-space>
|
||||||
<n-space align="center" justify="space-between">
|
<n-space align="center" justify="space-between">
|
||||||
{{ $t('app.pageTransition') }}
|
{{ $t('app.pageTransition') }}
|
||||||
<n-select v-model:value="appStore.transitionAnimation" class="w-10em" :options="transitionSelectorOptions" @update:value="appStore.reloadPage" />
|
<n-select
|
||||||
|
v-model:value="appStore.transitionAnimation" class="w-10em"
|
||||||
|
:options="transitionSelectorOptions" @update:value="appStore.reloadPage"
|
||||||
|
/>
|
||||||
</n-space>
|
</n-space>
|
||||||
|
|
||||||
<n-divider>{{ $t('app.interfaceDisplay') }}</n-divider>
|
<n-divider>{{ $t('app.interfaceDisplay') }}</n-divider>
|
||||||
|
@ -9,13 +9,13 @@ const name = import.meta.env.VITE_APP_NAME
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="h-60px text-xl flex-center cursor-pointer"
|
class="h-60px text-xl flex-center cursor-pointer gap-2 p-x-2"
|
||||||
@click="router.push('/')"
|
@click="router.push('/')"
|
||||||
>
|
>
|
||||||
<svg-icons-logo class="text-1.5em" />
|
<svg-icons-logo class="text-1.5em" />
|
||||||
<span
|
<span
|
||||||
v-show="!appStore.collapsed"
|
v-show="!appStore.collapsed"
|
||||||
class="mx-3 text-ellipsis overflow-hidden whitespace-nowrap"
|
class="text-ellipsis overflow-hidden whitespace-nowrap"
|
||||||
>{{ name }}</span>
|
>{{ name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
const BasicLayout = () => import('./BasicLayout/index.vue')
|
|
||||||
|
|
||||||
export { BasicLayout }
|
|
15
src/layouts/index.vue
Normal file
15
src/layouts/index.vue
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import leftMenu from './leftMenu.layout.vue'
|
||||||
|
import topMenu from './topMenu.layout.vue'
|
||||||
|
import { useAppStore } from '@/store/app'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const layoutMap = {
|
||||||
|
leftMenu,
|
||||||
|
topMenu,
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component :is="layoutMap[appStore.layoutMode]" />
|
||||||
|
</template>
|
@ -11,7 +11,7 @@ import {
|
|||||||
Setting,
|
Setting,
|
||||||
TabBar,
|
TabBar,
|
||||||
UserCenter,
|
UserCenter,
|
||||||
} from '../components'
|
} from './components'
|
||||||
import { useAppStore, useRouteStore } from '@/store'
|
import { useAppStore, useRouteStore } from '@/store'
|
||||||
|
|
||||||
const routeStore = useRouteStore()
|
const routeStore = useRouteStore()
|
59
src/layouts/topMenu.layout.vue
Normal file
59
src/layouts/topMenu.layout.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
BackTop,
|
||||||
|
FullScreen,
|
||||||
|
Logo,
|
||||||
|
Menu,
|
||||||
|
Notices,
|
||||||
|
Search,
|
||||||
|
Setting,
|
||||||
|
TabBar,
|
||||||
|
UserCenter,
|
||||||
|
} from './components'
|
||||||
|
import { useAppStore, useRouteStore } from '@/store'
|
||||||
|
|
||||||
|
const routeStore = useRouteStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-layout class="wh-full" embedded>
|
||||||
|
<n-layout
|
||||||
|
class="h-full flex flex-col" content-style="display: flex;flex-direction: column;min-height:100%;"
|
||||||
|
embedded :native-scrollbar="false"
|
||||||
|
>
|
||||||
|
<n-layout-header bordered position="absolute" class="z-1">
|
||||||
|
<div class="h-60px flex-y-center justify-between shrink-0">
|
||||||
|
<Logo v-if="appStore.showLogo" />
|
||||||
|
<Menu mode="horizontal" responsive />
|
||||||
|
<div class="flex-y-center gap-1 h-full p-x-xl">
|
||||||
|
<Search />
|
||||||
|
<Notices />
|
||||||
|
<FullScreen />
|
||||||
|
<DarkModeSwitch />
|
||||||
|
<LangsSwitch />
|
||||||
|
<Setting />
|
||||||
|
<UserCenter />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<TabBar v-if="appStore.showTabs" class="h-45px" />
|
||||||
|
</n-layout-header>
|
||||||
|
<div class="flex-1 p-16px flex flex-col">
|
||||||
|
<div class="h-60px" />
|
||||||
|
<div v-if="appStore.showTabs" class="h-45px" />
|
||||||
|
<router-view v-slot="{ Component, route }" class="flex-1">
|
||||||
|
<transition :name="appStore.transitionAnimation" mode="out-in">
|
||||||
|
<keep-alive :include="routeStore.cacheRoutes">
|
||||||
|
<component :is="Component" v-if="appStore.loadFlag" :key="route.fullPath" />
|
||||||
|
</keep-alive>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
|
<div v-if="appStore.showFooter" class="h-40px" />
|
||||||
|
</div>
|
||||||
|
<n-layout-footer v-if="appStore.showFooter" bordered position="absolute" class="h-40px flex-center">
|
||||||
|
{{ appStore.footerText }}
|
||||||
|
</n-layout-footer>
|
||||||
|
<BackTop />
|
||||||
|
</n-layout>
|
||||||
|
</n-layout>
|
||||||
|
</template>
|
@ -6,7 +6,7 @@ import { local } from '@/utils'
|
|||||||
|
|
||||||
export const i18n = createI18n({
|
export const i18n = createI18n({
|
||||||
legacy: false,
|
legacy: false,
|
||||||
locale: local.get('lang') || 'zhCN', // 默认显示语言
|
locale: local.get('lang') || 'enUS', // 默认显示语言
|
||||||
fallbackLocale: 'enUS',
|
fallbackLocale: 'enUS',
|
||||||
messages: {
|
messages: {
|
||||||
zhCN,
|
zhCN,
|
||||||
|
@ -6,7 +6,7 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
path: '/',
|
path: '/',
|
||||||
name: 'root',
|
name: 'root',
|
||||||
redirect: '/appRoot',
|
redirect: '/appRoot',
|
||||||
component: () => import('@/layouts/index'),
|
// component: () => import('@/layouts/index'),
|
||||||
children: [
|
children: [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,7 @@ import themeConfig from './theme.json'
|
|||||||
import { local, setLocale } from '@/utils'
|
import { local, setLocale } from '@/utils'
|
||||||
|
|
||||||
type TransitionAnimation = '' | 'fade-slide' | 'fade-bottom' | 'fade-scale' | 'zoom-fade' | 'zoom-out'
|
type TransitionAnimation = '' | 'fade-slide' | 'fade-bottom' | 'fade-scale' | 'zoom-fade' | 'zoom-out'
|
||||||
|
export type LayoutMode = 'leftMenu' | 'topMenu'
|
||||||
|
|
||||||
const docEle = ref(document.documentElement)
|
const docEle = ref(document.documentElement)
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ export const useAppStore = defineStore('app-store', {
|
|||||||
showBreadcrumbIcon: true,
|
showBreadcrumbIcon: true,
|
||||||
showWatermark: false,
|
showWatermark: false,
|
||||||
transitionAnimation: 'fade-slide' as TransitionAnimation,
|
transitionAnimation: 'fade-slide' as TransitionAnimation,
|
||||||
|
layoutMode: 'leftMenu' as LayoutMode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
@ -63,6 +65,7 @@ export const useAppStore = defineStore('app-store', {
|
|||||||
this.showBreadcrumbIcon = true
|
this.showBreadcrumbIcon = true
|
||||||
this.showWatermark = false
|
this.showWatermark = false
|
||||||
this.transitionAnimation = 'fade-slide'
|
this.transitionAnimation = 'fade-slide'
|
||||||
|
this.layoutMode = 'leftMenu'
|
||||||
|
|
||||||
// 重置所有配色
|
// 重置所有配色
|
||||||
this.setPrimaryColor(this.primaryColor)
|
this.setPrimaryColor(this.primaryColor)
|
||||||
|
@ -8,7 +8,7 @@ import { router } from '@/router'
|
|||||||
import { fetchUserRoutes } from '@/service'
|
import { fetchUserRoutes } from '@/service'
|
||||||
import { staticRoutes } from '@/router/routes.static'
|
import { staticRoutes } from '@/router/routes.static'
|
||||||
import { usePermission } from '@/hooks'
|
import { usePermission } from '@/hooks'
|
||||||
import { BasicLayout } from '@/layouts/index'
|
import Layout from '@/layouts/index.vue'
|
||||||
import { useAuthStore } from '@/store/auth'
|
import { useAuthStore } from '@/store/auth'
|
||||||
|
|
||||||
interface RoutesStatus {
|
interface RoutesStatus {
|
||||||
@ -121,7 +121,7 @@ export const useRouteStore = defineStore('route-store', {
|
|||||||
path: '/appRoot',
|
path: '/appRoot',
|
||||||
name: 'appRoot',
|
name: 'appRoot',
|
||||||
redirect: import.meta.env.VITE_HOME_PATH,
|
redirect: import.meta.env.VITE_HOME_PATH,
|
||||||
component: BasicLayout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
title: '',
|
title: '',
|
||||||
icon: 'icon-park-outline:home',
|
icon: 'icon-park-outline:home',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { defineConfig, presetAttributify, presetUno } from 'unocss'
|
import { defineConfig, presetAttributify, presetUno, transformerVariantGroup } from 'unocss'
|
||||||
|
|
||||||
// https://github.com/unocss/unocss
|
// https://github.com/unocss/unocss
|
||||||
|
|
||||||
@ -11,4 +11,7 @@ export default defineConfig({
|
|||||||
'flex-x-center': 'flex justify-center',
|
'flex-x-center': 'flex justify-center',
|
||||||
'flex-y-center': 'flex items-center',
|
'flex-y-center': 'flex items-center',
|
||||||
},
|
},
|
||||||
|
transformers: [
|
||||||
|
transformerVariantGroup(),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user