{T}

mixedBar 深色样式与折叠逻辑修正

概述

mixedBar 双菜单模式下,一级菜单条通常要比整体菜单背景更深一点才能分出层次,动态背景色不生效时往往是被内部 Menu 背景盖住了。这一节最有价值的是一条标准排错路径:AI 生成的颜色加深函数不能直接信,必须做输入输出验证,并最终定位到「十六进制通道没补前导 0」的经典 bug。同时折叠按钮在双菜单下不应再折叠一级菜单条,同一个 collapse 在不同模式下也有了不同业务含义。

学习目标

  • 用 darker() 在 mixedBar 下把一级菜单条做成派生深色,形成主次层次
  • 排查动态背景色不生效时优先看是否被内部 Menu 背景盖掉,必要时设 transparent
  • 养成对 AI 生成工具函数做输入输出验证的习惯,用固定色值最小验证
  • 修复十六进制颜色转换时通道必须用 padStart(2, "0") 补足两位
  • 理解双菜单下折叠目标会变,collapse 应按模式解释其作用区域

一、一级菜单条用派生深色拉开层次

mixedBar 下最左侧那条一级菜单导航条,不应和右侧二级菜单区完全同色,否则两层黏在一起、层次感很弱。更合理的方式是:二级菜单区继续用主题设置的 backgroundColor,一级菜单条基于它做一次轻微加深(如 10%)。这样能在一套主题色上做出主次,不需要再硬编码一套新颜色。

ts
const primaryMenuBg = computed(() => {
  if (settings.navMode !== "mixedBar") return settings.backgroundColor
  return darker(settings.backgroundColor, 0.1)
})

先把最外层高度撑满,深色条带才会完整覆盖整列。「加深 10%」适合作为阶段性经验值,最终结果按设计稿或主题算法统一配置。

二、动态背景色不生效先查内部 Menu

课程里明明给外层绑定了背景色,页面效果却几乎没变。最后排查发现不是外层 style 无效,而是内部 Menu 自己还有背景色,把外层颜色遮住了。两种思路:让 Menu 背景和外层一致,或把 Menu 背景设为 transparent——当前选第二种。

ts
const menuBg = computed(() => {
  if (settings.navMode === "mixedBar") return "transparent"
  return settings.backgroundColor
})
Vue SFC
<AppMenu :style="{ '--el-menu-bg-color': menuBg }" />

transparent 的前提是外层容器本身已有正确背景色。背景策略建议统一收口成计算属性或工具函数,别散落特判。

三、AI 生成的颜色函数必须验证

这一节最有价值的不是「颜色调深了」,而是演示了一条标准排错路径:先怀疑样式绑定没生效,把颜色临时改成固定值 red 确认绑定链路没问题,再回头检查 darker / adjustColor 工具函数。最后发现问题是 AI 工具生成的颜色函数本身有 bug,不在 Vue 模板、也不在动态样式。

ts
console.log("color", color)
console.log("adjustColor", adjustColor(color, 0.1))

经验:AI 生成的代码和预期不一致时,先最小化验证,不要立刻怀疑框架;样式问题非常适合用固定颜色值做最小验证;工具函数至少准备几组可复现样例。

四、十六进制通道必须补两位

最终定位到的 bug 很典型:AI 生成的函数把 RGB 通道转十六进制时,没有对单字符结果补前导 0,导致拼接后的颜色少一位。标准十六进制颜色应为 6 位,少一位通常就是转换逻辑有问题。修复核心是 channelToHex

ts
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,折叠按钮改为只影响二级菜单区。

ts
const primaryCollapse = computed(() => {
  return settings.navMode !== "mixedBar" && localSettings.collapse
})

这里不是删掉折叠功能,而是改变折叠目标。mixedBar 下更值得折叠的是二级菜单区,因为它才是真正占宽度的部分;一级菜单条承担模块导航条角色,稳定显示通常比可折叠更重要。

六、同一个 collapse 在不同模式含义不同

collapse 不是所有模式下都按同一种 UI 效果解释:sidebar 折叠整个左侧主菜单,mixedBar 只收起二级菜单区,top 通常不走左侧折叠逻辑。真正需要抽象的不是「一个布尔值」,而是「当前模式下这个布尔值该作用到哪里」。

ts
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 一级菜单映射

延伸阅读