refactor(frontend): 侧栏层级与表单页版式调整

侧栏层级口径改版(视觉稿 v4.2):组标题与菜单项同字号 13px,层级改由
「折叠箭头 + 组间留白 + 子项缩进」表达,不靠字号/字重压过父级。
- 去掉组标题的靛蓝竖标与激活项左侧 2px 竖条(竖条只能落在侧栏内边缘、
  与白底悬空,是重复的第二个信号),激活只留白底浮起 + 靛蓝字
- 子项缩进 30px 收到 22px,图标父子统一 16px
- AppMenu 的 .app-menu 提供横向 10px 留白,BasicLayout 的 .app-aside__menu
  只写上下内距——两处都用 padding 简写会同权重靠注入顺序决胜,横向留白
  会被整条抹掉、条目贴到侧栏边缘

公司信息页字段自适应分列:卡片够宽时两字段并排,窄时折回单列;说明文字
紧贴控件下方,不再顶到卡片最右缘(宽屏下与输入框隔上千像素,视线关联断掉)。

设计稿同步升 v4.2。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
liuwenxinandClaude Code committed 2026-10-23 16:21:19 +08:00
1 parent 61965e0b60
commit a00fca942d
4 files changed
+130 -80

No files matched your search

+86 -59
View File
@@ -10,7 +10,12 @@ const { topLevelMenus, groupedMenus } = useAppMenu()
<template>
<el-menu class="app-menu" :default-active="route.path" router>
<el-menu-item v-for="item in topLevelMenus" :key="item.path" :index="item.path">
<el-menu-item
v-for="item in topLevelMenus"
:key="item.path"
class="app-menu__top"
:index="item.path"
>
<el-icon v-if="item.icon" class="app-menu__icon" aria-hidden="true">
<component :is="item.icon" />
</el-icon>
@@ -32,69 +37,67 @@ const { topLevelMenus, groupedMenus } = useAppMenu()
<style scoped>
/*
* 中性灰侧栏上的菜单:覆盖 Element Plus 的主题变量而非直接写颜色 ——
* 后者与组件库规则同权重,受样式注入顺序影响而不稳定。
* 激活项 = 白底浮起(微渐变 + 内高光 + 分层阴影)+ 左侧 2px 靛蓝条。
* 侧栏菜单:中性灰底 + 靛蓝点缀。
* 覆盖 Element Plus 的主题变量而非直接写颜色 —— 后者与组件库规则同权重,
* 受样式注入顺序影响而不稳定。
*
* 层级口径:父子**同字号(13px)**,层级不靠字号/字重刻意压过 ——
* 组标题与条目一样大,结构由「组标题带折叠箭头 + 组间留白 + 子项缩进一档」承担。
* 前两版各走偏一次:先把组标题压成 11px 弱墨「目录行」,父级弱于子项;
* 后改成 14px/600 + 靛蓝竖标,父级刻意压过子项、像一排小标题在喊叫。
* 层级是位置关系,不是字号关系 —— 两版都不要。
*
* 激活态:白底浮起(微渐变 + 内高光 + 分层阴影)+ 靛蓝字,不再叠左侧竖条 ——
* 竖条只能落在侧栏内边缘(离白底悬空),是与白底重复的第二个信号。
*/
.app-menu {
--el-menu-bg-color: transparent;
--el-menu-text-color: var(--ys-ink-2);
--el-menu-active-color: var(--ys-indigo);
--el-menu-hover-bg-color: rgba(255, 255, 255, 0.55);
--el-menu-item-height: 36px;
--el-menu-item-height: 34px;
padding: 0 10px;
padding: 2px 10px 12px;
border-right: none;
}
/* 菜单项:内距 9px 10px、圆角 7px、13px 字 —— 组内层级靠缩进区分,不加分隔线 */
.app-menu :deep(.el-menu-item) {
height: 36px;
/* 组与组之间留白:分组交给间距,不加分割线 */
.app-menu :deep(.el-sub-menu) {
margin-top: 12px;
}
/*
* 顶层叶子项(首页):与组标题同级 —— 13px / 500 / 主墨。
* 用 scoped 模板 class 定位(权重高于组件库默认样式),
* 不与子项共用 :deep 规则,避免字号被子项规则带走。
*/
.app-menu__top {
height: 34px;
padding: 0 10px;
margin-bottom: 2px;
font-size: 13px;
line-height: 36px;
font-weight: 500;
line-height: 34px;
color: var(--ys-ink);
border-radius: var(--ys-radius-sm);
transition:
background-color 150ms var(--ys-ease),
color 150ms var(--ys-ease);
}
.app-menu :deep(.el-menu-item:hover) {
.app-menu__top:hover {
color: var(--ys-ink);
background-color: rgba(255, 255, 255, 0.55);
}
/* 子项向右缩进一档,与顶层项形成「树」的暗示而非并列 */
.app-menu :deep(.el-sub-menu .el-menu-item) {
padding-left: 22px !important;
}
.app-menu :deep(.el-menu-item.is-active) {
position: relative;
font-weight: 500;
background: linear-gradient(180deg, var(--ys-paper), #fbfcfe);
box-shadow:
inset 0 1px 0 #fff,
0 1px 2px rgba(28, 35, 51, 0.06),
0 4px 10px rgba(28, 35, 51, 0.06);
}
.app-menu :deep(.el-menu-item.is-active)::before {
content: '';
position: absolute;
top: 9px;
bottom: 9px;
left: -10px;
width: 2px;
border-radius: 1px;
background: var(--ys-indigo);
}
/* 组标题:11px 大字距的「目录行」,不抢子项;展开时转靛蓝并压一层白底 */
/* 组标题:与条目同字号,靠折叠箭头表明「可展开」,不靠压重 */
.app-menu :deep(.el-sub-menu__title) {
height: 32px;
height: 34px;
padding: 0 10px;
line-height: 32px;
font-size: 13px;
font-weight: 500;
line-height: 34px;
color: var(--ys-ink);
border-radius: var(--ys-radius-sm);
transition:
background-color 150ms var(--ys-ease),
@@ -102,23 +105,47 @@ const { topLevelMenus, groupedMenus } = useAppMenu()
}
.app-menu__group {
font-size: 11px;
font-size: 13px;
font-weight: 500;
letter-spacing: 0.1em;
color: var(--ys-ink-3);
transition: color 150ms var(--ys-ease);
color: inherit;
}
/*
* 子项:13px 次墨 + 缩进一档(12px)。
* Element Plus 的 `.el-menu--inline .el-menu-item` 带 !important 的 40px 缩进,
* 必须同样用 !important 才能压住,否则缩进档位不受控。
*/
.app-menu :deep(.el-sub-menu .el-menu-item) {
height: 32px;
padding-right: 10px !important;
padding-left: 22px !important;
margin-bottom: 1px;
font-size: 13px;
line-height: 32px;
color: var(--ys-ink-2);
border-radius: var(--ys-radius-sm);
transition:
background-color 150ms var(--ys-ease),
color 150ms var(--ys-ease);
}
.app-menu :deep(.el-sub-menu .el-menu-item:hover) {
color: var(--ys-ink);
}
/* 激活项:白底浮起 + 靛蓝字(阴影分层与设计稿卡片同口径) */
.app-menu :deep(.el-menu-item.is-active) {
font-weight: 500;
color: var(--ys-indigo);
background: linear-gradient(180deg, var(--ys-paper), #fbfcfe);
box-shadow:
inset 0 1px 0 #fff,
0 1px 2px rgba(28, 35, 51, 0.06),
0 4px 10px rgba(28, 35, 51, 0.06);
}
.app-menu :deep(.el-sub-menu__title:hover) {
background-color: rgba(255, 255, 255, 0.55);
}
.app-menu :deep(.el-sub-menu.is-opened > .el-sub-menu__title) {
background-color: rgba(255, 255, 255, 0.45);
}
.app-menu :deep(.el-sub-menu.is-opened > .el-sub-menu__title) .app-menu__group {
color: var(--ys-indigo);
background-color: rgba(255, 255, 255, 0.5);
}
/*
@@ -136,21 +163,21 @@ const { topLevelMenus, groupedMenus } = useAppMenu()
}
.app-menu :deep(.el-sub-menu.is-opened > .el-sub-menu__title .el-sub-menu__icon-arrow) {
color: var(--ys-indigo);
color: var(--ys-ink-2);
transform: rotate(0deg);
}
/*
* 菜单图标:设计稿口径为 16px、透明度 0.7(激活项 1)——Element Plus 默认
* 给菜单图标 24px 宽 + 5px 间距,直接沿用会让图标比文字更抢眼、字距发散,
* 故在此显式覆盖。
* 故在此显式覆盖。父子同级,图标尺寸统一,不再按层级分档。
*/
.app-menu__icon {
width: 16px;
height: 16px;
margin-right: 9px;
font-size: 16px;
opacity: 0.7;
opacity: 0.72;
}
.app-menu :deep(.el-menu-item.is-active) .app-menu__icon {
@@ -158,10 +185,10 @@ const { topLevelMenus, groupedMenus } = useAppMenu()
}
@media (prefers-reduced-motion: reduce) {
.app-menu :deep(.el-menu-item),
.app-menu__top,
.app-menu :deep(.el-sub-menu .el-menu-item),
.app-menu :deep(.el-sub-menu__title),
.app-menu :deep(.el-sub-menu__icon-arrow),
.app-menu__group {
.app-menu :deep(.el-sub-menu__icon-arrow) {
transition: none;
}
}
@@ -155,7 +155,13 @@ async function handleLogout(): Promise<void> {
.app-aside__menu {
flex: 1;
padding: 2px 0 12px;
/*
* 只写上下内距:横向 10px 留白由 AppMenu 自己的 .app-menu 提供。
* 这里若用 padding 简写,会与 .app-menu 的 padding 简写同权重、
* 靠样式注入顺序决胜 —— 横向留白可能被整条抹掉,条目贴到侧栏边缘。
*/
padding-top: 2px;
padding-bottom: 12px;
overflow-y: auto;
}
+11 -9
View File
@@ -118,9 +118,13 @@ const previewName = computed(() => form.company_name.trim() || '衍水集团')
label-width="92px"
@submit.prevent
>
<el-form-item label="公司名称" prop="company_name">
<!-- 控件与说明并排:说明靠行尾对齐,宽屏下整行铺满卡片而非缩在左上角 -->
<div class="field-control">
<!--
字段自适应分列:卡片够宽时两个字段并排(各占一列),窄时自动折回单列。
说明文字紧贴控件下方 —— 早前把它顶到卡片最右缘,宽屏下与输入框隔了
上千像素,视线关联直接断掉。
-->
<div class="field-grid">
<el-form-item label="公司名称" prop="company_name">
<el-input
v-model="form.company_name"
class="field-control__main"
@@ -129,11 +133,9 @@ const previewName = computed(() => form.company_name.trim() || '衍水集团')
clearable
/>
<p class="field-control__note">最多 64 个字符;保存后对所有在线用户立即生效。</p>
</div>
</el-form-item>
</el-form-item>
<el-form-item label="logo">
<div class="field-control">
<el-form-item label="logo">
<el-upload
class="logo-uploader"
:show-file-list="false"
@@ -149,8 +151,8 @@ const previewName = computed(() => form.company_name.trim() || '衍水集团')
<p class="field-control__note">
png / jpg,不超过 2MB;仅展示于登录页品牌位,按 ≤56×36 显示。
</p>
</div>
</el-form-item>
</el-form-item>
</div>
</el-form>
</div>