优化菜单
这里存在一个bug,修复如下:
修改src\components\baseline\menu\src\index.vue
<template>
<div>
<el-menu
class="el-menu-vertical-demo"
:default-active="defaultActive"
:router="router"
v-bind="$attrs"
>
<template v-for="(item, index) in data" :key="index">
<!-- 一级无二级菜单的菜单栏 -->
<el-menu-item
v-if="!item.children || !item.children.length"
:index="item.index"
>
<component v-if="item.icon" :is="item.icon"></component>
<span>{{ item.name }}</span>
</el-menu-item>
<el-sub-menu
v-if="item.children && item.children.length"
:index="item.index"
>
<template #title>
<component v-if="item.icon" :is="item.icon"></component>
<span>{{ item.name }}</span>
</template>
<!-- 二级菜单栏 -->
<el-menu-item
v-for="(item2, index2) in item.children"
:index="item2.index"
>
<component
v-if="item2.icon"
:is="item2.icon"
></component>
<span>{{ item2.name }}</span>
</el-menu-item>
</el-sub-menu>
</template>
</el-menu>
</div>
</template>
<script lang="ts" setup>
import { PropType } from 'vue'
import { MenuItem } from './types'
const props = defineProps({
//说明:
data: {
required: true,
type: Array as PropType<MenuItem[]>,
},
// 默认选中的菜单
defaultActive: {
type: String,
default: '',
},
//是否是路由模式 router,
//是否启用 vue-router 模式
//启用该模式会在激活导航时以 index 作为 path 进行路由跳转
router: {
type: Boolean,
default: false,
},
})
// console.log('data:', props.data)
</script>
<style lang="scss" scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 2rem;
min-height: 4rem;
}
svg {
margin-right: 0.04rem;
}
</style>
修改src\components\baseline\container\src\navAside\index.vue
<template>
<div>
<bs-menu
:collapse="collapse"
:data="data"
router
:default-active="$route.path"
></bs-menu>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
let props = defineProps<{ collapse: boolean }>()
const data = [
{
icon: 'el-icon-home-filled',
name: '首页',
index: '/',
},
{
icon: 'el-icon-check',
name: '图标选择器',
index: '/chooseIcon',
},
{
icon: 'el-icon-location',
name: '省市区选择',
index: '/chooseArea',
},
{
icon: 'el-icon-sort',
name: '趋势标记',
index: '/trend',
},
{
icon: 'el-icon-timer',
name: '时间选择',
index: '/chooseTime',
},
{
icon: 'el-icon-bell',
name: '通知菜单',
index: '/notification',
},
{
icon: 'el-icon-menu',
name: '导航菜单',
index: '/menu',
},
{
icon: 'el-icon-turn-off',
name: '城市选择',
index: '/chooseCity',
},
{
icon: 'el-icon-darrow-right',
name: '进度条',
index: '/progress',
},
{
icon: 'el-icon-scale-to-original',
name: '日历',
index: '/calendar',
},
{
icon: 'el-icon-setting',
name: '表单',
index: '/form',
},
{
icon: 'el-icon-setting',
name: '弹出框表单',
index: '/modalForm',
},
{
icon: 'el-icon-shopping-bag',
name: '表格',
index: '/table',
},
]
const options = reactive([
{
value: '选项1',
label: '基础组件',
},
{
value: '选项2',
label: '技术组件',
},
{
value: '选项3',
label: '业务组件',
},
])
const value = ref('选项1')
</script>
<style lang="scss" scoped>
::deep .el-radio-button {
padding: 0.12rem 0.16rem;
}
</style>
效果如下:
image.png
image.png
兼容后端字段名称
往往后端的字段名称和前端el-menu默认的name、index,和·我们定义的icon、children不一致。可以优化如下,变成名称自定义:
修改src\components\baseline\menu\src\index.vue
<!--
* @Author: bobokaka
* @Date: 2021-12-23 00:07:25
* @LastEditTime: 2021-12-31 17:56:13
* @LastEditors: Please set LastEditors
* @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
* @FilePath: \vue3-element-ui-baseline\src\components\baseline\trend\src\itemIndex.vue
-->
<template>
<div>
<el-menu
class="el-menu-vertical-demo"
:default-active="defaultActive"
:router="router"
v-bind="$attrs"
>
<template v-for="(item, itemIndex) in data" :key="itemIndex">
<!-- 一级无二级菜单的菜单栏 -->
<el-menu-item
v-if="!item[children] || !item[children].length"
:index="item[index]"
>
<component v-if="item[icon]" :is="item[icon]"></component>
<span>{{ item[name] }}</span>
</el-menu-item>
<el-sub-menu
v-if="item[children] && item[children].length"
:index="item[index]"
>
<template #title>
<component
v-if="item[icon]"
:is="item[icon]"
></component>
<span>{{ item[name] }}</span>
</template>
<!-- 二级菜单栏 -->
<el-menu-item
v-for="(item2, index2) in item[children]"
:index="item2[index]"
>
<component
v-if="item2[icon]"
:is="item2[icon]"
></component>
<span>{{ item[name] }}</span>
</el-menu-item>
</el-sub-menu>
</template>
</el-menu>
</div>
</template>
<script lang="ts" setup>
import { PropType } from 'vue'
const props = defineProps({
//说明:
data: {
required: true,
type: Array as PropType<any[]>,
},
// 默认选中的菜单
defaultActive: {
type: String,
default: '',
},
//是否是路由模式 router,
//是否启用 vue-router 模式
//启用该模式会在激活导航时以 itemIndex 作为 path 进行路由跳转
router: {
type: Boolean,
default: false,
},
//菜单标题的件名
name: {
type: String,
default: 'name',
},
//菜单标识的键名
index: {
type: String,
default: 'index',
},
// 菜单图标的键名
icon: {
type: String,
default: 'icon',
},
// 菜单子菜单的键名
children: {
type: String,
default: 'children',
},
})
// console.log('data:', props.data)
</script>
<style lang="scss" scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 2rem;
min-height: 4rem;
}
svg {
margin-right: 0.04rem;
}
</style>
修改src\components\baseline\container\src\navAside\index.vue
<template>
<div>
<bs-menu
:collapse="collapse"
:data="data"
router
name="text"
index="code"
icon="imgSrc"
children="item"
:default-active="$route.path"
></bs-menu>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
let props = defineProps<{ collapse: boolean }>()
const data = [
{
imgSrc: 'el-icon-home-filled',
text: '首页',
code: '/',
},
{
imgSrc: 'el-icon-check',
text: '图标选择器',
code: '/chooseIcon',
},
{
imgSrc: 'el-icon-location',
text: '省市区选择',
code: '/chooseArea',
},
{
imgSrc: 'el-icon-sort',
text: '趋势标记',
code: '/trend',
},
{
imgSrc: 'el-icon-timer',
text: '时间选择',
code: '/chooseTime',
},
{
imgSrc: 'el-icon-bell',
text: '通知菜单',
code: '/notification',
},
{
imgSrc: 'el-icon-menu',
text: '导航菜单',
code: '/menu',
},
{
imgSrc: 'el-icon-turn-off',
text: '城市选择',
code: '/chooseCity',
},
{
imgSrc: 'el-icon-darrow-right',
text: '进度条',
code: '/progress',
},
{
imgSrc: 'el-icon-scale-to-original',
text: '日历',
code: '/calendar',
},
{
imgSrc: 'el-icon-setting',
text: '表单',
code: '/form',
},
{
imgSrc: 'el-icon-setting',
text: '弹出框表单',
code: '/modalForm',
},
{
imgSrc: 'el-icon-shopping-bag',
text: '表格',
code: '/table',
},
]
const options = reactive([
{
value: '选项1',
label: '基础组件',
},
{
value: '选项2',
label: '技术组件',
},
{
value: '选项3',
label: '业务组件',
},
])
const value = ref('选项1')
</script>
<style lang="scss" scoped>
::deep .el-radio-button {
padding: 0.12rem 0.16rem;
}
</style>
运行后,效果一致。
将无限层级菜单,也同样修改:
修改src\components\baseline\menu\src\index.vue
/*
* @Author: your name
* @Date: 2021-12-30 18:07:50
* @LastEditTime: 2021-12-31 18:09:09
* @LastEditors: Please set LastEditors
* @Description: 递归展开层级菜单
* @FilePath: \vue3-element-ui-baseline\src\components\baseline\menu\src\menu.tsx
*/
import { defineComponent, PropType, useAttrs, h, resolveComponent } from 'vue'
import { MenuItem } from './types'
export default defineComponent({
name: 'infiniteMenu',
props: {
//说明:
data: {
required: true,
type: [],
},
// 默认选中的菜单
defaultActive: {
type: String,
default: '',
},
//是否是路由模式 router,
//是否启用 vue-router 模式
//启用该模式会在激活导航时以 index 作为 path 进行路由跳转
router: {
type: Boolean,
default: false,
},
//菜单标题的件名
name: {
type: String,
default: 'name',
},
//菜单标识的键名
index: {
type: String,
default: 'index',
},
// 菜单图标的键名
icon: {
type: String,
default: 'icon',
},
// 菜单子菜单的键名
children: {
type: String,
default: 'children',
},
},
setup(props, ctx) {
//****************** 封装渲染一个无限层菜单的方法 ******************/
let renderMenu = (data: []) => {
return data.map((item: any) => {
//每个菜单的图标
let slots = {
title: () => {
return (
<>
{item[props.icon]
? h(resolveComponent(item[props.icon]))
: ''}
<span>{item[props.name]}</span>
</>
)
},
}
//递归渲染children
if (item[props.children] && item[props.children].length) {
return (
<el-sub-menu index={item[props.index]} v-slots={slots}>
{/* 递归 */}
{renderMenu(item.children)}
</el-sub-menu>
)
}
//正常渲染普通的菜单
return (
<el-menu-item index={item[props.index]}>
{/* 这里直接写<item[props.icon]/>无法渲染出来,需要进行如下处理 */}
{item[props.icon]
? h(resolveComponent(item[props.icon]))
: ''}
<span>{item[props.name]}</span>
</el-menu-item>
)
})
}
let attrs = useAttrs()
// console.log(attrs)
return () => {
return (
<el-menu
default-active={props.defaultActive}
router={props.router}
{...attrs}
>
{renderMenu(props.data)}
</el-menu>
)
}
},
})
修改src\components\baseline\container\src\navAside\index.vue
<!--
* @Author: bobokaka
* @Date: 2021-12-20 00:54:44
* @LastEditTime: 2021-12-31 18:10:22
* @LastEditors: Please set LastEditors
* @Description: 左侧菜单栏
* @FilePath: \vue3-element-ui-baseline\src\components\baseline\container\src\navAside\index.vue
-->
<template>
<div>
<!-- <bs-menu
:collapse="collapse"
:data="data"
router
name="text"
index="code"
icon="imgSrc"
children="item"
:default-active="$route.path"
></bs-menu> -->
<bs-infinite-menu
:collapse="collapse"
:data="data"
router
name="text"
index="code"
icon="imgSrc"
children="item"
:default-active="$route.path"
></bs-infinite-menu>
<!-- <el-menu
default-active="1"
:collapse="collapse"
class="el-menu-vertical-demo"
> -->
<!-- <el-menu-item index="0">
<el-select
v-model="value"
placeholder="请选择"
style="width: 2rem"
v-if="!collapse"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-menu-item> -->
<!-- <el-menu-item index="1">
<el-icon><el-icon-menu /></el-icon>
<span>首页</span>
</el-menu-item>
<el-menu-item index="2">
<el-icon><el-icon-menu /></el-icon>
<span>图标选择器</span>
</el-menu-item>
<el-menu-item index="3">
<el-icon><el-icon-menu /></el-icon>
<span>省市区选择组件</span>
</el-menu-item>
<el-menu-item index="4">
<el-icon><el-icon-menu /></el-icon>
<span>趋势标记</span>
</el-menu-item> -->
<!-- </el-menu> -->
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
let props = defineProps<{ collapse: boolean }>()
const data = [
{
imgSrc: 'el-icon-home-filled',
text: '首页',
code: '/',
},
{
imgSrc: 'el-icon-check',
text: '图标选择器',
code: '/chooseIcon',
},
{
imgSrc: 'el-icon-location',
text: '省市区选择',
code: '/chooseArea',
},
{
imgSrc: 'el-icon-sort',
text: '趋势标记',
code: '/trend',
},
{
imgSrc: 'el-icon-timer',
text: '时间选择',
code: '/chooseTime',
},
{
imgSrc: 'el-icon-bell',
text: '通知菜单',
code: '/notification',
},
{
imgSrc: 'el-icon-menu',
text: '导航菜单',
code: '/menu',
},
{
imgSrc: 'el-icon-turn-off',
text: '城市选择',
code: '/chooseCity',
},
{
imgSrc: 'el-icon-darrow-right',
text: '进度条',
code: '/progress',
},
{
imgSrc: 'el-icon-scale-to-original',
text: '日历',
code: '/calendar',
},
{
imgSrc: 'el-icon-setting',
text: '表单',
code: '/form',
},
{
imgSrc: 'el-icon-setting',
text: '弹出框表单',
code: '/modalForm',
},
{
imgSrc: 'el-icon-shopping-bag',
text: '表格',
code: '/table',
},
]
const options = reactive([
{
value: '选项1',
label: '基础组件',
},
{
value: '选项2',
label: '技术组件',
},
{
value: '选项3',
label: '业务组件',
},
])
const value = ref('选项1')
</script>
<style lang="scss" scoped>
::deep .el-radio-button {
padding: 0.12rem 0.16rem;
}
</style>
运行效果一致:
image.png