fix: perfect menu manage

This commit is contained in:
chansee97 2024-04-13 12:26:39 +08:00
parent cd67da6b00
commit de368c7e9e
12 changed files with 71 additions and 55 deletions

2
.env
View File

@ -5,7 +5,7 @@ VITE_APP_NAME=Nova - Admin
# 路由模式 # 路由模式
VITE_ROUTE_MODE = web VITE_ROUTE_MODE = web
# 权限路由模式: static dynamic # 权限路由模式: static dynamic
VITE_AUTH_ROUTE_MODE=dynamic VITE_AUTH_ROUTE_MODE=static
# 设置登陆后跳转地址 # 设置登陆后跳转地址
VITE_HOME_PATH = /dashboard/workbench VITE_HOME_PATH = /dashboard/workbench

View File

@ -0,0 +1,16 @@
<script setup lang="ts">
interface Props {
message: string
}
const props = defineProps<Props>()
</script>
<template>
<n-tooltip :show-arrow="false" trigger="hover">
<template #trigger>
<icon-park-outline-help />
</template>
{{ props.message }}
</n-tooltip>
</template>

View File

@ -1,6 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { icons } from './icons' import { icons } from './icons'
interface Props {
disabled?: boolean
}
const props = withDefaults(defineProps<Props>(), {
disabled: false,
})
const value = defineModel('value', { type: String }) const value = defineModel('value', { type: String })
const searchValue = ref('') const searchValue = ref('')
const showPopover = ref(false) const showPopover = ref(false)
@ -16,11 +23,11 @@ function handleSelectIcon(icon: string) {
</script> </script>
<template> <template>
<n-popover v-model:show="showPopover" placement="bottom" trigger="click"> <n-popover v-model:show="showPopover" placement="bottom" trigger="click" :disabled="props.disabled">
<template #trigger> <template #trigger>
<n-input :value="value" readonly :placeholder="t('components.iconSelector.inputPlaceholder')"> <n-input :value="value" readonly :placeholder="t('components.iconSelector.inputPlaceholder')">
<template #suffix> <template #suffix>
<nova-icon :icon="value || 'icon-park-outline:all-application'" /> <nova-icon :icon="value" />
</template> </template>
</n-input> </n-input>
</template> </template>
@ -30,9 +37,7 @@ function handleSelectIcon(icon: string) {
<div class="w-400px"> <div class="w-400px">
<div v-if="iconList.length > 0" class="grid grid-cols-9 h-auto overflow-auto gap-1"> <div v-if="iconList.length > 0" class="grid grid-cols-9 h-auto overflow-auto gap-1">
<div <div
v-for="(item, index) in iconList" v-for="(item, index) in iconList" :key="index" class="border border-gray-200 m-2px p-5px flex-center"
:key="index"
class="border border-gray-200 m-2px p-5px flex-center"
@click="handleSelectIcon(item)" @click="handleSelectIcon(item)"
> >
<nova-icon :icon="item" :size="24" /> <nova-icon :icon="item" :size="24" />

View File

@ -1,31 +0,0 @@
<script lang="ts" setup>
const props = defineProps({
prefix: {
type: String,
default: 'icon',
},
name: {
type: String,
required: true,
},
size: {
type: Number,
default: 18,
},
})
const symbolId = computed(() => `#${props.prefix}-${props.name}`)
</script>
<template>
<svg
aria-hidden="true"
:width="`${props.size}px`"
:height="`${props.size}px`"
class="inline"
>
<use
:xlink:href="symbolId"
fill="currentColor"
/>
</svg>
</template>

View File

@ -1,6 +1,6 @@
import type { App } from 'vue' import type { App } from 'vue'
import AppVue from './App.vue' import AppVue from './App.vue'
import AppLoading from './components/common/appLoading.vue' import AppLoading from './components/common/AppLoading.vue'
import { installRouter } from '@/router' import { installRouter } from '@/router'
import { installPinia } from '@/store' import { installPinia } from '@/store'

View File

@ -261,7 +261,7 @@ export const staticRoutes: AppRoute.RowRoute[] = [
'meta.requiresAuth': true, 'meta.requiresAuth': true,
'meta.icon': 'logos:vueuse', 'meta.icon': 'logos:vueuse',
'meta.herf': 'https://vueuse.org/guide/', 'meta.herf': 'https://vueuse.org/guide/',
'componentPath': '/docments/vueuse/index.vue', 'componentPath': 'null',
'id': 27, 'id': 27,
'pid': 24, 'pid': 24,
}, },

View File

@ -19,7 +19,7 @@ declare namespace AppRoute {
order?: number order?: number
/* 嵌套外链 */ /* 嵌套外链 */
herf?: string herf?: string
/** 当前路由不在左侧菜单显示,但需要需要高亮某个菜单的情况 */ /** 当前路由不在左侧菜单显示,但需要高亮某个菜单的情况 */
activeMenu?: string activeMenu?: string
/** 当前路由是否会被添加到Tab中 */ /** 当前路由是否会被添加到Tab中 */
withoutTab?: boolean withoutTab?: boolean

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import HelpInfo from '@/components/common/HelpInfo.vue'
import { useLoading } from '@/hooks' import { useLoading } from '@/hooks'
interface Props { interface Props {
@ -177,10 +178,14 @@ const options = [
:disabled="modalType === 'view'" :disabled="modalType === 'view'"
> >
<n-grid :cols="2" :x-gap="18"> <n-grid :cols="2" :x-gap="18">
<n-form-item-grid-item :span="2" label="父级目录" path="pid"> <n-form-item-grid-item :span="2" path="pid">
<template #label>
父级目录
<HelpInfo message="不填写则为顶层菜单" />
</template>
<n-tree-select <n-tree-select
v-model:value="formModel.pid" filterable clearable :options="dirTreeOptions" key-field="id" label-field="meta.title" v-model:value="formModel.pid" filterable clearable :options="dirTreeOptions" key-field="id"
children-field="children" placeholder="请选择父级目录" label-field="meta.title" children-field="children" placeholder="请选择父级目录"
/> />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="菜单名称" path="name"> <n-form-item-grid-item :span="1" label="菜单名称" path="name">
@ -189,7 +194,7 @@ const options = [
<n-form-item-grid-item :span="1" label="标题" path="meta.title"> <n-form-item-grid-item :span="1" label="标题" path="meta.title">
<n-input v-model:value="formModel['meta.title']" /> <n-input v-model:value="formModel['meta.title']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="2" label="路径" path="path"> <n-form-item-grid-item :span="2" label="路由路径" path="path">
<n-input v-model:value="formModel.path" /> <n-input v-model:value="formModel.path" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="菜单类型" path="meta.menuType"> <n-form-item-grid-item :span="1" label="菜单类型" path="meta.menuType">
@ -205,7 +210,7 @@ const options = [
</n-radio-group> </n-radio-group>
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="图标" path="meta.icon"> <n-form-item-grid-item :span="1" label="图标" path="meta.icon">
<icon-select v-model:value="formModel['meta.icon']" /> <icon-select v-model:value="formModel['meta.icon']" :disabled="modalType === 'view'" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item v-if="formModel['meta.menuType'] === 'page'" :span="2" label="组件路径" path="componentPath"> <n-form-item-grid-item v-if="formModel['meta.menuType'] === 'page'" :span="2" label="组件路径" path="componentPath">
<n-input v-model:value="formModel.componentPath" /> <n-input v-model:value="formModel.componentPath" />
@ -213,28 +218,49 @@ const options = [
<n-form-item-grid-item :span="1" label="菜单排序" path="meta.order"> <n-form-item-grid-item :span="1" label="菜单排序" path="meta.order">
<n-input-number v-model:value="formModel['meta.order']" /> <n-input-number v-model:value="formModel['meta.order']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="外链页面" path="meta.herf"> <n-form-item-grid-item v-if="formModel['meta.menuType'] === 'page'" :span="1" path="meta.herf">
<template #label>
外链页面
<HelpInfo message="填写后,点击菜单将跳转到该地址,组件路径任意填写" />
</template>
<n-input v-model:value="formModel['meta.herf']" /> <n-input v-model:value="formModel['meta.herf']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="登录访问" path="meta.requiresAuth"> <n-form-item-grid-item :span="1" label="登录访问" path="meta.requiresAuth">
<n-switch v-model:value="formModel['meta.requiresAuth']" /> <n-switch v-model:value="formModel['meta.requiresAuth']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="页面缓存" path="meta.keepAlive"> <n-form-item-grid-item
v-if="formModel['meta.menuType'] === 'page'" :span="1" label="页面缓存"
path="meta.keepAlive"
>
<n-switch v-model:value="formModel['meta.keepAlive']" /> <n-switch v-model:value="formModel['meta.keepAlive']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="标签栏可见" path="meta.withoutTab"> <n-form-item-grid-item
v-if="formModel['meta.menuType'] === 'page'" :span="1" label="标签栏可见"
path="meta.withoutTab"
>
<n-switch v-model:value="formModel['meta.withoutTab']" /> <n-switch v-model:value="formModel['meta.withoutTab']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="常驻标签栏" path="meta.pinTab"> <n-form-item-grid-item v-if="formModel['meta.menuType'] === 'page'" :span="1" label="常驻标签栏" path="meta.pinTab">
<n-switch v-model:value="formModel['meta.pinTab']" /> <n-switch v-model:value="formModel['meta.pinTab']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="1" label="侧边菜单隐藏" path="meta.hide"> <n-form-item-grid-item :span="1" label="侧边菜单隐藏" path="meta.hide">
<n-switch v-model:value="formModel['meta.hide']" /> <n-switch v-model:value="formModel['meta.hide']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item v-if="formModel['meta.hide']" :span="2" label="高亮菜单" path="meta.activeMenu"> <n-form-item-grid-item
v-if="formModel['meta.menuType'] === 'page' && formModel['meta.hide']" :span="2"
path="meta.activeMenu"
>
<template #label>
高亮菜单
<HelpInfo message="当前路由不在左侧菜单显示,但需要高亮某个菜单" />
</template>
<n-input v-model:value="formModel['meta.activeMenu']" /> <n-input v-model:value="formModel['meta.activeMenu']" />
</n-form-item-grid-item> </n-form-item-grid-item>
<n-form-item-grid-item :span="2" label="访问角色" path="meta.roles"> <n-form-item-grid-item :span="2" path="meta.roles">
<template #label>
访问角色
<HelpInfo message="不填写则表示所有角色都可以访问" />
</template>
<n-select v-model:value="formModel['meta.roles']" multiple filterable :options="options" /> <n-select v-model:value="formModel['meta.roles']" multiple filterable :options="options" />
</n-form-item-grid-item> </n-form-item-grid-item>
</n-grid> </n-grid>

View File

@ -24,6 +24,7 @@ const columns: DataTableColumns<AppRoute.RowRoute> = [
title: '图标', title: '图标',
align: 'center', align: 'center',
key: 'meta.icon', key: 'meta.icon',
width: '6em',
render: (row) => { render: (row) => {
return row['meta.icon'] && renderIcon(row['meta.icon'], { size: 20 })() return row['meta.icon'] && renderIcon(row['meta.icon'], { size: 20 })()
}, },
@ -31,7 +32,6 @@ const columns: DataTableColumns<AppRoute.RowRoute> = [
{ {
title: '标题', title: '标题',
align: 'center', align: 'center',
width: 200,
key: 'meta.title', key: 'meta.title',
ellipsis: { ellipsis: {
tooltip: true, tooltip: true,
@ -40,7 +40,6 @@ const columns: DataTableColumns<AppRoute.RowRoute> = [
{ {
title: '路径', title: '路径',
key: 'path', key: 'path',
width: 300,
ellipsis: { ellipsis: {
tooltip: true, tooltip: true,
}, },
@ -49,7 +48,6 @@ const columns: DataTableColumns<AppRoute.RowRoute> = [
{ {
title: '组件路径', title: '组件路径',
key: 'componentPath', key: 'componentPath',
width: 300,
ellipsis: { ellipsis: {
tooltip: true, tooltip: true,
}, },
@ -61,11 +59,13 @@ const columns: DataTableColumns<AppRoute.RowRoute> = [
title: '排序值', title: '排序值',
key: 'meta.order', key: 'meta.order',
align: 'center', align: 'center',
width: '6em',
}, },
{ {
title: '菜单类型', title: '菜单类型',
align: 'center', align: 'center',
key: 'meta.menuType', key: 'meta.menuType',
width: '6em',
render: (row) => { render: (row) => {
const menuType = row['meta.menuType'] || 'page' const menuType = row['meta.menuType'] || 'page'
const menuTagType: Record<AppRoute.MenuType, NaiveUI.ThemeColor> = { const menuTagType: Record<AppRoute.MenuType, NaiveUI.ThemeColor> = {
@ -98,7 +98,7 @@ const columns: DataTableColumns<AppRoute.RowRoute> = [
<NPopconfirm onPositiveClick={() => deleteData(rowData.id)}> <NPopconfirm onPositiveClick={() => deleteData(rowData.id)}>
{{ {{
default: () => '确认删除', default: () => '确认删除',
trigger: () => <NButton size="small">删除</NButton>, trigger: () => <NButton size="small" type="error">删除</NButton>,
}} }}
</NPopconfirm> </NPopconfirm>
</NSpace> </NSpace>