feat(menu): 新增菜单自定义样式配置功能

1. 新增菜单样式表单字段,支持传入JSON格式自定义菜单样式
2. 改造侧边栏组件,实现菜单样式的动态绑定与渲染
3. 添加路由元信息style字段的解析与调试日志
4. 修复侧边栏菜单默认样式冲突问题
This commit is contained in:
2026-06-27 13:12:33 +08:00
parent b94b7823e5
commit 3658728acd
6 changed files with 85 additions and 9 deletions

View File

@@ -18,6 +18,11 @@ export default {
},
render(h, context) {
const { icon, title, hasChildren } = context.props
// 函数式组件中 :style 绑定不会进 props需要从 context.data.style 读取
const customStyle = context.data.style || {}
if (Object.keys(customStyle).length > 0) {
console.log('[Item] ✅ 收到 style:', customStyle, '| title:', title)
}
const vnodes = []
if (icon) {
@@ -40,7 +45,25 @@ export default {
)
}
return vnodes
return (
<span class="menu-item-inner" style={customStyle}>
{vnodes}
</span>
)
}
}
</script>
<style lang="scss" scoped>
.menu-item-inner {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
padding: inherit;
box-sizing: border-box;
}
</style>

View File

@@ -4,14 +4,14 @@
<template v-if="hasVisibleChild">
<app-link :to="subMenuRoute">
<el-menu-item :index="basePath" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="item.meta && item.meta.icon" :title="item.meta && item.meta.title" :has-children="true" />
<item :icon="item.meta && item.meta.icon" :title="item.meta && item.meta.title" :has-children="true" :style="menuStyle" />
</el-menu-item>
</app-link>
</template>
<template v-else>
<app-link :to="selfRoute">
<el-menu-item :index="basePath" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="item.meta && item.meta.icon" :title="item.meta && item.meta.title" />
<item :icon="item.meta && item.meta.icon" :title="item.meta && item.meta.title" :style="menuStyle" />
</el-menu-item>
</app-link>
</template>
@@ -20,14 +20,14 @@
<template v-else-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" :style="menuStyle" />
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<el-submenu v-else :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" :style="menuStyle" />
</template>
<sidebar-item
v-for="(child, idx) in item.children"
@@ -89,6 +89,23 @@ export default {
},
subMenuRoute() {
return this.findFirstLeaf(this.item, this.basePath)
},
menuStyle() {
console.log('[SidebarItem] 完整 item 对象:', JSON.parse(JSON.stringify(this.item)))
console.log('[SidebarItem] item.meta:', this.item.meta)
console.log('[SidebarItem] item.meta?.style 原始值:', this.item.meta && this.item.meta.style)
if (this.item.meta && this.item.meta.style) {
try {
const parsed = JSON.parse(this.item.meta.style)
console.log('[SidebarItem] ✅ menuStyle 解析成功:', parsed)
return parsed
} catch (e) {
console.warn('[SidebarItem] ❌ JSON.parse 失败:', e)
return {}
}
}
console.log('[SidebarItem] ⚠️ item.meta.style 为空,返回 {}')
return {}
}
},
methods: {

View File

@@ -13,7 +13,7 @@
class="sub-nav__dropdown"
@command="handleCommand"
>
<span class="sub-nav__item sub-nav__item--dropdown" :class="{ 'is-active': isActive(item) }">
<span class="sub-nav__item sub-nav__item--dropdown" :class="{ 'is-active': isActive(item) }" :style="item._style">
{{ item._title }}
<i class="el-icon-arrow-down el-icon--right" />
</span>
@@ -34,6 +34,7 @@
:to="item._fullPath"
class="sub-nav__item"
:class="{ 'is-active': isActive(item) }"
:style="item._style"
>
{{ item._title }}
</router-link>
@@ -160,7 +161,18 @@ export default {
_ownPath: ownPath,
_fullPath: routeFullPath,
_title: title,
_hasChildren: hasChildren
_hasChildren: hasChildren,
_style: (() => {
const s = c.meta && c.meta.style
if (s) {
try {
return JSON.parse(s)
} catch (e) {
return {}
}
}
return {}
})()
}
if (hasChildren) {
item._children = this.buildItems(c.children, ownPath)