旧模板的 193 个文件整体删除,换成纯 Vue 3.5 + Naive UI 2.42 的树。
- 去掉 auto-import(组件与 API 全显式 import)、v-hasPerm(改 user.has('码'),全库 8 处)、
Stylelint;n-tag 的 type 统一过 utils/tag.ts 转换(Arco 色词直接透传会静默变灰)
- 图标改为构建期抽取:scripts/extract-pc-icons.py -> icons/icon-data.ts(37 个),
内网离线部署不会白屏
- 重搭后修掉 6 类"编译 / lint / typecheck 全绿、界面上就是坏的"故障:
① NGrid 的 cols 写成像素值(不是断点名 -> 永远只 1 列,工作台因此错乱)
② n-tab-pane 用 tab 属性(只渲染空注释节点,整排 tab 一个字都不显示)
③ 漏 import 被当未知自定义元素(样式整块丢) ④ .n-card__content 类名不存在
(高度链第一环失效,表格行在 DOM 里但屏幕上没有) ⑤ 填报单状态映射错位一格
⑥ 有标题的卡片没有上内边距(naive 的 padding-top 只给"内容区是长子"的卡片)
- 卡片式版面提成全局基线:styles/index.scss 的 xt-* 令牌 + .page-card / .toolbar /
.section-title,页面只引用、不再各写一套
- 品牌:改名「深学在线」,文字标换成几何标(方 = 卷宗 / 环 = 在线,不用文字字形);
index.html 的页签图标从 404 的 /favicon.ico 改指真实存在的 /favicon.svg
7.0 KiB
name, description
| name | description |
|---|---|
| code-css | SCSS 写法、样式作用域与全局样式的边界。编写或修改 .vue 的 style 块、.scss 文件时加载本技能。 |
样式规范(SCSS + Naive UI)
⚠️ Stylelint 已随 2026-10 重搭移除(
pnpm lint:style不存在了)。样式只受 ESLint 与人工约定约束。
放哪里
| 场景 | 位置 |
|---|---|
| 组件的具体样式 | 该 SFC 的 <style scoped lang="scss"> |
| 跨页面的版面基线(背景、卡片外观、卡片间距、滚动、分页器容器、区块标题) | src/styles/index.scss(只放跨页面的东西) |
| 组件外观的主题变量(Card / Button / DataTable 的描边、圆角、阴影) | src/App.vue 的 themeOverrides |
设计基线(2026-10 卡片式改造后:页面只引用,不再各写一套)
src/styles/index.scss 的 :root 里定义,页面直接 var():
| 变量 | 值 | 用途 |
|---|---|---|
--xt-header-h |
56px |
顶栏高度(.app-shell__header 与 .page-card 高度算式共用) |
--xt-content-pad |
16px |
内容区内边距(同上,两处共用,别写死数值) |
--xt-gap |
16px |
卡片/区域之间的间距 |
--xt-bg |
#f2f4f7 |
页面底色(卡片要靠它才浮得起来) |
--xt-card-border / --xt-card-border-weak |
#e5e8ee / #eef1f5 |
卡片描边 / 嵌套卡片描边 |
--xt-card-radius |
10px |
卡片圆角 |
--xt-card-pad-x / --xt-card-pad-y |
18px / 16px |
卡片内容区左右 / 上下内边距(别在页面里写死,见下) |
--xt-band-bg |
#f7f8fa |
浅灰色带:卡片头 / 表头 / 卡片内的子格子 |
--xt-text / --xt-text-2 / --xt-text-3 |
#1f2229 / #667085 / #98a2b3 |
主 / 次 / 弱文字 |
⚠️ 卡片内边距:不要让页面自己写
Naive 的规则是 cB("card-content", "padding: 0 var(--n-padding-left) var(--n-padding-bottom) …")
外加 c("&:first-child", "padding-top: var(--n-padding-top)") ——
padding-top 只给"内容区是长子"(也就是没有卡片头)的卡片。
所以有标题的卡片,内容区 padding-top 是 0,第一行内容直接贴在标题下面那条灰条上。
全局 .n-card.n-card > .n-card-content { padding: var(--xt-card-pad-y) var(--xt-card-pad-x) }
已经统一处理掉了。但它是普通样式,会被内联样式盖掉 ——
页面里写 :content-style="{ padding: '12px' }" 会让那张卡片比邻居紧一圈
(而且那个写法只解决左右,不管上面这件事)。要调内边距就改 --xt-card-pad-x/y。
📌 排查手法:
getComputedStyle(card.querySelector(':scope > .n-card-content')).paddingTop应当是16px;是0说明没匹配上,是别的值说明有内联样式或更具体的选择器在盖。
现成的全局类(优先复用,不要另起名字):
| 类 | 用途 |
|---|---|
.page-plain |
普通(非列表)页根节点:flex 列 + --xt-gap 间距 |
.page-card + .page-card__body |
列表页专用高度链:卡片撑满内容区 → 表体自滚 → 分页器钉底 |
.toolbar / .toolbar--plain |
筛选与动作行(自带下分隔线 / 去掉分隔线) |
.toolbar__hint |
工具条里的灰色小字提示 |
.section-title + .section-title__note |
卡片内部次级区块标题(左侧蓝色小竖条 + 右侧灰色说明) |
卡片式的三条规矩
- 卡片外观是全局给的:
.n-card.n-card统一白底 + 描边 + 轻阴影 + 卡片头浅灰色带 (权重写成.n-card.n-card是因为 naive 的样式由 css-render 运行时注入,先后顺序不保证)。 页面不要再自己给卡片写边框/阴影。 - 卡里不要套卡:卡片内部要分区时,用浅灰底(
--xt-band-bg)+ 弱描边(--xt-card-border-weak) 的小格子,或.section-title+1px分隔线。嵌套的NCard已被全局降级为无阴影 + 无色带, 但布局上仍然容易糊成一片。 - 区域间隔靠"色带 + 描边",不是靠加大 margin:卡片之间
--xt-gap,卡片内区块 12~20px。 上一版的教训是把卡片写成:bordered="false"+ 内容区白底 → 白卡片浮在白底上,整页没有任何边界。
按钮的可见性
- 页面主操作(新建 / 导入 / 批量通过…)用
type="primary",默认尺寸(不要size="small")。 - 同一行内控件尺寸必须一致:一行里混用
small(28px) 与默认(34px) 会参差不齐。 工具条统一默认尺寸;表格行内动作保持text+size="small"。 - 默认按钮的描边已在
App.vue里加深(#d0d5dd)—— 原来用的common.borderColor(#e0e0e6) 在白色卡片上几乎看不见,这是"按钮不明显"的主因。 - ⚠️ naive 的按钮边框画在子元素
.n-button__border上,不是根节点; 用getComputedStyle(btn).borderTopWidth检查会得到0px,别据此判断"边框没生效"。
覆盖 naive 内部类之前:先确认真实类名
⚠️ naive 的类名不是一套统一规则,同一个组件里两种命名混着用:
| 真实类名 | 由什么生成 |
|---|---|
.n-card-header / .n-card-content |
cB("card-header") / cB("card-content") —— 单短横 |
.n-button__border / .n-button__content |
cE("border") / cE("content") —— 双下划线 |
所以"想当然按 BEM 写"会静默失效::deep(.n-card__content) 匹配不到任何元素,
不报错、不影响编译,只是那条样式什么也没做。
2026-10 实际踩到:
.page-card的高度链第一环(卡片内容区要是能撑满的 flex 列) 就写在:deep(.n-card__content)里 —— 结果「操作日志」的表格体高度算成 0, 行在 DOM 里、数据也查得到,屏幕上一行都看不见。正确做法:在 DevTools 里选中那个元素,看它实际的 class;或者去
node_modules/naive-ui/es/<组件>/src/styles/index.cssr.mjs里搜一眼。 顺带:.n-card-content与.n-card-header这两个名字已经被styles/index.scss用到, 改卡片相关样式时以那里为准。
约定
- class 用 kebab-case;同一块的子元素用
块__元素风格(task__tabs、join__hint), 与现有页面保持一致即可,不要引入新的方法论或工具链。 - 颜色优先用 Naive UI 的 CSS 变量 / 主题变量,不要写死 Arco 时代的
--arco-*变量(已不存在)。 - 选择器保持扁平(≤2 层),避免
!important;确实要覆盖 naive 内部类时加注释说明原因。
踩过的坑(照抄现有写法即可)
- 列表页卡片要能自己滚:分页器被裁出可视区是本项目反复踩的问题 ——
列表页统一用
styles/index.scss里的.page-card容器(卡片撑满内容区 → 表体自己滚 → 分页器钉在卡片底部)。 - 页面根节点用
NPagination/NDataTable时别自己写overflow: hidden,会把分页器裁掉。