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:
liuwenxin 2026-10-23 16:21:19 +08:00
parent 61965e0b60
commit a00fca942d
4 changed files with 130 additions and 80 deletions

View File

@ -5,7 +5,12 @@
<title>衍水工资系统 · 视觉方向预览</title>
<style>
/* ============================================================
设计体系 v4.1(同步已交付模块 + 实测补订):中性灰白 · 靛蓝点缀 · 微质感
设计体系 v4.2(侧栏层级口径改版):中性灰白 · 靛蓝点缀 · 微质感
v4.1 → v4.2 变更(2026-10-23):侧栏层级口径改版 + 工作台动态分页,不动色板/字体/组件基调
1) 组标题与菜单项同字号 13px —— 层级靠缩进与留白,不再靠字重压过
2) 去掉组标题的靛蓝竖标与激活项左侧竖条,激活只留白底浮起 + 靛蓝字
3) 子项缩进收到 12px,菜单图标父子统一 16px
4) §05 最近动态改服务端分页(每页 10/20/50 条 + 模块筛选 + 分页条)
v3 → v4 变更(2026-10-22):设计稿与已交付功能对齐
1) 侧栏菜单改为真实路由分组(5 组 + 首页 = 21 项),组可折叠
2) 首页工作台按真实四区块重画(总览 / 待办 / 预警 / 动态)
@ -264,10 +269,8 @@
background: linear-gradient(180deg, #ffffff, #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);
}
.menu-item.active::before {
content: ''; position: absolute; left: -14px; top: 8px; bottom: 8px; width: 2px;
border-radius: 1px; background: var(--ys-indigo);
}
/* 子项缩进一档(12px):层级是位置关系,不是字号关系 */
.menu-group-head ~ .menu-item { padding-left: 22px; }
.menu-item svg { width: 16px; height: 16px; flex: none; opacity: 0.7; }
.menu-item.active svg { opacity: 1; }
.aside-foot { margin-top: auto; padding: 0 10px 2px; }
@ -423,14 +426,17 @@
.login-btn { width: 100%; height: 38px; margin-top: 6px; font-size: 14px; }
/* ---------- 菜单分组(路由 meta.group 派生的可折叠组) ---------- */
/*
* 层级口径(v4.2):父子同字号 13px —— 组标题与条目一样大,
* 结构靠「折叠箭头 + 组间留白 + 子项缩进一档」表达,不靠字号压过。
*/
.menu-group-head {
display: flex; align-items: center; justify-content: space-between;
padding: 9px 10px; margin-bottom: 4px; border-radius: var(--ys-radius-sm);
font-size: 11px; letter-spacing: 0.1em; color: var(--ys-ink-3);
font-size: 13px; font-weight: 500; color: var(--ys-ink);
}
.menu-group.open .menu-group-head { color: var(--ys-indigo); background: rgba(255, 255, 255, 0.45); }
.menu-group-head .chev { width: 11px; height: 11px; flex: none; opacity: 0.55; }
.menu-group.open .menu-group-head .chev { opacity: 0.9; transform: rotate(90deg); }
.menu-group-head .chev { width: 11px; height: 11px; flex: none; opacity: 0.55; color: var(--ys-ink-3); }
.menu-group.open .menu-group-head .chev { opacity: 0.9; color: var(--ys-ink-2); transform: rotate(90deg); }
/* ---------- 工作台卡片内的列表行 ---------- */
.b-count {
@ -1039,7 +1045,9 @@
<tr><td>不进菜单</td><td>报表打印视图(独立顶层路由)</td><td class="num">浏览器打印只出报表本身</td></tr>
</table>
<div class="note">
侧栏是<b>单列树</b>:组标题可折叠,当前页所在组默认展开,当前项白底浮起 + 左侧 2px 靛蓝竖条。
侧栏是<b>单列树</b>:组标题可折叠,当前页所在组默认展开,当前项白底浮起 + 靛蓝字(不再叠左侧竖条)。
<b>层级口径(v4.2)</b>:父子<b>同字号 13px</b>,组标题不靠字号/字重压过子项——
结构由「折叠箭头 + 组间留白 + 子项缩进 12px」表达;此前的 11px 目录行版与 14px/600 竖标版都不采用。
菜单图标(16px、静止 0.7 / 激活 1、组标题不加图标)已于 2026-10-22 落地:
路由 <code>meta.icon</code> 只写语义 key,图标注册在 <code>menuIcons.ts</code>。
本稿画的是手绘线性稿,实际取自 Element Plus 图标库的线性变体——<b>形状以图标库为准</b>,
@ -1178,9 +1186,14 @@
<div class="b-eyebrow">最近动态
<span style="margin-left:auto;font-size:11px;color:var(--ys-ink-3);border:1px solid var(--ys-line);border-radius:var(--ys-radius-sm);padding:1px 8px;background:#fff">全部模块 ▾</span>
</div>
<div class="todo-list" style="max-height:40px;margin-top:4px">
<div class="todo-list" style="margin-top:4px">
<div class="b-act"><span class="tm">10-22 09:12</span><span class="b-tag b-tag-info">核算 · 月结</span><span class="sum">生成 2026-10 月结草稿,绩效合计 268,430.16 元</span><span class="us">张岚</span></div>
<div class="b-act"><span class="tm">10-21 16:40</span><span class="b-tag b-tag-info">回款 · 新增</span><span class="sum">录入回款 86,400.00 元(业务 BS20261018)</span><span class="us">王浩</span></div>
<div class="b-act"><span class="tm">10-21 14:32</span><span class="b-tag b-tag-info">业务 · 新增</span><span class="sum">创建业务 YW20261022-0001(客户ID=166,明细 1 行)</span><span class="us">王浩</span></div>
</div>
<div class="pager" style="margin-top:10px;justify-content:flex-end">
<span class="pg">‹</span><span class="pg cur">1</span><span class="pg">2</span><span class="pg">›</span>
<span style="margin-left:10px">共 128 条 · 每页 10 条</span>
</div>
</div>
@ -1204,6 +1217,8 @@
待办与预警并排 2×2 → 最近动态与常用操作整行铺满(<code>b-full</code>)。
卡片入场 <b>45ms 交错上浮(320ms)</b>,悬停抬升 2px、影加深。
待办与预警的列表<b>在卡内滚动</b>(上限 178px)——条目再多也不撑开网格,行高恒定。
<b>最近动态走服务端分页</b>(2026-10-23):列表不再靠卡内滚动藏条目,每页 10/20/50 条,
底部接右对齐分页条(与列表页同一套 <code>AppPagination</code>);模块筛选同步到服务端。
空态(<code>b-empty</code>)在卡片剩余高度内<b>垂直居中</b>,不全部挤在顶部留一整块空白。
</div>
</section>

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;
}
}

View File

@ -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;
}

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>