/** * 页面顶栏的统一样式,代理 / 连接 / 规则 / 日志四个页面共用(见 PageHeader.tsx)。 * * 断点约定: * >1024 一行排下:标题 · 分段标签 · 搜索 · 操作钮 * ≤1024 搜索换到第二行,操作钮靠 margin 顶到第一行右端 * ≤768 顶栏拉平到页面底色(窄屏顶部已经有一条不透明的导航条, * 顶栏再铺一层外壳色会夹出第三条色带),按钮退化成正方形图标钮, * 凹陷的控件(分段标签轨道、输入框)翻成浮起 */ .header { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; gap: 12px; padding: 20px 32px 15px; // 半透明外壳色 + 毛玻璃 + 下边框:内容从底下滚过去时能透出一点, // 同时比纯白的卡片灰一档。弹层都 portal 到 body, // 不受 backdrop-filter 生成包含块的影响 background: var(--color-chrome); backdrop-filter: saturate(180%) blur(20px); border-bottom: 1px solid var(--color-chrome-border); @media (max-width: 1024px) { flex-wrap: wrap; padding: 16px 16px 12px; } @media (max-width: 768px) { gap: 8px; padding: 10px 12px; background: var(--color-background); backdrop-filter: none; } } .title { margin: 0; font-size: 1.6rem; font-weight: 700; line-height: 1.2; letter-spacing: -0.01em; color: var(--color-text-highlight); white-space: nowrap; @media (max-width: 1024px) { font-size: 1.35rem; } @media (max-width: 768px) { font-size: 1.1rem; } } /** 窄屏下强制换行的占位,让它后面的东西独占下一行 */ .rowBreak { display: none; @media (max-width: 768px) { display: block; flex-basis: 100%; height: 0; } } /* ---------- 分段标签 ---------- */ .tabs { display: flex; align-items: center; gap: 2px; padding: 4px; border-radius: 12px; background: var(--color-track); flex-shrink: 0; // 顶栏在窄屏拉平到页面底色后,--color-track 就是底色本身,凹槽会整个消失。 // 这里去掉凹槽,改成「选中项浮起来」的样式,和导航胶囊、卡片同一套语言 @media (max-width: 768px) { padding: 0; gap: 4px; background: none; } } .tab { display: inline-flex; align-items: center; gap: 7px; appearance: none; border: none; background: transparent; font-family: inherit; font-size: 0.88rem; font-weight: 500; color: var(--color-text-secondary); padding: 7px 14px; border-radius: 9px; cursor: pointer; white-space: nowrap; transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease; &:hover:not(.tabActive) { color: var(--color-text); background: var(--color-hover-soft); } &:focus-visible { outline: 2px solid var(--color-focus-blue); outline-offset: -1px; } @media (max-width: 768px) { gap: 5px; padding: 5px 10px; font-size: 0.8rem; } } .tabActive { background: var(--color-card); color: var(--color-text-highlight); box-shadow: var(--shadow-segment); @media (max-width: 768px) { border: 1px solid var(--color-card-border); // 补掉边框占掉的 1px,选中和未选中的高度才对得齐 padding: 4px 9px; } } .tabCount { font-family: var(--font-mono); font-size: 0.72rem; font-weight: 500; color: var(--color-text-secondary); // 小屏上标题 + 标签 + 几个操作钮刚好挤满一行,计数是这里最先能舍的东西 @media (max-width: 400px) { display: none; } } /* ---------- 搜索 ---------- */ .search { position: relative; display: flex; align-items: center; flex: 1 1 240px; min-width: 160px; max-width: 340px; margin-left: auto; // 同时覆盖裸 和 TextFilter 自带的样式,两种用法长得一样 input { width: 100%; height: 38px; padding: 0 12px 0 38px; border-radius: 10px; border: 1px solid var(--color-card-border); background: var(--color-track); box-shadow: none; color: var(--color-text); font: inherit; font-size: 0.85rem; outline: none; appearance: none; &::placeholder { color: var(--color-text-secondary); opacity: 0.75; } &:focus { border-color: var(--color-focus-blue); } } @media (max-width: 1024px) { flex-basis: 100%; min-width: 0; max-width: none; margin-left: 0; } @media (max-width: 768px) { input { height: 34px; padding-left: 34px; font-size: 0.8rem; // 同分段标签:底色拉平后凹陷的输入框会看不见,改成和卡片同层浮起 background: var(--color-card); } } } /** * 搜索旁边还要并排放别的控件(下拉筛选)时叠这个类: * 窄屏下别独占整行,把剩下的宽度让出来 */ .searchInline { @media (max-width: 768px) { flex: 1 1 auto; flex-basis: auto; min-width: 0; } } .searchIcon { position: absolute; left: 13px; color: var(--color-text-secondary); pointer-events: none; @media (max-width: 768px) { left: 11px; } } /* ---------- 下拉筛选 ---------- */ // 叠在 Select 组件自带样式之上,把它拉齐到顶栏这套尺寸。 // 类名写两遍是为了翻倍特异性——Select 自己的 .select 也是单类, // 靠打包顺序决定胜负太脆 .select.select { height: 38px; width: auto; flex-shrink: 0; border-radius: 10px; border-color: var(--color-card-border); background-color: var(--color-track); box-shadow: none; font-size: 0.85rem; @media (max-width: 768px) { height: 34px; font-size: 0.8rem; background-color: var(--color-card); } } /* ---------- 操作按钮 ---------- */ .actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; // 搜索换到第二行后,靠 margin 把操作钮顶到第一行右端 @media (max-width: 1024px) { margin-left: auto; } @media (max-width: 768px) { gap: 6px; } } .btn { display: inline-flex; align-items: center; gap: 6px; appearance: none; height: 38px; padding: 0 15px; border-radius: 10px; border: 1px solid transparent; font-family: inherit; font-size: 0.85rem; font-weight: 500; cursor: pointer; white-space: nowrap; transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, opacity 0.15s ease; &:focus-visible { outline: 2px solid var(--color-focus-blue); outline-offset: 2px; } // 窄屏下文字全部收起,按钮退化成正方形图标钮 @media (max-width: 768px) { gap: 0; width: 34px; height: 34px; padding: 0; justify-content: center; } } .btnGhost { background: var(--color-card); border-color: var(--color-card-border); color: var(--color-text); &:hover { border-color: var(--color-focus-blue); color: var(--color-focus-blue); } } .btnPrimary { background: var(--color-focus-blue); color: #fff; &:hover:not(:disabled) { filter: brightness(1.08); } &:disabled { cursor: default; } } .btnDanger { background: var(--color-danger); color: #fff; &:hover { filter: brightness(1.08); } } .btnPaused { background: var(--color-warn-soft-bg); color: var(--color-warn); border-color: transparent; } .btnBusy { opacity: 0.75; } /** 中等宽度就收起的文字,让给更重要的按钮 */ .btnText { @media (max-width: 1200px) { display: none; } } /** 比 .btnText 保留得久一些:只有窄屏才收起 */ .btnTextSm { @media (max-width: 768px) { display: none; } } .iconBtn { display: inline-flex; align-items: center; justify-content: center; appearance: none; width: 38px; height: 38px; border-radius: 10px; border: 1px solid var(--color-card-border); background: var(--color-card); color: var(--color-text-secondary); cursor: pointer; transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease; &:hover { border-color: var(--color-focus-blue); color: var(--color-focus-blue); } &:focus-visible { outline: 2px solid var(--color-focus-blue); outline-offset: 2px; } @media (max-width: 768px) { width: 34px; height: 34px; } } .iconBtnActive { background: var(--color-focus-blue); border-color: var(--color-focus-blue); color: #fff; &:hover { color: #fff; } }