mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2025-05-01 06:39:01 +08:00
wip: 主题设置
This commit is contained in:
parent
c8616ebbf3
commit
4f0fbf6107
10
src/App.vue
10
src/App.vue
@ -12,7 +12,7 @@
|
||||
:locale="zhCN"
|
||||
:date-locale="dateZhCN"
|
||||
:theme="appStore.isDark ? darkTheme : undefined"
|
||||
:theme-overrides="settings.naiveThemeOverrides"
|
||||
:theme-overrides="appStore.naiveThemeOverrides"
|
||||
>
|
||||
<router-view v-if="Layout" v-slot="{ Component, route: curRoute }">
|
||||
<component :is="Layout">
|
||||
@ -20,16 +20,18 @@
|
||||
<component :is="Component" v-if="!tabStore.reloading" :key="curRoute.fullPath" />
|
||||
</KeepAlive>
|
||||
</component>
|
||||
|
||||
<ThemeSetting class="fixed bottom-12 right-12" />
|
||||
</router-view>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { zhCN, dateZhCN, darkTheme } from 'naive-ui'
|
||||
import { ThemeSetting } from '@/components'
|
||||
import { useCssVar } from '@vueuse/core'
|
||||
import { kebabCase } from 'lodash-es'
|
||||
import { useAppStore, useTabStore } from '@/store'
|
||||
import settings from '@/settings'
|
||||
|
||||
const layouts = new Map()
|
||||
function getLayout(name) {
|
||||
@ -44,11 +46,11 @@ const route = useRoute()
|
||||
const appStore = useAppStore()
|
||||
const Layout = computed(() => {
|
||||
if (!route.matched?.length) return null
|
||||
return getLayout(route.meta?.layout || appStore.layout || settings.defaultLayout)
|
||||
return getLayout(route.meta?.layout || appStore.layout)
|
||||
})
|
||||
|
||||
function setupCssVar() {
|
||||
const common = settings.naiveThemeOverrides?.common || {}
|
||||
const common = appStore.naiveThemeOverrides?.common || {}
|
||||
for (const key in common) {
|
||||
useCssVar(`--${kebabCase(key)}`, document.documentElement).value = common[key] || ''
|
||||
if (key === 'primaryColor') window.localStorage.setItem('__THEME_COLOR__', common[key] || '')
|
||||
|
21
src/assets/icons/isme/theme.svg
Normal file
21
src/assets/icons/isme/theme.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<svg
|
||||
t="1702480351321"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="11122"
|
||||
width="200"
|
||||
height="200"
|
||||
>
|
||||
<path
|
||||
d="M509.9008 519.8336m-450.816 0a450.816 450.816 0 1 0 901.632 0 450.816 450.816 0 1 0-901.632 0Z"
|
||||
fill="#C65EDB"
|
||||
p-id="11123"
|
||||
></path>
|
||||
<path
|
||||
d="M798.1568 512.512l-113.3056-78.3872a47.4112 47.4112 0 0 1-20.4288-39.0656l0.3584-137.7792c0.1024-39.2704-44.9024-61.5936-76.0832-37.7856l-109.568 83.5072a47.2832 47.2832 0 0 1-43.4688 7.3216l-130.9184-42.9056c-37.3248-12.2368-72.448 23.6544-59.4432 60.7232l45.568 129.9968A47.3088 47.3088 0 0 1 284.416 501.76l-81.2544 111.2576c-23.1424 31.6928 0.1024 76.2368 39.3728 75.3152l137.728-3.1744a47.3344 47.3344 0 0 1 39.4752 19.6096l80.6912 111.6672c22.9888 31.8464 72.4992 23.4496 83.7632-14.1824l37.9392-126.6176 126.5664 118.272a27.648 27.648 0 0 0 17.7664 7.4752c7.8848 0.3584 15.872-2.6112 21.7088-8.8064a27.91936 27.91936 0 0 0-1.3312-39.4752l-124.8768-116.6848 123.8016-39.8848c37.376-11.9808 44.6976-61.6448 12.3904-84.0192z m-389.6832-6.5024l-42.8032 77.824a27.86816 27.86816 0 0 1-37.9392 11.008 27.93984 27.93984 0 0 1-11.008-37.9392l40.0384-72.7552-19.0464-63.6928c-4.4032-14.7968 3.9936-30.3616 18.7392-34.7648 14.7456-4.4032 30.3616 3.9936 34.7648 18.7392l20.6848 69.2224c3.2256 10.752 1.9968 22.528-3.4304 32.3584z"
|
||||
fill="#FFFFFF"
|
||||
p-id="11124"
|
||||
></path>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
38
src/components/common/ThemeSetting.vue
Normal file
38
src/components/common/ThemeSetting.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-popover trigger="click">
|
||||
<template #trigger>
|
||||
<i class="i-me-theme cursor-pointer text-40" />
|
||||
</template>
|
||||
<div class="h-600 w-260">
|
||||
<h3 class="font-normal">主题设置</h3>
|
||||
|
||||
<n-divider>布局</n-divider>
|
||||
<ul class="h-32 flex items-center justify-between">
|
||||
<li
|
||||
v-for="(item, index) in layouts"
|
||||
:key="index"
|
||||
class="cursor-pointer opacity-70"
|
||||
@click="appStore.setLayout(item.value)"
|
||||
>
|
||||
{{ item.desc }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<n-divider>主题色</n-divider>
|
||||
</div>
|
||||
</n-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useAppStore } from '@/store'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const layouts = [
|
||||
{ value: 'simple', desc: '简约' },
|
||||
{ value: 'normal', desc: '通用' },
|
||||
{ value: 'full', desc: '全面' },
|
||||
{ value: 'empty', desc: '空白' },
|
||||
]
|
||||
</script>
|
@ -2,3 +2,4 @@ export { default as AppCard } from './AppCard.vue'
|
||||
export { default as TheFooter } from './TheFooter.vue'
|
||||
export { default as AppPage } from './AppPage.vue'
|
||||
export { default as CommonPage } from './CommonPage.vue'
|
||||
export { default as ThemeSetting } from './ThemeSetting.vue'
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:24:19
|
||||
- @LastEditTime: 2023/12/13 20:54:55
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
@ -17,7 +17,7 @@
|
||||
</aside>
|
||||
|
||||
<article class="w-0 flex-col flex-1">
|
||||
<AppHeader :class="`h-${header.height}`" class="flex-shrink-0" />
|
||||
<AppHeader class="h-60 flex-shrink-0" />
|
||||
<slot />
|
||||
</article>
|
||||
</div>
|
||||
@ -28,9 +28,6 @@ import { useAppStore } from '@/store'
|
||||
import SideBar from './sidebar/index.vue'
|
||||
import AppHeader from './header/index.vue'
|
||||
|
||||
import settings from '@/settings'
|
||||
const { header } = settings
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
83
src/layouts/normal/header/components/UserAvatar.vue
Normal file
83
src/layouts/normal/header/components/UserAvatar.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:46
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<n-dropdown :options="options" @select="handleSelect">
|
||||
<div class="flex cursor-pointer items-center">
|
||||
<n-avatar round :size="36" :src="userStore.avatar" class="mr-12" />
|
||||
<div v-if="userStore.userInfo" class="flex-col items-center">
|
||||
<span class="text-14">{{ userStore.nickName ?? userStore.username }}</span>
|
||||
<span class="text-12 opacity-50">[{{ userStore.currentRole?.name }}]</span>
|
||||
</div>
|
||||
</div>
|
||||
</n-dropdown>
|
||||
|
||||
<RoleSelect ref="roleSelectRef" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useUserStore, useAuthStore, usePermissionStore } from '@/store'
|
||||
import { RoleSelect } from '@/layouts/components'
|
||||
import { initUserAndPermissions } from '@/router'
|
||||
import api from '@/api'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const authStore = useAuthStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const options = reactive([
|
||||
{
|
||||
label: '个人资料',
|
||||
key: 'profile',
|
||||
icon: () => h('i', { class: 'i-material-symbols:person-outline text-14' }),
|
||||
show: computed(() => permissionStore.accessRoutes?.some((item) => item.path === '/profile')),
|
||||
},
|
||||
{
|
||||
label: '切换角色',
|
||||
key: 'toggleRole',
|
||||
icon: () => h('i', { class: 'i-basil:exchange-solid text-14' }),
|
||||
show: computed(() => userStore.roles.length > 1),
|
||||
},
|
||||
{
|
||||
label: '退出登录',
|
||||
key: 'logout',
|
||||
icon: () => h('i', { class: 'i-mdi:exit-to-app text-14' }),
|
||||
},
|
||||
])
|
||||
|
||||
const roleSelectRef = ref(null)
|
||||
function handleSelect(key) {
|
||||
switch (key) {
|
||||
case 'profile':
|
||||
router.push('/profile')
|
||||
break
|
||||
case 'toggleRole':
|
||||
roleSelectRef.value?.open({
|
||||
onOk() {
|
||||
initUserAndPermissions().then(() => {
|
||||
router.replace('/')
|
||||
})
|
||||
},
|
||||
})
|
||||
break
|
||||
case 'logout':
|
||||
$dialog.confirm({
|
||||
title: '提示',
|
||||
type: 'info',
|
||||
content: '确认退出?',
|
||||
async confirm() {
|
||||
await api.logout()
|
||||
authStore.logout()
|
||||
$message.success('已退出登录')
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/layouts/normal/header/components/index.js
Normal file
2
src/layouts/normal/header/components/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as UserAvatar } from './UserAvatar.vue'
|
||||
export { default as AppTab } from './tab/index.vue'
|
125
src/layouts/normal/header/components/tab/ContextMenu.vue
Normal file
125
src/layouts/normal/header/components/tab/ContextMenu.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:32
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<n-dropdown
|
||||
:show="show"
|
||||
:options="options"
|
||||
:x="x"
|
||||
:y="y"
|
||||
placement="bottom-start"
|
||||
@clickoutside="handleHideDropdown"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useTabStore } from '@/store'
|
||||
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
currentPath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
x: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
y: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:show'])
|
||||
|
||||
const tabStore = useTabStore()
|
||||
|
||||
const options = computed(() => [
|
||||
{
|
||||
label: '重新加载',
|
||||
key: 'reload',
|
||||
disabled: props.currentPath !== tabStore.activeTab,
|
||||
icon: () => h('i', { class: 'i-mdi:refresh text-14' }),
|
||||
},
|
||||
{
|
||||
label: '关闭',
|
||||
key: 'close',
|
||||
disabled: tabStore.tabs.length <= 1,
|
||||
icon: () => h('i', { class: 'i-mdi:close text-14' }),
|
||||
},
|
||||
{
|
||||
label: '关闭其他',
|
||||
key: 'close-other',
|
||||
disabled: tabStore.tabs.length <= 1,
|
||||
icon: () => h('i', { class: 'i-mdi:arrow-expand-horizontal text-14' }),
|
||||
},
|
||||
{
|
||||
label: '关闭左侧',
|
||||
key: 'close-left',
|
||||
disabled: tabStore.tabs.length <= 1 || props.currentPath === tabStore.tabs[0].path,
|
||||
icon: () => h('i', { class: 'i-mdi:arrow-expand-left text-14' }),
|
||||
},
|
||||
{
|
||||
label: '关闭右侧',
|
||||
key: 'close-right',
|
||||
disabled:
|
||||
tabStore.tabs.length <= 1 ||
|
||||
props.currentPath === tabStore.tabs[tabStore.tabs.length - 1].path,
|
||||
icon: () => h('i', { class: 'i-mdi:arrow-expand-right text-14' }),
|
||||
},
|
||||
])
|
||||
|
||||
const route = useRoute()
|
||||
const actionMap = new Map([
|
||||
[
|
||||
'reload',
|
||||
() => {
|
||||
tabStore.reloadTab(route.fullPath, route.meta?.keepAlive)
|
||||
},
|
||||
],
|
||||
[
|
||||
'close',
|
||||
() => {
|
||||
tabStore.removeTab(props.currentPath)
|
||||
},
|
||||
],
|
||||
[
|
||||
'close-other',
|
||||
() => {
|
||||
tabStore.removeOther(props.currentPath)
|
||||
},
|
||||
],
|
||||
[
|
||||
'close-left',
|
||||
() => {
|
||||
tabStore.removeLeft(props.currentPath)
|
||||
},
|
||||
],
|
||||
[
|
||||
'close-right',
|
||||
() => {
|
||||
tabStore.removeRight(props.currentPath)
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
function handleHideDropdown() {
|
||||
emit('update:show', false)
|
||||
}
|
||||
|
||||
function handleSelect(key) {
|
||||
const actionFn = actionMap.get(key)
|
||||
actionFn && actionFn()
|
||||
handleHideDropdown()
|
||||
}
|
||||
</script>
|
101
src/layouts/normal/header/components/tab/index.vue
Normal file
101
src/layouts/normal/header/components/tab/index.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:38
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-tabs
|
||||
:value="tabStore.activeTab"
|
||||
:closable="tabStore.tabs.length > 1"
|
||||
:style="`--selected-bg: ${appStore.isDark ? '#1b2429' : '#eaf0f1'}`"
|
||||
type="card"
|
||||
@close="(path) => tabStore.removeTab(path)"
|
||||
>
|
||||
<n-tab
|
||||
v-for="item in tabStore.tabs"
|
||||
:key="item.path"
|
||||
:name="item.path"
|
||||
@click="handleItemClick(item.path)"
|
||||
@contextmenu.prevent="handleContextMenu($event, item)"
|
||||
>
|
||||
{{ item.title }}
|
||||
</n-tab>
|
||||
</n-tabs>
|
||||
|
||||
<ContextMenu
|
||||
v-if="contextMenuOption.show"
|
||||
v-model:show="contextMenuOption.show"
|
||||
:current-path="contextMenuOption.currentPath"
|
||||
:x="contextMenuOption.x"
|
||||
:y="contextMenuOption.y"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ContextMenu from './ContextMenu.vue'
|
||||
import { useTabStore, useAppStore } from '@/store'
|
||||
|
||||
const router = useRouter()
|
||||
const appStore = useAppStore()
|
||||
const tabStore = useTabStore()
|
||||
|
||||
const contextMenuOption = reactive({
|
||||
show: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
currentPath: '',
|
||||
})
|
||||
|
||||
const handleItemClick = (path) => {
|
||||
tabStore.setActiveTab(path)
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function showContextMenu() {
|
||||
contextMenuOption.show = true
|
||||
}
|
||||
function hideContextMenu() {
|
||||
contextMenuOption.show = false
|
||||
}
|
||||
function setContextMenu(x, y, currentPath) {
|
||||
Object.assign(contextMenuOption, { x, y, currentPath })
|
||||
}
|
||||
|
||||
// 右击菜单
|
||||
async function handleContextMenu(e, tagItem) {
|
||||
const { clientX, clientY } = e
|
||||
hideContextMenu()
|
||||
setContextMenu(clientX, clientY, tagItem.path)
|
||||
await nextTick()
|
||||
showContextMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.n-tabs) {
|
||||
.n-tabs-tab {
|
||||
padding-left: 16px;
|
||||
height: 36px;
|
||||
background: transparent !important;
|
||||
border-radius: 4px !important;
|
||||
margin-right: 4px;
|
||||
&:hover {
|
||||
border: 1px solid var(--primary-color) !important;
|
||||
}
|
||||
}
|
||||
.n-tabs-tab--active {
|
||||
border: 1px solid var(--primary-color) !important;
|
||||
background-color: var(--selected-bg) !important;
|
||||
}
|
||||
.n-tabs-pad,
|
||||
.n-tabs-tab-pad,
|
||||
.n-tabs-scroll-padding {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
64
src/layouts/normal/header/index.vue
Normal file
64
src/layouts/normal/header/index.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:23
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<AppCard class="flex items-center px-12" border-b="1px solid light_border dark:dark_border">
|
||||
<div
|
||||
class="f-c-c cursor-pointer rounded-4 p-6 text-22 transition-all-300 auto-bg-hover"
|
||||
@click="appStore.switchCollapsed"
|
||||
>
|
||||
<i :class="appStore.collapsed ? 'i-line-md-menu-unfold-left' : 'i-line-md-menu-fold-left'" />
|
||||
</div>
|
||||
|
||||
<AppTab class="w-0 flex-1 px-12" />
|
||||
|
||||
<span class="mx-6 opacity-20">|</span>
|
||||
|
||||
<div class="flex flex-shrink-0 items-center px-12 text-18">
|
||||
<i
|
||||
class="mr-16 cursor-pointer"
|
||||
:class="isDark ? 'i-fe:moon' : 'i-fe:sun'"
|
||||
@click="toggleDark"
|
||||
/>
|
||||
<i
|
||||
class="mr-16 cursor-pointer"
|
||||
:class="isFullscreen ? 'i-fe:minimize' : 'i-fe:maximize'"
|
||||
@click="toggle"
|
||||
/>
|
||||
|
||||
<i
|
||||
class="i-fe:github mr-16 cursor-pointer"
|
||||
@click="handleLinkClick('https://github.com/zclzone/vue-naive-admin/tree/2.x-beta')"
|
||||
/>
|
||||
<i
|
||||
class="i-me:gitee mr-16 cursor-pointer"
|
||||
@click="handleLinkClick('https://gitee.com/isme-admin/vue-naive-admin/tree/2.x-beta')"
|
||||
/>
|
||||
<UserAvatar />
|
||||
</div>
|
||||
</AppCard>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { AppTab, UserAvatar } from './components'
|
||||
import { useAppStore } from '@/store'
|
||||
import { useDark, useToggle, useFullscreen } from '@vueuse/core'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const isDark = useDark()
|
||||
const toggleDark = () => {
|
||||
appStore.toggleDark()
|
||||
useToggle(isDark)()
|
||||
}
|
||||
|
||||
const { isFullscreen, toggle } = useFullscreen()
|
||||
|
||||
function handleLinkClick(link) {
|
||||
window.open(link)
|
||||
}
|
||||
</script>
|
38
src/layouts/normal/index.vue
Normal file
38
src/layouts/normal/index.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/13 20:54:55
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<div class="wh-full flex">
|
||||
<aside
|
||||
class="flex-col flex-shrink-0 transition-width-300"
|
||||
:class="appStore.collapsed ? 'w-64' : 'w-220'"
|
||||
border-r="1px solid light_border dark:dark_border"
|
||||
>
|
||||
<SideBar />
|
||||
</aside>
|
||||
|
||||
<article class="w-0 flex-col flex-1">
|
||||
<AppHeader class="h-60 flex-shrink-0" />
|
||||
<slot />
|
||||
</article>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useAppStore } from '@/store'
|
||||
import SideBar from './sidebar/index.vue'
|
||||
import AppHeader from './header/index.vue'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.collapsed {
|
||||
width: 64px;
|
||||
}
|
||||
</style>
|
26
src/layouts/normal/sidebar/components/SideLogo.vue
Normal file
26
src/layouts/normal/sidebar/components/SideLogo.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:55
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<router-link class="h-60 f-c-c" to="/">
|
||||
<img src="@/assets/images/logo.png" class="h-40" />
|
||||
<h2
|
||||
v-show="!appStore.collapsed"
|
||||
class="ml-10 max-w-140 flex-shrink-0 text-16 font-bold color-primary"
|
||||
>
|
||||
{{ title }}
|
||||
</h2>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useAppStore } from '@/store'
|
||||
const title = import.meta.env.VITE_TITLE
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
62
src/layouts/normal/sidebar/components/SideMenu.vue
Normal file
62
src/layouts/normal/sidebar/components/SideMenu.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:24:02
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<n-menu
|
||||
ref="menu"
|
||||
class="side-menu"
|
||||
accordion
|
||||
:indent="18"
|
||||
:collapsed-icon-size="22"
|
||||
:collapsed-width="64"
|
||||
:collapsed="appStore.collapsed"
|
||||
:options="permissionStore.menus"
|
||||
:value="activeKey"
|
||||
@update:value="handleMenuSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useAppStore, usePermissionStore } from '@/store'
|
||||
import { isExternal } from '@/utils'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const appStore = useAppStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const activeKey = computed(() => route.meta?.parentKey || route.name)
|
||||
|
||||
const menu = ref(null)
|
||||
watch(route, async () => {
|
||||
await nextTick()
|
||||
menu.value?.showOption()
|
||||
})
|
||||
|
||||
function handleMenuSelect(key, item) {
|
||||
if (isExternal(item.path)) {
|
||||
window.open(item.path)
|
||||
} else {
|
||||
router.push(item.path)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.side-menu:not(.n-menu--collapsed) {
|
||||
.n-menu-item-content {
|
||||
&::before {
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
&.n-menu-item-content--selected::before {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
17
src/layouts/normal/sidebar/index.vue
Normal file
17
src/layouts/normal/sidebar/index.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:24:09
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<SideLogo border-b="1px solid light_border dark:dark_border" />
|
||||
<SideMenu class="cus-scroll-y mt-4 h-0 flex-1" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import SideLogo from './components/SideLogo.vue'
|
||||
import SideMenu from './components/SideMenu.vue'
|
||||
</script>
|
83
src/layouts/simple/header/components/UserAvatar.vue
Normal file
83
src/layouts/simple/header/components/UserAvatar.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:46
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<n-dropdown :options="options" @select="handleSelect">
|
||||
<div class="flex cursor-pointer items-center">
|
||||
<n-avatar round :size="36" :src="userStore.avatar" class="mr-12" />
|
||||
<div v-if="userStore.userInfo" class="flex-col items-center">
|
||||
<span class="text-14">{{ userStore.nickName ?? userStore.username }}</span>
|
||||
<span class="text-12 opacity-50">[{{ userStore.currentRole?.name }}]</span>
|
||||
</div>
|
||||
</div>
|
||||
</n-dropdown>
|
||||
|
||||
<RoleSelect ref="roleSelectRef" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useUserStore, useAuthStore, usePermissionStore } from '@/store'
|
||||
import { RoleSelect } from '@/layouts/components'
|
||||
import { initUserAndPermissions } from '@/router'
|
||||
import api from '@/api'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const authStore = useAuthStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const options = reactive([
|
||||
{
|
||||
label: '个人资料',
|
||||
key: 'profile',
|
||||
icon: () => h('i', { class: 'i-material-symbols:person-outline text-14' }),
|
||||
show: computed(() => permissionStore.accessRoutes?.some((item) => item.path === '/profile')),
|
||||
},
|
||||
{
|
||||
label: '切换角色',
|
||||
key: 'toggleRole',
|
||||
icon: () => h('i', { class: 'i-basil:exchange-solid text-14' }),
|
||||
show: computed(() => userStore.roles.length > 1),
|
||||
},
|
||||
{
|
||||
label: '退出登录',
|
||||
key: 'logout',
|
||||
icon: () => h('i', { class: 'i-mdi:exit-to-app text-14' }),
|
||||
},
|
||||
])
|
||||
|
||||
const roleSelectRef = ref(null)
|
||||
function handleSelect(key) {
|
||||
switch (key) {
|
||||
case 'profile':
|
||||
router.push('/profile')
|
||||
break
|
||||
case 'toggleRole':
|
||||
roleSelectRef.value?.open({
|
||||
onOk() {
|
||||
initUserAndPermissions().then(() => {
|
||||
router.replace('/')
|
||||
})
|
||||
},
|
||||
})
|
||||
break
|
||||
case 'logout':
|
||||
$dialog.confirm({
|
||||
title: '提示',
|
||||
type: 'info',
|
||||
content: '确认退出?',
|
||||
async confirm() {
|
||||
await api.logout()
|
||||
authStore.logout()
|
||||
$message.success('已退出登录')
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/layouts/simple/header/components/index.js
Normal file
2
src/layouts/simple/header/components/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as UserAvatar } from './UserAvatar.vue'
|
||||
export { default as AppTab } from './tab/index.vue'
|
125
src/layouts/simple/header/components/tab/ContextMenu.vue
Normal file
125
src/layouts/simple/header/components/tab/ContextMenu.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:32
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<n-dropdown
|
||||
:show="show"
|
||||
:options="options"
|
||||
:x="x"
|
||||
:y="y"
|
||||
placement="bottom-start"
|
||||
@clickoutside="handleHideDropdown"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useTabStore } from '@/store'
|
||||
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
currentPath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
x: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
y: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:show'])
|
||||
|
||||
const tabStore = useTabStore()
|
||||
|
||||
const options = computed(() => [
|
||||
{
|
||||
label: '重新加载',
|
||||
key: 'reload',
|
||||
disabled: props.currentPath !== tabStore.activeTab,
|
||||
icon: () => h('i', { class: 'i-mdi:refresh text-14' }),
|
||||
},
|
||||
{
|
||||
label: '关闭',
|
||||
key: 'close',
|
||||
disabled: tabStore.tabs.length <= 1,
|
||||
icon: () => h('i', { class: 'i-mdi:close text-14' }),
|
||||
},
|
||||
{
|
||||
label: '关闭其他',
|
||||
key: 'close-other',
|
||||
disabled: tabStore.tabs.length <= 1,
|
||||
icon: () => h('i', { class: 'i-mdi:arrow-expand-horizontal text-14' }),
|
||||
},
|
||||
{
|
||||
label: '关闭左侧',
|
||||
key: 'close-left',
|
||||
disabled: tabStore.tabs.length <= 1 || props.currentPath === tabStore.tabs[0].path,
|
||||
icon: () => h('i', { class: 'i-mdi:arrow-expand-left text-14' }),
|
||||
},
|
||||
{
|
||||
label: '关闭右侧',
|
||||
key: 'close-right',
|
||||
disabled:
|
||||
tabStore.tabs.length <= 1 ||
|
||||
props.currentPath === tabStore.tabs[tabStore.tabs.length - 1].path,
|
||||
icon: () => h('i', { class: 'i-mdi:arrow-expand-right text-14' }),
|
||||
},
|
||||
])
|
||||
|
||||
const route = useRoute()
|
||||
const actionMap = new Map([
|
||||
[
|
||||
'reload',
|
||||
() => {
|
||||
tabStore.reloadTab(route.fullPath, route.meta?.keepAlive)
|
||||
},
|
||||
],
|
||||
[
|
||||
'close',
|
||||
() => {
|
||||
tabStore.removeTab(props.currentPath)
|
||||
},
|
||||
],
|
||||
[
|
||||
'close-other',
|
||||
() => {
|
||||
tabStore.removeOther(props.currentPath)
|
||||
},
|
||||
],
|
||||
[
|
||||
'close-left',
|
||||
() => {
|
||||
tabStore.removeLeft(props.currentPath)
|
||||
},
|
||||
],
|
||||
[
|
||||
'close-right',
|
||||
() => {
|
||||
tabStore.removeRight(props.currentPath)
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
function handleHideDropdown() {
|
||||
emit('update:show', false)
|
||||
}
|
||||
|
||||
function handleSelect(key) {
|
||||
const actionFn = actionMap.get(key)
|
||||
actionFn && actionFn()
|
||||
handleHideDropdown()
|
||||
}
|
||||
</script>
|
101
src/layouts/simple/header/components/tab/index.vue
Normal file
101
src/layouts/simple/header/components/tab/index.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:38
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-tabs
|
||||
:value="tabStore.activeTab"
|
||||
:closable="tabStore.tabs.length > 1"
|
||||
:style="`--selected-bg: ${appStore.isDark ? '#1b2429' : '#eaf0f1'}`"
|
||||
type="card"
|
||||
@close="(path) => tabStore.removeTab(path)"
|
||||
>
|
||||
<n-tab
|
||||
v-for="item in tabStore.tabs"
|
||||
:key="item.path"
|
||||
:name="item.path"
|
||||
@click="handleItemClick(item.path)"
|
||||
@contextmenu.prevent="handleContextMenu($event, item)"
|
||||
>
|
||||
{{ item.title }}
|
||||
</n-tab>
|
||||
</n-tabs>
|
||||
|
||||
<ContextMenu
|
||||
v-if="contextMenuOption.show"
|
||||
v-model:show="contextMenuOption.show"
|
||||
:current-path="contextMenuOption.currentPath"
|
||||
:x="contextMenuOption.x"
|
||||
:y="contextMenuOption.y"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ContextMenu from './ContextMenu.vue'
|
||||
import { useTabStore, useAppStore } from '@/store'
|
||||
|
||||
const router = useRouter()
|
||||
const appStore = useAppStore()
|
||||
const tabStore = useTabStore()
|
||||
|
||||
const contextMenuOption = reactive({
|
||||
show: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
currentPath: '',
|
||||
})
|
||||
|
||||
const handleItemClick = (path) => {
|
||||
tabStore.setActiveTab(path)
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
function showContextMenu() {
|
||||
contextMenuOption.show = true
|
||||
}
|
||||
function hideContextMenu() {
|
||||
contextMenuOption.show = false
|
||||
}
|
||||
function setContextMenu(x, y, currentPath) {
|
||||
Object.assign(contextMenuOption, { x, y, currentPath })
|
||||
}
|
||||
|
||||
// 右击菜单
|
||||
async function handleContextMenu(e, tagItem) {
|
||||
const { clientX, clientY } = e
|
||||
hideContextMenu()
|
||||
setContextMenu(clientX, clientY, tagItem.path)
|
||||
await nextTick()
|
||||
showContextMenu()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.n-tabs) {
|
||||
.n-tabs-tab {
|
||||
padding-left: 16px;
|
||||
height: 36px;
|
||||
background: transparent !important;
|
||||
border-radius: 4px !important;
|
||||
margin-right: 4px;
|
||||
&:hover {
|
||||
border: 1px solid var(--primary-color) !important;
|
||||
}
|
||||
}
|
||||
.n-tabs-tab--active {
|
||||
border: 1px solid var(--primary-color) !important;
|
||||
background-color: var(--selected-bg) !important;
|
||||
}
|
||||
.n-tabs-pad,
|
||||
.n-tabs-tab-pad,
|
||||
.n-tabs-scroll-padding {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
64
src/layouts/simple/header/index.vue
Normal file
64
src/layouts/simple/header/index.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:23
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<AppCard class="flex items-center px-12" border-b="1px solid light_border dark:dark_border">
|
||||
<div
|
||||
class="f-c-c cursor-pointer rounded-4 p-6 text-22 transition-all-300 auto-bg-hover"
|
||||
@click="appStore.switchCollapsed"
|
||||
>
|
||||
<i :class="appStore.collapsed ? 'i-line-md-menu-unfold-left' : 'i-line-md-menu-fold-left'" />
|
||||
</div>
|
||||
|
||||
<AppTab class="w-0 flex-1 px-12" />
|
||||
|
||||
<span class="mx-6 opacity-20">|</span>
|
||||
|
||||
<div class="flex flex-shrink-0 items-center px-12 text-18">
|
||||
<i
|
||||
class="mr-16 cursor-pointer"
|
||||
:class="isDark ? 'i-fe:moon' : 'i-fe:sun'"
|
||||
@click="toggleDark"
|
||||
/>
|
||||
<i
|
||||
class="mr-16 cursor-pointer"
|
||||
:class="isFullscreen ? 'i-fe:minimize' : 'i-fe:maximize'"
|
||||
@click="toggle"
|
||||
/>
|
||||
|
||||
<i
|
||||
class="i-fe:github mr-16 cursor-pointer"
|
||||
@click="handleLinkClick('https://github.com/zclzone/vue-naive-admin/tree/2.x-beta')"
|
||||
/>
|
||||
<i
|
||||
class="i-me:gitee mr-16 cursor-pointer"
|
||||
@click="handleLinkClick('https://gitee.com/isme-admin/vue-naive-admin/tree/2.x-beta')"
|
||||
/>
|
||||
<UserAvatar />
|
||||
</div>
|
||||
</AppCard>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { AppTab, UserAvatar } from './components'
|
||||
import { useAppStore } from '@/store'
|
||||
import { useDark, useToggle, useFullscreen } from '@vueuse/core'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const isDark = useDark()
|
||||
const toggleDark = () => {
|
||||
appStore.toggleDark()
|
||||
useToggle(isDark)()
|
||||
}
|
||||
|
||||
const { isFullscreen, toggle } = useFullscreen()
|
||||
|
||||
function handleLinkClick(link) {
|
||||
window.open(link)
|
||||
}
|
||||
</script>
|
38
src/layouts/simple/index.vue
Normal file
38
src/layouts/simple/index.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/13 20:54:55
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<div class="wh-full flex">
|
||||
<aside
|
||||
class="flex-col flex-shrink-0 transition-width-300"
|
||||
:class="appStore.collapsed ? 'w-64' : 'w-220'"
|
||||
border-r="1px solid light_border dark:dark_border"
|
||||
>
|
||||
<SideBar />
|
||||
</aside>
|
||||
|
||||
<article class="w-0 flex-col flex-1">
|
||||
<AppHeader class="h-60 flex-shrink-0" />
|
||||
<slot />
|
||||
</article>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useAppStore } from '@/store'
|
||||
import SideBar from './sidebar/index.vue'
|
||||
import AppHeader from './header/index.vue'
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.collapsed {
|
||||
width: 64px;
|
||||
}
|
||||
</style>
|
26
src/layouts/simple/sidebar/components/SideLogo.vue
Normal file
26
src/layouts/simple/sidebar/components/SideLogo.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:23:55
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<router-link class="h-60 f-c-c" to="/">
|
||||
<img src="@/assets/images/logo.png" class="h-40" />
|
||||
<h2
|
||||
v-show="!appStore.collapsed"
|
||||
class="ml-10 max-w-140 flex-shrink-0 text-16 font-bold color-primary"
|
||||
>
|
||||
{{ title }}
|
||||
</h2>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useAppStore } from '@/store'
|
||||
const title = import.meta.env.VITE_TITLE
|
||||
|
||||
const appStore = useAppStore()
|
||||
</script>
|
62
src/layouts/simple/sidebar/components/SideMenu.vue
Normal file
62
src/layouts/simple/sidebar/components/SideMenu.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:24:02
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<n-menu
|
||||
ref="menu"
|
||||
class="side-menu"
|
||||
accordion
|
||||
:indent="18"
|
||||
:collapsed-icon-size="22"
|
||||
:collapsed-width="64"
|
||||
:collapsed="appStore.collapsed"
|
||||
:options="permissionStore.menus"
|
||||
:value="activeKey"
|
||||
@update:value="handleMenuSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useAppStore, usePermissionStore } from '@/store'
|
||||
import { isExternal } from '@/utils'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const appStore = useAppStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const activeKey = computed(() => route.meta?.parentKey || route.name)
|
||||
|
||||
const menu = ref(null)
|
||||
watch(route, async () => {
|
||||
await nextTick()
|
||||
menu.value?.showOption()
|
||||
})
|
||||
|
||||
function handleMenuSelect(key, item) {
|
||||
if (isExternal(item.path)) {
|
||||
window.open(item.path)
|
||||
} else {
|
||||
router.push(item.path)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.side-menu:not(.n-menu--collapsed) {
|
||||
.n-menu-item-content {
|
||||
&::before {
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
&.n-menu-item-content--selected::before {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
17
src/layouts/simple/sidebar/index.vue
Normal file
17
src/layouts/simple/sidebar/index.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:24:09
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<script setup>
|
||||
import SideLogo from './components/SideLogo.vue'
|
||||
import SideMenu from './components/SideMenu.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SideLogo border-b="1px solid light_border dark:dark_border" />
|
||||
<SideMenu class="cus-scroll-y mt-4 h-0 flex-1" />
|
||||
</template>
|
@ -1,21 +1,14 @@
|
||||
/**********************************
|
||||
* @Author: Ronnie Zhang
|
||||
* @LastEditor: Ronnie Zhang
|
||||
* @LastEditTime: 2023/12/05 21:30:24
|
||||
* @LastEditTime: 2023/12/13 20:54:36
|
||||
* @Email: zclzone@outlook.com
|
||||
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
**********************************/
|
||||
|
||||
export default {
|
||||
defaultLayout: 'default',
|
||||
header: {
|
||||
height: 60,
|
||||
},
|
||||
tab: {
|
||||
visible: true,
|
||||
height: 50,
|
||||
},
|
||||
naiveThemeOverrides: {
|
||||
export const defaultLayout = 'normal'
|
||||
|
||||
export const naiveThemeOverrides = {
|
||||
common: {
|
||||
primaryColor: '#316C72FF',
|
||||
primaryColorHover: '#316C72E3',
|
||||
@ -42,7 +35,6 @@ export default {
|
||||
errorColorPressed: '#AB1F3FFF',
|
||||
errorColorSuppl: '#DE576DFF',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const basePermissions = [
|
||||
|
@ -8,13 +8,14 @@
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { useDark } from '@vueuse/core'
|
||||
import settings from '@/settings'
|
||||
import { defaultLayout, naiveThemeOverrides } from '@/settings'
|
||||
|
||||
export const useAppStore = defineStore('app', {
|
||||
state: () => ({
|
||||
collapsed: false,
|
||||
isDark: useDark(),
|
||||
layout: settings.defaultLayout,
|
||||
layout: defaultLayout,
|
||||
naiveThemeOverrides,
|
||||
}),
|
||||
actions: {
|
||||
switchCollapsed() {
|
||||
@ -26,12 +27,12 @@ export const useAppStore = defineStore('app', {
|
||||
toggleDark() {
|
||||
this.isDark = !this.isDark
|
||||
},
|
||||
setDeaultLayout(v) {
|
||||
setLayout(v) {
|
||||
this.layout = v
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
paths: ['layout', 'collapsed'],
|
||||
paths: ['layout', 'collapsed', 'naiveThemeOverrides'],
|
||||
storage: localStorage,
|
||||
},
|
||||
})
|
||||
|
@ -64,7 +64,7 @@ export const usePermissionStore = defineStore('permission', {
|
||||
meta: {
|
||||
icon: item.icon,
|
||||
title: item.name,
|
||||
layout: item.layout || 'default',
|
||||
layout: item.layout,
|
||||
keepAlive: !!item.keepAlive,
|
||||
parentKey,
|
||||
btns: item.children
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
import * as NaiveUI from 'naive-ui'
|
||||
import { isNullOrUndef } from '@/utils'
|
||||
import settings from '@/settings'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
export function setupMessage(NMessage) {
|
||||
@ -104,10 +103,9 @@ export function setupDialog(NDialog) {
|
||||
|
||||
export function setupNaiveDiscreteApi() {
|
||||
const appStore = useAppStore()
|
||||
const { naiveThemeOverrides: themeOverrides } = settings
|
||||
const configProviderProps = computed(() => ({
|
||||
theme: appStore.isDark ? NaiveUI.darkTheme : undefined,
|
||||
themeOverrides,
|
||||
themeOverrides: useAppStore().naiveThemeOverrides,
|
||||
}))
|
||||
const { message, dialog, notification, loadingBar } = NaiveUI.createDiscreteApi(
|
||||
['message', 'dialog', 'notification', 'loadingBar'],
|
||||
|
@ -155,7 +155,10 @@ const iconOptions = icons.map((item) => ({
|
||||
value: item,
|
||||
}))
|
||||
const layoutOptions = [
|
||||
{ label: '默认-default', value: 'default' },
|
||||
{ label: '跟随系统', value: '' },
|
||||
{ label: '简约-simple', value: 'simple' },
|
||||
{ label: '通用-normal', value: 'normal' },
|
||||
{ label: '全面-full', value: 'full' },
|
||||
{ label: '空白-empty', value: 'empty' },
|
||||
]
|
||||
const required = {
|
||||
|
@ -49,7 +49,7 @@
|
||||
<span v-else>无</span>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="layout">
|
||||
{{ currentMenu.layout ?? 'default' }}
|
||||
{{ currentMenu.layout || '跟随系统' }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="是否显示">
|
||||
{{ currentMenu.show ? '是' : '否' }}
|
||||
|
Loading…
x
Reference in New Issue
Block a user