mirror of
https://github.com/chansee97/nova-admin.git
synced 2025-04-06 03:57:54 +08:00
fix: perfect menu manage
This commit is contained in:
parent
cd67da6b00
commit
de368c7e9e
2
.env
2
.env
@ -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
|
||||||
|
16
src/components/common/HelpInfo.vue
Normal file
16
src/components/common/HelpInfo.vue
Normal 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>
|
@ -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" />
|
@ -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>
|
|
@ -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'
|
||||||
|
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
2
src/typings/route.d.ts
vendored
2
src/typings/route.d.ts
vendored
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user