refactor(pc): 从 Gi Admin Pro + Arco 重搭为纯 Vue 3 + Naive UI
旧模板的 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
This commit is contained in:
@@ -1,36 +1,112 @@
|
||||
---
|
||||
name: code-css
|
||||
description: Vue SFC 样式块、BEM 命名、Stylelint 与 scoped 约定。编写或修改组件样式(.vue style、scss、less、css)时加载本技能。
|
||||
description: SCSS 写法、样式作用域与全局样式的边界。编写或修改 .vue 的 style 块、.scss 文件时加载本技能。
|
||||
---
|
||||
|
||||
# CSS 规范(Vue 3)
|
||||
# 样式规范(SCSS + Naive UI)
|
||||
|
||||
## SFC 样式块
|
||||
> ⚠️ **Stylelint 已随 2026-10 重搭移除**(`pnpm lint:style` 不存在了)。样式只受 ESLint 与人工约定约束。
|
||||
|
||||
- 位于 `<script>` 之后;默认 **scoped**;全局样式须在注释中说明原因。
|
||||
- 预处理器按项目约定,同一组件内保持一致。
|
||||
## 放哪里
|
||||
|
||||
## 命名(BEM)
|
||||
| 场景 | 位置 |
|
||||
|---|---|
|
||||
| 组件的具体样式 | 该 SFC 的 `<style scoped lang="scss">` |
|
||||
| **跨页面**的版面基线(背景、卡片外观、卡片间距、滚动、分页器容器、区块标题) | `src/styles/index.scss`(**只放跨页面的东西**) |
|
||||
| 组件外观的主题变量(Card / Button / DataTable 的描边、圆角、阴影) | `src/App.vue` 的 `themeOverrides` |
|
||||
|
||||
- 自定义 class:**kebab-case** + **BEM**(`block`、`block__element`、`block--modifier`)。
|
||||
- 一个 SFC 通常一个 Block;元素用 `__`,修饰符用 `--`;**禁止** `block__a__b` 链式元素。
|
||||
- 第三方 UI 类名、全局工具类不在 BEM 约束内。
|
||||
## 设计基线(2026-10 卡片式改造后:**页面只引用,不再各写一套**)
|
||||
|
||||
```scss
|
||||
.user-card {
|
||||
&__avatar { width: 48px; }
|
||||
&__badge--vip { color: var(--color-warning); }
|
||||
}
|
||||
```
|
||||
`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` | 主 / 次 / 弱文字 |
|
||||
|
||||
- 颜色、间距、字号优先复用项目 CSS 变量或设计令牌。
|
||||
- 避免内联 `style`(动态样式除外)和滥用 `!important`。
|
||||
- 遵循项目 Stylelint;改动后视情况运行 `lint:style`。
|
||||
### ⚠️ 卡片内边距:**不要让页面自己写**
|
||||
|
||||
## 禁止
|
||||
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**,第一行内容直接贴在标题下面那条灰条上。
|
||||
|
||||
- ❌ 自定义 class 用 camelCase 或非语义命名
|
||||
- ❌ BEM 元素链式嵌套(`block__a__b`)
|
||||
- ❌ 大段内联 style 或过多 `:deep()` 堆叠
|
||||
全局 `.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`,会把分页器裁掉。
|
||||
|
||||
Reference in New Issue
Block a user