mixedBar 深色样式与折叠逻辑修正
概述
mixedBar 双菜单模式下,一级菜单条通常要比整体菜单背景更深一点才能分出层次,动态背景色不生效时往往是被内部 Menu 背景盖住了。这一节最有价值的是一条标准排错路径:AI 生成的颜色加深函数不能直接信,必须做输入输出验证,并最终定位到「十六进制通道没补前导 0」的经典 bug。同时折叠按钮在双菜单下不应再折叠一级菜单条,同一个 collapse 在不同模式下也有了不同业务含义。
学习目标
- 用 darker() 在 mixedBar 下把一级菜单条做成派生深色,形成主次层次
- 排查动态背景色不生效时优先看是否被内部 Menu 背景盖掉,必要时设 transparent
- 养成对 AI 生成工具函数做输入输出验证的习惯,用固定色值最小验证
- 修复十六进制颜色转换时通道必须用 padStart(2, "0") 补足两位
- 理解双菜单下折叠目标会变,collapse 应按模式解释其作用区域
一、一级菜单条用派生深色拉开层次
mixedBar 下最左侧那条一级菜单导航条,不应和右侧二级菜单区完全同色,否则两层黏在一起、层次感很弱。更合理的方式是:二级菜单区继续用主题设置的 backgroundColor,一级菜单条基于它做一次轻微加深(如 10%)。这样能在一套主题色上做出主次,不需要再硬编码一套新颜色。
const primaryMenuBg = computed(() => {
if (settings.navMode !== "mixedBar") return settings.backgroundColor
return darker(settings.backgroundColor, 0.1)
})先把最外层高度撑满,深色条带才会完整覆盖整列。「加深 10%」适合作为阶段性经验值,最终结果按设计稿或主题算法统一配置。
二、动态背景色不生效先查内部 Menu
课程里明明给外层绑定了背景色,页面效果却几乎没变。最后排查发现不是外层 style 无效,而是内部 Menu 自己还有背景色,把外层颜色遮住了。两种思路:让 Menu 背景和外层一致,或把 Menu 背景设为 transparent——当前选第二种。
const menuBg = computed(() => {
if (settings.navMode === "mixedBar") return "transparent"
return settings.backgroundColor
})<AppMenu :style="{ '--el-menu-bg-color': menuBg }" />transparent 的前提是外层容器本身已有正确背景色。背景策略建议统一收口成计算属性或工具函数,别散落特判。
三、AI 生成的颜色函数必须验证
这一节最有价值的不是「颜色调深了」,而是演示了一条标准排错路径:先怀疑样式绑定没生效,把颜色临时改成固定值 red 确认绑定链路没问题,再回头检查 darker / adjustColor 工具函数。最后发现问题是 AI 工具生成的颜色函数本身有 bug,不在 Vue 模板、也不在动态样式。
console.log("color", color)
console.log("adjustColor", adjustColor(color, 0.1))经验:AI 生成的代码和预期不一致时,先最小化验证,不要立刻怀疑框架;样式问题非常适合用固定颜色值做最小验证;工具函数至少准备几组可复现样例。
四、十六进制通道必须补两位
最终定位到的 bug 很典型:AI 生成的函数把 RGB 通道转十六进制时,没有对单字符结果补前导 0,导致拼接后的颜色少一位。标准十六进制颜色应为 6 位,少一位通常就是转换逻辑有问题。修复核心是 channelToHex:
function channelToHex(value: number) {
return value.toString(16).padStart(2, "0")
}
function darker(color: string, ratio = 0.1) {
const [r, g, b] = hexToRgb(color)
const nextR = Math.max(0, Math.floor(r * (1 - ratio)))
const nextG = Math.max(0, Math.floor(g * (1 - ratio)))
const nextB = Math.max(0, Math.floor(b * (1 - ratio)))
return `#${channelToHex(nextR)}${channelToHex(nextG)}${channelToHex(nextB)}`
}除了 padStart,还要确保输入先规范化成合法的 6 位十六进制字符串。与其在页面反复试,不如给工具函数补几个断言样例。
五、双菜单下折叠不该再收一级菜单条
mixedBar 双菜单模式下,Header 上的折叠按钮逻辑要重新定义。单侧菜单时代折叠整个左侧没问题,但现在一级菜单已是很窄的模块导航条,再去折叠它交互语义就不对了。因此一级菜单不再响应 collapse,折叠按钮改为只影响二级菜单区。
const primaryCollapse = computed(() => {
return settings.navMode !== "mixedBar" && localSettings.collapse
})这里不是删掉折叠功能,而是改变折叠目标。mixedBar 下更值得折叠的是二级菜单区,因为它才是真正占宽度的部分;一级菜单条承担模块导航条角色,稳定显示通常比可折叠更重要。
六、同一个 collapse 在不同模式含义不同
collapse 不是所有模式下都按同一种 UI 效果解释:sidebar 折叠整个左侧主菜单,mixedBar 只收起二级菜单区,top 通常不走左侧折叠逻辑。真正需要抽象的不是「一个布尔值」,而是「当前模式下这个布尔值该作用到哪里」。
const collapseTarget = computed(() => {
if (settings.navMode === "mixedBar") return "secondary"
if (settings.navMode === "sidebar") return "primary"
return "none"
})状态值和状态语义要分开思考。模式越多,越适合把「折叠目标」抽成计算逻辑,而不是散落在模板里到处写 navMode !== "mixedBar" && collapse。
七、mixTop 遗留问题与下节铺垫
切到 mixTop 后顶部菜单虽然显示出来了,但仍表现成普通横向菜单的 hover 下拉行为,而不是「真正的一层模块导航」,同时左侧二级菜单数据也不准确。这说明当前只是把模式切换接通了,但顶部一级菜单的数据源和职责还没真正切换到 mixTop 需要的结构——这不是单纯样式问题,而是菜单职责和数据映射还没切换对。更适合作为下一节单独展开,而不是在当前章节硬塞完。
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 外层背景色绑定了但视觉几乎没变 | 内部 Menu 仍有自己的背景色 | 内部背景设成一致或直接 transparent |
| darker() 没报错但结果颜色不对 | 通道转十六进制没补前导 0 | 用 padStart(2, "0") 保证每段两位 |
| 不知道是模板还是工具函数问题 | 直接在页面肉眼判断 | 先用固定色值测绑定链路,再查工具函数 |
| 双菜单下折叠把一级菜单也收起 | 仍沿用单菜单折叠语义 | mixedBar 下禁用一级折叠,只保留二级 |
| 同 collapse 不同模式效果不一致 | 把布尔值和业务语义混在一起 | 抽出「当前模式折叠目标」计算逻辑 |
| mixTop 菜单显示了但行为是 hover 下拉 | 顶部一级菜单职责没拆开 | 下节单独处理 mixTop 一级菜单映射 |