mirror of
https://github.com/XiaoDaiGua-Ray/ray-template.git
synced 2025-05-05 09:47:03 +08:00
v3.1.1,补充面包屑和修复一些小问题
This commit is contained in:
parent
13db02c6c3
commit
5676356af5
13
CHANGELOG.md
Normal file
13
CHANGELOG.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# CHANGE LOG
|
||||||
|
|
||||||
|
## 3.1.1
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- 修复国际化语言包模块合并处理不能正常合并问题
|
||||||
|
- 修复国际化切换时,面包屑、标签页不能正常切换
|
||||||
|
|
||||||
|
### Feats
|
||||||
|
|
||||||
|
- 新增面包屑
|
||||||
|
- 支持国际化语言包分包管理(但是,依旧是合并到一个文件中,所以需要注意 key 的管理)
|
@ -8,6 +8,7 @@
|
|||||||
"scrollReveal": "Scroll Reveal",
|
"scrollReveal": "Scroll Reveal",
|
||||||
"Axios": "Axios Request",
|
"Axios": "Axios Request",
|
||||||
"Table": "Table",
|
"Table": "Table",
|
||||||
|
"MultiMenu": "MultiMenu",
|
||||||
"Doc": "Doc",
|
"Doc": "Doc",
|
||||||
"DocLocal": "Doc (China)"
|
"DocLocal": "Doc (China)"
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"scrollReveal": "Scroll Reveal",
|
"scrollReveal": "Scroll Reveal",
|
||||||
"Axios": "Axios Request",
|
"Axios": "Axios Request",
|
||||||
"Table": "Table",
|
"Table": "Table",
|
||||||
|
"MultiMenu": "MultiMenu",
|
||||||
"Doc": "Doc",
|
"Doc": "Doc",
|
||||||
"DocLocal": "Doc (China)"
|
"DocLocal": "Doc (China)"
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"scrollReveal": "滚动动画",
|
"scrollReveal": "滚动动画",
|
||||||
"Axios": "请求",
|
"Axios": "请求",
|
||||||
"Table": "表格",
|
"Table": "表格",
|
||||||
|
"MultiMenu": "多级菜单",
|
||||||
"Doc": "文档",
|
"Doc": "文档",
|
||||||
"DocLocal": "文档 (国内地址)"
|
"DocLocal": "文档 (国内地址)"
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"scrollReveal": "Scroll Reveal",
|
"scrollReveal": "Scroll Reveal",
|
||||||
"Axios": "Axios Request",
|
"Axios": "Axios Request",
|
||||||
"Table": "Table",
|
"Table": "Table",
|
||||||
|
"MultiMenu": "MultiMenu",
|
||||||
"Doc": "Doc",
|
"Doc": "Doc",
|
||||||
"DocLocal": "Doc (China)"
|
"DocLocal": "Doc (China)"
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"scrollReveal": "滚动动画",
|
"scrollReveal": "滚动动画",
|
||||||
"Axios": "请求",
|
"Axios": "请求",
|
||||||
"Table": "表格",
|
"Table": "表格",
|
||||||
|
"MultiMenu": "多级菜单",
|
||||||
"Doc": "文档",
|
"Doc": "文档",
|
||||||
"DocLocal": "文档 (国内地址)"
|
"DocLocal": "文档 (国内地址)"
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"scrollReveal": "滚动动画",
|
"scrollReveal": "滚动动画",
|
||||||
"Axios": "请求",
|
"Axios": "请求",
|
||||||
"Table": "表格",
|
"Table": "表格",
|
||||||
|
"MultiMenu": "多级菜单",
|
||||||
"Doc": "文档",
|
"Doc": "文档",
|
||||||
"DocLocal": "文档 (国内地址)"
|
"DocLocal": "文档 (国内地址)"
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ray-template",
|
"name": "ray-template",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
@ -28,6 +28,7 @@ import { createI18n } from 'vue-i18n'
|
|||||||
|
|
||||||
import { naiveLocales } from './language'
|
import { naiveLocales } from './language'
|
||||||
import { getCache } from '@use-utils/cache'
|
import { getCache } from '@use-utils/cache'
|
||||||
|
import { forIn, merge } from 'lodash-es'
|
||||||
|
|
||||||
export { naiveLocales, localOptions } from './language'
|
export { naiveLocales, localOptions } from './language'
|
||||||
|
|
||||||
@ -51,10 +52,16 @@ export const getMatchLanguageModule = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const moduleKeys = Object.keys(modules)
|
const moduleKeys = Object.keys(modules)
|
||||||
moduleKeys.forEach((curr) => {
|
|
||||||
const k = curr.match(reg)?.[1] as string
|
|
||||||
|
|
||||||
msg[k] = Object.assign({}, JSON.parse(modules[curr]))
|
moduleKeys.forEach((curr) => {
|
||||||
|
const k = curr.match(reg)?.[1] as string // 当前语言包类型(zh-CN, en-US...)
|
||||||
|
const content = JSON.parse(modules[curr]) // 当前语言包内容
|
||||||
|
|
||||||
|
msg[k] = merge({}, msg[k])
|
||||||
|
|
||||||
|
forIn(content, (value, ckey) => {
|
||||||
|
msg[k][ckey] = merge(msg[k][ckey], value)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
@ -19,9 +19,11 @@ const MenuTag = defineComponent({
|
|||||||
name: 'MenuTag',
|
name: 'MenuTag',
|
||||||
setup() {
|
setup() {
|
||||||
const menuStore = useMenu()
|
const menuStore = useMenu()
|
||||||
const { menuTagOptions, menuKey } = storeToRefs(menuStore)
|
const { menuKey } = storeToRefs(menuStore)
|
||||||
const { menuModelValueChange, spliceMenTagOptions } = menuStore
|
const { menuModelValueChange, spliceMenTagOptions } = menuStore
|
||||||
|
|
||||||
|
const modelMenuTagOptions = computed(() => menuStore.menuTagOptions)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param idx 索引
|
* @param idx 索引
|
||||||
@ -32,7 +34,7 @@ const MenuTag = defineComponent({
|
|||||||
spliceMenTagOptions(idx)
|
spliceMenTagOptions(idx)
|
||||||
|
|
||||||
if (menuKey.value !== '/dashboard') {
|
if (menuKey.value !== '/dashboard') {
|
||||||
const options = menuTagOptions.value as MenuOption[]
|
const options = modelMenuTagOptions.value
|
||||||
const length = options.length
|
const length = options.length
|
||||||
|
|
||||||
const tag = options[length - 1]
|
const tag = options[length - 1]
|
||||||
@ -50,7 +52,7 @@ const MenuTag = defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menuTagOptions,
|
modelMenuTagOptions,
|
||||||
menuModelValueChange,
|
menuModelValueChange,
|
||||||
handleCloseTag,
|
handleCloseTag,
|
||||||
menuKey,
|
menuKey,
|
||||||
@ -61,10 +63,10 @@ const MenuTag = defineComponent({
|
|||||||
return (
|
return (
|
||||||
<NScrollbar class="menu-tag" xScrollable>
|
<NScrollbar class="menu-tag" xScrollable>
|
||||||
<NSpace class="menu-tag-sapce" wrap={false} align="center">
|
<NSpace class="menu-tag-sapce" wrap={false} align="center">
|
||||||
{this.menuTagOptions.map((curr, idx) => (
|
{this.modelMenuTagOptions.map((curr, idx) => (
|
||||||
<NTag
|
<NTag
|
||||||
closable={
|
closable={
|
||||||
curr.key !== '/dashboard' && this.menuTagOptions.length > 1
|
curr.key !== '/dashboard' && this.modelMenuTagOptions.length > 1
|
||||||
}
|
}
|
||||||
onClose={() => this.handleCloseTag(idx)}
|
onClose={() => this.handleCloseTag(idx)}
|
||||||
type={curr.key === this.menuKey ? 'success' : 'info'}
|
type={curr.key === this.menuKey ? 'success' : 'info'}
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||||
|
*
|
||||||
|
* @date 2023-03-03
|
||||||
|
*
|
||||||
|
* @workspace ray-template
|
||||||
|
*
|
||||||
|
* @remark 今天也是元气满满撸代码的一天
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 顶部面包屑
|
||||||
|
*
|
||||||
|
* 如果下拉菜单条目小于一条, 则不会触发下拉菜单
|
||||||
|
*
|
||||||
|
* 添加 <span> 标签, 避免 Runtime directive used on component... 警告
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NDropdown, NBreadcrumb, NBreadcrumbItem } from 'naive-ui'
|
||||||
|
|
||||||
|
import { useMenu } from '@/store'
|
||||||
|
|
||||||
|
import type { DropdownOption } from 'naive-ui'
|
||||||
|
|
||||||
|
const Breadcrumb = defineComponent({
|
||||||
|
name: 'Breadcrumb',
|
||||||
|
setup() {
|
||||||
|
const menuStore = useMenu()
|
||||||
|
|
||||||
|
const { menuModelValueChange } = menuStore
|
||||||
|
const modelBreadcrumbOptions = computed(() => menuStore.breadcrumbOptions)
|
||||||
|
|
||||||
|
const handleDropdownSelect = (
|
||||||
|
key: string | number,
|
||||||
|
option: DropdownOption,
|
||||||
|
) => {
|
||||||
|
menuModelValueChange(key, option)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modelBreadcrumbOptions,
|
||||||
|
handleDropdownSelect,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<NBreadcrumb>
|
||||||
|
{this.modelBreadcrumbOptions.map((curr) => (
|
||||||
|
<NBreadcrumbItem key={curr.key}>
|
||||||
|
<NDropdown
|
||||||
|
labelField="breadcrumbLabel"
|
||||||
|
options={
|
||||||
|
curr.children && curr.children?.length > 1 ? curr.children : []
|
||||||
|
}
|
||||||
|
onSelect={this.handleDropdownSelect.bind(this)}
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
default: () => (
|
||||||
|
<span>
|
||||||
|
{curr.label && typeof curr.label === 'function'
|
||||||
|
? curr.label()
|
||||||
|
: curr.breadcrumbLabel}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
</NDropdown>
|
||||||
|
</NBreadcrumbItem>
|
||||||
|
))}
|
||||||
|
</NBreadcrumb>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default Breadcrumb
|
@ -38,8 +38,12 @@ const SettingDrawer = defineComponent({
|
|||||||
const settingStore = useSetting()
|
const settingStore = useSetting()
|
||||||
|
|
||||||
const { changePrimaryColor, changeSwitcher } = settingStore
|
const { changePrimaryColor, changeSwitcher } = settingStore
|
||||||
const { themeValue, primaryColorOverride, menuTagSwitch } =
|
const {
|
||||||
storeToRefs(settingStore)
|
themeValue,
|
||||||
|
primaryColorOverride,
|
||||||
|
menuTagSwitch,
|
||||||
|
breadcrumbSwitch,
|
||||||
|
} = storeToRefs(settingStore)
|
||||||
|
|
||||||
const modelShow = computed({
|
const modelShow = computed({
|
||||||
get: () => props.show,
|
get: () => props.show,
|
||||||
@ -61,6 +65,7 @@ const SettingDrawer = defineComponent({
|
|||||||
primaryColorOverride,
|
primaryColorOverride,
|
||||||
menuTagSwitch,
|
menuTagSwitch,
|
||||||
changeSwitcher,
|
changeSwitcher,
|
||||||
|
breadcrumbSwitch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
@ -135,6 +140,14 @@ const SettingDrawer = defineComponent({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</NDescriptionsItem>
|
</NDescriptionsItem>
|
||||||
|
<NDescriptionsItem label="显示面包屑">
|
||||||
|
<NSwitch
|
||||||
|
v-model:value={this.breadcrumbSwitch}
|
||||||
|
onUpdateValue={(bool: boolean) =>
|
||||||
|
this.changeSwitcher(bool, 'breadcrumbSwitch')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</NDescriptionsItem>
|
||||||
</NDescriptions>
|
</NDescriptions>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</NDrawerContent>
|
</NDrawerContent>
|
||||||
|
@ -15,6 +15,7 @@ import { NLayoutHeader, NSpace, NTooltip, NDropdown, NTag } from 'naive-ui'
|
|||||||
import RayIcon from '@/components/RayIcon/index'
|
import RayIcon from '@/components/RayIcon/index'
|
||||||
import RayTooltipIcon from '@/components/RayTooltipIcon/index'
|
import RayTooltipIcon from '@/components/RayTooltipIcon/index'
|
||||||
import SettingDrawer from './components/SettingDrawer/index'
|
import SettingDrawer from './components/SettingDrawer/index'
|
||||||
|
import Breadcrumb from './components/Breadcrumb/index'
|
||||||
|
|
||||||
import { useSetting } from '@/store'
|
import { useSetting } from '@/store'
|
||||||
import { localOptions } from '@/language/index'
|
import { localOptions } from '@/language/index'
|
||||||
@ -39,7 +40,7 @@ const SiderBar = defineComponent({
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { updateLocale, changeSwitcher } = settingStore
|
const { updateLocale, changeSwitcher } = settingStore
|
||||||
const modelDrawerPlacement = ref(settingStore.drawerPlacement)
|
const { drawerPlacement, breadcrumbSwitch } = storeToRefs(settingStore)
|
||||||
const showSettings = ref(false)
|
const showSettings = ref(false)
|
||||||
const person = getCache('person')
|
const person = getCache('person')
|
||||||
const spaceItemStyle = {
|
const spaceItemStyle = {
|
||||||
@ -127,12 +128,13 @@ const SiderBar = defineComponent({
|
|||||||
rightTooltipIconOptions,
|
rightTooltipIconOptions,
|
||||||
t,
|
t,
|
||||||
handleIconClick,
|
handleIconClick,
|
||||||
modelDrawerPlacement,
|
|
||||||
showSettings,
|
showSettings,
|
||||||
updateLocale,
|
updateLocale,
|
||||||
handlePersonSelect,
|
handlePersonSelect,
|
||||||
person,
|
person,
|
||||||
spaceItemStyle,
|
spaceItemStyle,
|
||||||
|
drawerPlacement,
|
||||||
|
breadcrumbSwitch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
@ -159,6 +161,7 @@ const SiderBar = defineComponent({
|
|||||||
}}
|
}}
|
||||||
</NTooltip>
|
</NTooltip>
|
||||||
))}
|
))}
|
||||||
|
{this.breadcrumbSwitch ? <Breadcrumb /> : ''}
|
||||||
</NSpace>
|
</NSpace>
|
||||||
<NSpace align="center" itemStyle={this.spaceItemStyle}>
|
<NSpace align="center" itemStyle={this.spaceItemStyle}>
|
||||||
{this.rightTooltipIconOptions.map((curr) => (
|
{this.rightTooltipIconOptions.map((curr) => (
|
||||||
@ -203,7 +206,7 @@ const SiderBar = defineComponent({
|
|||||||
</NSpace>
|
</NSpace>
|
||||||
<SettingDrawer
|
<SettingDrawer
|
||||||
v-model:show={this.showSettings}
|
v-model:show={this.showSettings}
|
||||||
placement={this.modelDrawerPlacement}
|
placement={this.drawerPlacement}
|
||||||
/>
|
/>
|
||||||
</NLayoutHeader>
|
</NLayoutHeader>
|
||||||
)
|
)
|
||||||
|
@ -4,10 +4,11 @@ import { constantRoutes } from './routes'
|
|||||||
import { permissionRouter as _permissionRouter } from './permission'
|
import { permissionRouter as _permissionRouter } from './permission'
|
||||||
|
|
||||||
import type { App } from 'vue'
|
import type { App } from 'vue'
|
||||||
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: constantRoutes,
|
routes: constantRoutes as unknown as RouteRecordRaw[],
|
||||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import scrollReveal from './scroll-reveal'
|
|||||||
import axios from './axios'
|
import axios from './axios'
|
||||||
import table from './table'
|
import table from './table'
|
||||||
import doc from './doc'
|
import doc from './doc'
|
||||||
|
import multiMenu from './multi-menu'
|
||||||
import docLocal from './doc-local'
|
import docLocal from './doc-local'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
@ -15,6 +16,7 @@ const routes = [
|
|||||||
axios,
|
axios,
|
||||||
scrollReveal,
|
scrollReveal,
|
||||||
error,
|
error,
|
||||||
|
multiMenu,
|
||||||
doc,
|
doc,
|
||||||
docLocal,
|
docLocal,
|
||||||
reyl,
|
reyl,
|
||||||
|
40
src/router/modules/multi-menu.ts
Normal file
40
src/router/modules/multi-menu.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
export default {
|
||||||
|
path: '/multi-menu',
|
||||||
|
name: 'multi-menu',
|
||||||
|
component: () => import('@/views/multi-menu/index'),
|
||||||
|
meta: {
|
||||||
|
i18nKey: 'MultiMenu',
|
||||||
|
icon: 'table',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'multi-menu-one',
|
||||||
|
name: 'multi-menu-one',
|
||||||
|
component: () => import('@/views/multi-menu/views/multi-menu-one/index'),
|
||||||
|
meta: {
|
||||||
|
noLocalTitle: '多级菜单-1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'multi-menu-two',
|
||||||
|
name: 'multi-menu-two',
|
||||||
|
component: () => import('@/views/multi-menu/views/multi-menu-two/index'),
|
||||||
|
meta: {
|
||||||
|
noLocalTitle: '多级菜单-2',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'sub-menu',
|
||||||
|
name: 'sub-menu',
|
||||||
|
component: () =>
|
||||||
|
import(
|
||||||
|
'@/views/multi-menu/views/multi-menu-two/views/sub-menu/index'
|
||||||
|
),
|
||||||
|
meta: {
|
||||||
|
noLocalTitle: '多级菜单-2-1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
23
src/router/remark.md
Normal file
23
src/router/remark.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
- 类型
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface RouteMeta {
|
||||||
|
i18nKey: string
|
||||||
|
icon?: string
|
||||||
|
windowOpen?: string
|
||||||
|
role?: string[]
|
||||||
|
hidden?: boolean
|
||||||
|
noLocalTitle?: string | number
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- 说明
|
||||||
|
|
||||||
|
```
|
||||||
|
i18nKey: i18n 国际化 key, 会优先使用该字段
|
||||||
|
icon: icon 图标, 用于 Menu 菜单(依赖 RayIcon 组件实现)
|
||||||
|
windowOpen: 超链接打开
|
||||||
|
role: 权限表
|
||||||
|
hidden: 是否显示
|
||||||
|
noLocalTitle: 不使用国际化渲染 Menu Titile
|
||||||
|
```
|
@ -3,7 +3,7 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
|||||||
import type { App } from 'vue'
|
import type { App } from 'vue'
|
||||||
|
|
||||||
export { useSetting } from './modules/setting' // import { useSetting } from '@/store' 即可使用
|
export { useSetting } from './modules/setting' // import { useSetting } from '@/store' 即可使用
|
||||||
export { useMenu } from './modules/menu'
|
export { useMenu } from './modules/menu/index'
|
||||||
export { useSignin } from './modules/signin'
|
export { useSignin } from './modules/signin'
|
||||||
|
|
||||||
const store = createPinia()
|
const store = createPinia()
|
||||||
|
@ -1,165 +0,0 @@
|
|||||||
import { NEllipsis } from 'naive-ui'
|
|
||||||
import RayIcon from '@/components/RayIcon/index'
|
|
||||||
|
|
||||||
import { getCache, setCache } from '@/utils/cache'
|
|
||||||
import { validRole } from '@/router/basic'
|
|
||||||
|
|
||||||
import type { MenuOption } from 'naive-ui'
|
|
||||||
import type { RouteMeta } from 'vue-router'
|
|
||||||
|
|
||||||
export const useMenu = defineStore('menu', () => {
|
|
||||||
const router = useRouter()
|
|
||||||
const route = useRoute()
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const cacheMenuKey =
|
|
||||||
getCache('menuKey') === 'no' ? '/dashboard' : getCache('menuKey')
|
|
||||||
|
|
||||||
const menuState = reactive({
|
|
||||||
menuKey: cacheMenuKey as string | null, // 当前菜单 `key`
|
|
||||||
options: [] as IMenuOptions[], // 菜单列表
|
|
||||||
collapsed: false, // 是否折叠菜单
|
|
||||||
menuTagOptions: [] as TagMenuOptions[],
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleMenuTagOptions = (item: IMenuOptions) => {
|
|
||||||
if (item.path !== menuState.menuKey) {
|
|
||||||
const tag = menuState.menuTagOptions.find(
|
|
||||||
(curr) => curr.path === item.path,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!tag) {
|
|
||||||
menuState.menuTagOptions.push(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param key 菜单更新后的 `key`
|
|
||||||
* @param item 菜单当前 `item`
|
|
||||||
*
|
|
||||||
* 修改 `menu key` 后的回调函数
|
|
||||||
*/
|
|
||||||
const menuModelValueChange = (key: string, item: MenuOption) => {
|
|
||||||
const meta = item.meta as RouteMeta
|
|
||||||
|
|
||||||
if (meta.windowOpen) {
|
|
||||||
window.open(meta.windowOpen)
|
|
||||||
} else {
|
|
||||||
handleMenuTagOptions(item as unknown as TagMenuOptions)
|
|
||||||
|
|
||||||
menuState.menuKey = key
|
|
||||||
|
|
||||||
router.push(`${item.path}`)
|
|
||||||
|
|
||||||
setCache('menuKey', key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param path 路由地址
|
|
||||||
*
|
|
||||||
* 监听路由地址变化更新菜单状态
|
|
||||||
*/
|
|
||||||
const updateMenuKeyWhenRouteUpdate = (path: string) => {
|
|
||||||
const matchMenuItem = (options: MenuOption[]) => {
|
|
||||||
for (const i of options) {
|
|
||||||
if (i?.children?.length) {
|
|
||||||
matchMenuItem(i.children)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path === i.path) {
|
|
||||||
menuModelValueChange(i.path, i)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matchMenuItem(menuState.options as MenuOption[])
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @remark 初始化菜单列表, 并且按照权限过滤
|
|
||||||
* @remark 如果权限发生变动, 则会触发强制弹出页面并且重新登陆
|
|
||||||
*/
|
|
||||||
const setupAppRoutes = () => {
|
|
||||||
const layout = router.getRoutes().find((route) => route.name === 'layout')
|
|
||||||
|
|
||||||
const resolveRoutes = (routes: IMenuOptions[], index: number) => {
|
|
||||||
return routes.map((curr) => {
|
|
||||||
if (curr.children?.length) {
|
|
||||||
curr.children = resolveRoutes(curr.children, index++)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { meta } = curr
|
|
||||||
|
|
||||||
const route = {
|
|
||||||
...curr,
|
|
||||||
key: curr.path,
|
|
||||||
label: () =>
|
|
||||||
h(NEllipsis, null, {
|
|
||||||
default: () => t(`GlobalMenuOptions.${meta!.i18nKey}`),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandIcon = {
|
|
||||||
icon: () =>
|
|
||||||
h(
|
|
||||||
RayIcon,
|
|
||||||
{
|
|
||||||
name: meta!.icon as string,
|
|
||||||
size: 20,
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
const attr: IMenuOptions = meta?.icon
|
|
||||||
? Object.assign({}, route, expandIcon)
|
|
||||||
: route
|
|
||||||
|
|
||||||
if (curr.path === cacheMenuKey) {
|
|
||||||
menuState.menuTagOptions.push(attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
attr.show = validRole(curr)
|
|
||||||
|
|
||||||
return attr
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
menuState.options = resolveRoutes(layout?.children as IMenuOptions[], 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param collapsed 折叠菜单开关
|
|
||||||
*/
|
|
||||||
const collapsedMenu = (collapsed: boolean) =>
|
|
||||||
(menuState.collapsed = collapsed)
|
|
||||||
|
|
||||||
const spliceMenTagOptions = (idx: number) =>
|
|
||||||
menuState.menuTagOptions.splice(idx, 1)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => route.fullPath,
|
|
||||||
(newData) => {
|
|
||||||
updateMenuKeyWhenRouteUpdate(newData)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...toRefs(menuState),
|
|
||||||
menuModelValueChange,
|
|
||||||
setupAppRoutes,
|
|
||||||
collapsedMenu,
|
|
||||||
spliceMenTagOptions,
|
|
||||||
}
|
|
||||||
})
|
|
110
src/store/modules/menu/helper.ts
Normal file
110
src/store/modules/menu/helper.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||||
|
*
|
||||||
|
* @date 2023-03-03
|
||||||
|
*
|
||||||
|
* @workspace ray-template
|
||||||
|
*
|
||||||
|
* @remark 今天也是元气满满撸代码的一天
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 本方法感谢 <https://yunkuangao.me/> 的支持 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param node 当前节点
|
||||||
|
* @param key 动态字段
|
||||||
|
* @param value 匹配值
|
||||||
|
*
|
||||||
|
* @remark 检查是否为所需项
|
||||||
|
*/
|
||||||
|
const check = (
|
||||||
|
node: IMenuOptions,
|
||||||
|
key: string | number,
|
||||||
|
value: string | number,
|
||||||
|
) => {
|
||||||
|
return node[key] === value || node.key === value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param options 节点数组
|
||||||
|
* @param key 动态字段
|
||||||
|
* @param value 匹配值
|
||||||
|
*
|
||||||
|
* @remark 匹配所有节点
|
||||||
|
*/
|
||||||
|
const process = (
|
||||||
|
options: IMenuOptions,
|
||||||
|
key: string | number,
|
||||||
|
value: string | number,
|
||||||
|
) => {
|
||||||
|
const temp: IMenuOptions[] = []
|
||||||
|
|
||||||
|
// 检查当前节点是否匹配值
|
||||||
|
if (check(options, key, value)) {
|
||||||
|
temp.push(options)
|
||||||
|
|
||||||
|
return temp
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历子节点
|
||||||
|
if (options.children && options.children.length > 0) {
|
||||||
|
for (const it of options.children) {
|
||||||
|
// 子节点递归调用
|
||||||
|
const innerTemp = process(it, key, value)
|
||||||
|
|
||||||
|
// 如果子节点匹配到了,则将当前节点加入数组
|
||||||
|
if (innerTemp.length > 0) {
|
||||||
|
temp.push(options, ...innerTemp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return temp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param options 节点数组
|
||||||
|
* @param key 动态字段
|
||||||
|
* @param value 匹配值
|
||||||
|
*/
|
||||||
|
export const parse = (
|
||||||
|
options: IMenuOptions[],
|
||||||
|
key: string | number,
|
||||||
|
value: string | number,
|
||||||
|
) => {
|
||||||
|
const temp = []
|
||||||
|
|
||||||
|
for (const it of options) {
|
||||||
|
const innerTemp = process(it, key, value)
|
||||||
|
|
||||||
|
if (innerTemp.length > 0) {
|
||||||
|
temp.push(...innerTemp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return temp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param item menu options
|
||||||
|
*
|
||||||
|
* @remark 查找当前菜单项
|
||||||
|
*/
|
||||||
|
export const matchMenuOption = (
|
||||||
|
item: IMenuOptions,
|
||||||
|
key: MenuKey,
|
||||||
|
menuTagOptions: TagMenuOptions[],
|
||||||
|
) => {
|
||||||
|
if (item.path !== key) {
|
||||||
|
const tag = menuTagOptions.find((curr) => curr.path === item.path)
|
||||||
|
|
||||||
|
if (!tag) {
|
||||||
|
menuTagOptions.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
218
src/store/modules/menu/index.ts
Normal file
218
src/store/modules/menu/index.ts
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||||
|
*
|
||||||
|
* @date 2022-11-03
|
||||||
|
*
|
||||||
|
* @workspace ray-template
|
||||||
|
*
|
||||||
|
* @remark 今天也是元气满满撸代码的一天
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 该文件为 menu 菜单 pinia store
|
||||||
|
*
|
||||||
|
* 说明:
|
||||||
|
* - BreadcrumbMenu、TagMenu、Menu 统一管理
|
||||||
|
* - BreadcrumbMenu、TagMenu、Menu 属性值重度依赖 vue-router routers, 所以需要按照该项目约定方法进行配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NEllipsis } from 'naive-ui'
|
||||||
|
import RayIcon from '@/components/RayIcon/index'
|
||||||
|
|
||||||
|
import { getCache, setCache } from '@/utils/cache'
|
||||||
|
import { validRole } from '@/router/basic'
|
||||||
|
import { parse, matchMenuOption } from './helper'
|
||||||
|
|
||||||
|
import type { MenuOption } from 'naive-ui'
|
||||||
|
import type { RouteMeta } from 'vue-router'
|
||||||
|
|
||||||
|
export const useMenu = defineStore(
|
||||||
|
'menu',
|
||||||
|
() => {
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const cacheMenuKey =
|
||||||
|
getCache('menuKey') === 'no' ? '/dashboard' : getCache('menuKey')
|
||||||
|
|
||||||
|
const menuState = reactive({
|
||||||
|
menuKey: cacheMenuKey as MenuKey, // 当前菜单 `key`
|
||||||
|
options: [] as IMenuOptions[], // 菜单列表
|
||||||
|
collapsed: false, // 是否折叠菜单
|
||||||
|
menuTagOptions: [] as TagMenuOptions[], // tag 标签菜单
|
||||||
|
breadcrumbOptions: [] as IMenuOptions[], // 面包屑菜单
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param key 菜单更新后的 `key`
|
||||||
|
* @param item 菜单当前 `item`
|
||||||
|
*
|
||||||
|
* 修改 `menu key` 后的回调函数
|
||||||
|
*/
|
||||||
|
const menuModelValueChange = (key: string | number, item: MenuOption) => {
|
||||||
|
const meta = item.meta as RouteMeta
|
||||||
|
|
||||||
|
if (meta.windowOpen) {
|
||||||
|
window.open(meta.windowOpen)
|
||||||
|
} else {
|
||||||
|
// 防止重复点击做重复操作处理
|
||||||
|
if (menuState.menuKey !== key) {
|
||||||
|
matchMenuOption(
|
||||||
|
item as unknown as TagMenuOptions,
|
||||||
|
menuState.menuKey,
|
||||||
|
menuState.menuTagOptions,
|
||||||
|
)
|
||||||
|
|
||||||
|
menuState.breadcrumbOptions = parse(menuState.options, 'key', key) // 获取面包屑
|
||||||
|
|
||||||
|
if (key[0] !== '/') {
|
||||||
|
const p = menuState.breadcrumbOptions
|
||||||
|
.map((curr) => curr.key)
|
||||||
|
.join('/')
|
||||||
|
|
||||||
|
router.push(p)
|
||||||
|
} else {
|
||||||
|
router.push(item.path as string)
|
||||||
|
}
|
||||||
|
|
||||||
|
menuState.menuKey = key
|
||||||
|
setCache('menuKey', key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param path 路由地址
|
||||||
|
*
|
||||||
|
* 监听路由地址变化更新菜单状态
|
||||||
|
*/
|
||||||
|
const updateMenuKeyWhenRouteUpdate = (path: string) => {
|
||||||
|
const matchMenuItem = (options: MenuOption[]) => {
|
||||||
|
for (const i of options) {
|
||||||
|
if (i?.children?.length) {
|
||||||
|
matchMenuItem(i.children)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path === i.path) {
|
||||||
|
menuModelValueChange(i.path, i)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matchMenuItem(menuState.options as MenuOption[])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @remark 初始化菜单列表, 并且按照权限过滤
|
||||||
|
* @remark 如果权限发生变动, 则会触发强制弹出页面并且重新登陆
|
||||||
|
*/
|
||||||
|
const setupAppRoutes = () => {
|
||||||
|
const layout = router.getRoutes().find((route) => route.name === 'layout')
|
||||||
|
|
||||||
|
const resolveRoutes = (routes: IMenuOptions[], index: number) => {
|
||||||
|
return routes.map((curr) => {
|
||||||
|
if (curr.children?.length) {
|
||||||
|
curr.children = resolveRoutes(curr.children, index++)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { meta } = curr
|
||||||
|
const label = computed(() =>
|
||||||
|
meta?.i18nKey
|
||||||
|
? t(`GlobalMenuOptions.${meta!.i18nKey}`)
|
||||||
|
: meta?.noLocalTitle,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** 拼装菜单项 */
|
||||||
|
const route = {
|
||||||
|
...curr,
|
||||||
|
key: curr.path,
|
||||||
|
label: () =>
|
||||||
|
h(NEllipsis, null, {
|
||||||
|
default: () => label.value,
|
||||||
|
}),
|
||||||
|
breadcrumbLabel: label.value,
|
||||||
|
} as IMenuOptions
|
||||||
|
|
||||||
|
/** 是否有 icon */
|
||||||
|
const expandIcon = {
|
||||||
|
icon: () =>
|
||||||
|
h(
|
||||||
|
RayIcon,
|
||||||
|
{
|
||||||
|
name: meta!.icon as string,
|
||||||
|
size: 20,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
const attr: IMenuOptions = meta?.icon
|
||||||
|
? Object.assign({}, route, expandIcon)
|
||||||
|
: route
|
||||||
|
|
||||||
|
if (curr.path === cacheMenuKey) {
|
||||||
|
menuState.menuTagOptions.push(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
attr.show = validRole(curr)
|
||||||
|
|
||||||
|
return attr
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
menuState.options = resolveRoutes(layout?.children as IMenuOptions[], 0)
|
||||||
|
|
||||||
|
/** 初始化后渲染面包屑 */
|
||||||
|
nextTick(() => {
|
||||||
|
menuState.breadcrumbOptions = parse(
|
||||||
|
menuState.options,
|
||||||
|
'key',
|
||||||
|
menuState.menuKey as string,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param collapsed 折叠菜单开关
|
||||||
|
*/
|
||||||
|
const collapsedMenu = (collapsed: boolean) =>
|
||||||
|
(menuState.collapsed = collapsed)
|
||||||
|
|
||||||
|
const spliceMenTagOptions = (idx: number) =>
|
||||||
|
menuState.menuTagOptions.splice(idx, 1)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.fullPath,
|
||||||
|
(newData) => {
|
||||||
|
updateMenuKeyWhenRouteUpdate(newData)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(menuState),
|
||||||
|
menuModelValueChange,
|
||||||
|
setupAppRoutes,
|
||||||
|
collapsedMenu,
|
||||||
|
spliceMenTagOptions,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
persist: {
|
||||||
|
key: 'piniaMenuStore',
|
||||||
|
storage: window.sessionStorage,
|
||||||
|
paths: ['breadcrumbOptions', 'menuKey'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
@ -1,4 +1,5 @@
|
|||||||
import { naiveLocales, getDefaultNaiveLocal } from '@/language/index'
|
import { naiveLocales, getDefaultNaiveLocal } from '@/language/index'
|
||||||
|
import { setCache } from '@use-utils/cache'
|
||||||
|
|
||||||
export const useSetting = defineStore(
|
export const useSetting = defineStore(
|
||||||
'setting',
|
'setting',
|
||||||
@ -15,6 +16,7 @@ export const useSetting = defineStore(
|
|||||||
menuTagSwitch: true, // 多标签页开关
|
menuTagSwitch: true, // 多标签页开关
|
||||||
naiveLocal: getDefaultNaiveLocal(), // `naive ui` 语言包
|
naiveLocal: getDefaultNaiveLocal(), // `naive ui` 语言包
|
||||||
spinSwitch: false, // 全屏加载
|
spinSwitch: false, // 全屏加载
|
||||||
|
breadcrumbSwitch: true, // 面包屑开关
|
||||||
})
|
})
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
|
|
||||||
@ -22,6 +24,8 @@ export const useSetting = defineStore(
|
|||||||
// TODO: 修改语言
|
// TODO: 修改语言
|
||||||
locale.value = key
|
locale.value = key
|
||||||
settingState.naiveLocal = naiveLocales(key)
|
settingState.naiveLocal = naiveLocales(key)
|
||||||
|
|
||||||
|
setCache('localeLanguage', key, 'localStorage')
|
||||||
}
|
}
|
||||||
|
|
||||||
const changePrimaryColor = (value: string) => {
|
const changePrimaryColor = (value: string) => {
|
||||||
|
4
src/types/store.d.ts
vendored
4
src/types/store.d.ts
vendored
@ -13,7 +13,11 @@ declare global {
|
|||||||
show?: boolean
|
show?: boolean
|
||||||
children?: IMenuOptions[]
|
children?: IMenuOptions[]
|
||||||
meta?: RouteMeta
|
meta?: RouteMeta
|
||||||
|
breadcrumbLabel?: string
|
||||||
|
noLocalTitle?: string | number
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface TagMenuOptions extends IMenuOptions {}
|
declare interface TagMenuOptions extends IMenuOptions {}
|
||||||
|
|
||||||
|
declare type MenuKey = null | string | number
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
NLayoutHeader,
|
NLayoutHeader,
|
||||||
NSpace,
|
NSpace,
|
||||||
NInput,
|
NInput,
|
||||||
|
NButton,
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import { onAxiosTest } from '@use-api/test'
|
import { onAxiosTest } from '@use-api/test'
|
||||||
|
|
||||||
@ -41,9 +42,13 @@ const Axios = defineComponent({
|
|||||||
]
|
]
|
||||||
|
|
||||||
const handleInputCityValue = async (value: string) => {
|
const handleInputCityValue = async (value: string) => {
|
||||||
const cb = await onAxiosTest(value)
|
try {
|
||||||
|
const cb = await onAxiosTest(value)
|
||||||
|
|
||||||
state.weatherData = cb.data as unknown as IUnknownObjectKey[]
|
state.weatherData = cb.data as unknown as IUnknownObjectKey[]
|
||||||
|
} catch (e) {
|
||||||
|
window.$message.error('请求已被取消')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
@ -62,20 +67,23 @@ const Axios = defineComponent({
|
|||||||
<NLayout>
|
<NLayout>
|
||||||
<NLayoutHeader bordered>
|
<NLayoutHeader bordered>
|
||||||
<NCard title="请求函数">
|
<NCard title="请求函数">
|
||||||
基于 axios 封装, 能够自动取消连续请求, 避免重复渲染造成问题.
|
基于 axios 封装,能够自动取消连续请求,避免重复渲染造成问题
|
||||||
|
<p>
|
||||||
|
打开控制台 => 网络 => 使用低速3g网络 =>
|
||||||
|
查看控制台被取消的请求
|
||||||
|
</p>
|
||||||
</NCard>
|
</NCard>
|
||||||
</NLayoutHeader>
|
</NLayoutHeader>
|
||||||
<NLayoutHeader bordered>
|
<NLayoutHeader bordered>
|
||||||
<NSpace
|
<NSpace class="axios-header__btn" align="center">
|
||||||
class="axios-header__btn"
|
|
||||||
align="center"
|
|
||||||
justify="space-between"
|
|
||||||
>
|
|
||||||
<NInput
|
<NInput
|
||||||
v-model:value={this.inputCityValue}
|
v-model:value={this.inputCityValue}
|
||||||
onInput={this.handleInputCityValue.bind(this)}
|
onInput={this.handleInputCityValue.bind(this)}
|
||||||
placeholder="请输入城市"
|
placeholder="请输入城市"
|
||||||
/>
|
/>
|
||||||
|
<NButton onClick={this.handleInputCityValue.bind(this, '')}>
|
||||||
|
搜索
|
||||||
|
</NButton>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</NLayoutHeader>
|
</NLayoutHeader>
|
||||||
<NLayoutContent>
|
<NLayoutContent>
|
||||||
|
24
src/views/multi-menu/index.tsx
Normal file
24
src/views/multi-menu/index.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||||
|
*
|
||||||
|
* @date 2023-03-01
|
||||||
|
*
|
||||||
|
* @workspace ray-template
|
||||||
|
*
|
||||||
|
* @remark 今天也是元气满满撸代码的一天
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { RouterView } from 'vue-router'
|
||||||
|
|
||||||
|
const MultiMenu = defineComponent({
|
||||||
|
name: 'MultiMenu',
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return <RouterView />
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default MultiMenu
|
22
src/views/multi-menu/views/multi-menu-one/index.tsx
Normal file
22
src/views/multi-menu/views/multi-menu-one/index.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||||
|
*
|
||||||
|
* @date 2023-03-01
|
||||||
|
*
|
||||||
|
* @workspace ray-template
|
||||||
|
*
|
||||||
|
* @remark 今天也是元气满满撸代码的一天
|
||||||
|
*/
|
||||||
|
|
||||||
|
const MultiMenuOne = defineComponent({
|
||||||
|
name: 'MultiMenuOne',
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return <div>多级菜单-1</div>
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default MultiMenuOne
|
24
src/views/multi-menu/views/multi-menu-two/index.tsx
Normal file
24
src/views/multi-menu/views/multi-menu-two/index.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||||
|
*
|
||||||
|
* @date 2023-03-01
|
||||||
|
*
|
||||||
|
* @workspace ray-template
|
||||||
|
*
|
||||||
|
* @remark 今天也是元气满满撸代码的一天
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { RouterView } from 'vue-router'
|
||||||
|
|
||||||
|
const MultiMenuTwo = defineComponent({
|
||||||
|
name: 'MultiMenuTwo',
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return <RouterView />
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default MultiMenuTwo
|
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Ray <https://github.com/XiaoDaiGua-Ray>
|
||||||
|
*
|
||||||
|
* @date 2023-03-01
|
||||||
|
*
|
||||||
|
* @workspace ray-template
|
||||||
|
*
|
||||||
|
* @remark 今天也是元气满满撸代码的一天
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SubMenu = defineComponent({
|
||||||
|
name: 'SubMenu',
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return <div>多级菜单-2-1</div>
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default SubMenu
|
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@ -17,6 +17,7 @@ declare module 'vue-router' {
|
|||||||
windowOpen?: string
|
windowOpen?: string
|
||||||
role?: string[]
|
role?: string[]
|
||||||
hidden?: boolean
|
hidden?: boolean
|
||||||
|
noLocalTitle?: string | number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user