5b877451eb
旧模板的 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
113 lines
7.0 KiB
Markdown
113 lines
7.0 KiB
Markdown
---
|
||
name: code-css
|
||
description: 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` | 卡片**内部**次级区块标题(左侧蓝色小竖条 + 右侧灰色说明) |
|
||
|
||
## 卡片式的三条规矩
|
||
|
||
1. **卡片外观是全局给的**:`.n-card.n-card` 统一白底 + 描边 + 轻阴影 + 卡片头浅灰色带
|
||
(权重写成 `.n-card.n-card` 是因为 naive 的样式由 css-render **运行时注入**,先后顺序不保证)。
|
||
页面**不要再**自己给卡片写边框/阴影。
|
||
2. **卡里不要套卡**:卡片内部要分区时,用浅灰底(`--xt-band-bg`)+ 弱描边(`--xt-card-border-weak`)
|
||
的小格子,或 `.section-title` + `1px` 分隔线。嵌套的 `NCard` 已被全局降级为无阴影 + 无色带,
|
||
但布局上仍然容易糊成一片。
|
||
3. **区域间隔靠"色带 + 描边",不是靠加大 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 内部类时**加注释说明原因**。
|
||
|
||
## 踩过的坑(照抄现有写法即可)
|
||
|
||
1. **列表页卡片要能自己滚**:分页器被裁出可视区是本项目反复踩的问题 ——
|
||
列表页统一用 `styles/index.scss` 里的 `.page-card` 容器(卡片撑满内容区 → 表体自己滚 → 分页器钉在卡片底部)。
|
||
2. 页面根节点用 `NPagination`/`NDataTable` 时别自己写 `overflow: hidden`,会把分页器裁掉。
|