fix(pc): 弹窗遮罩改毛玻璃并清掉 9 处提示折行
遮罩底色被 Naive 写死在 css-render 里(Modal / Drawer 主题没有 mask token, themeOverrides 改不到),只能上 CSS;加 backdrop-filter 后遮罩后面的页面糊掉、 弹层本体仍是纯白,NModal / NDrawer / useDialog 共用 .n-modal-mask 一条规则覆盖。 折行的根因是格子宽度写死(半宽 264px / span 8 192px / 定宽 160px),与窗口宽度无关: 能压短的压短;「文件名预览」那处是 .n-form-item-blank 属于 flex 行、把提示挤成 3 行, 改成包一层块级容器;材料预览失败的占位在 108px 小格里曾折 3 行且被裁掉。 约定写进 frontend-pc/AGENTS.md 第 11 条与 code-css skill;实测与刻意不改的清单见 docs/交接摘要.md 的 §一·四十二。
This commit is contained in:
@@ -30,6 +30,7 @@ description: SCSS 写法、样式作用域与全局样式的边界。编写或
|
||||
| `--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` | 主 / 次 / 弱文字 |
|
||||
| `--xt-mask-bg` / `--xt-mask-blur` | `rgb(16 24 40 / 26%)` / `8px` | 弹窗遮罩的底色与模糊半径(见下节) |
|
||||
|
||||
### ⚠️ 卡片内边距:**不要让页面自己写**
|
||||
|
||||
@@ -67,6 +68,30 @@ Naive 的规则是 `cB("card-content", "padding: 0 var(--n-padding-left) var(--n
|
||||
3. **区域间隔靠"色带 + 描边",不是靠加大 margin**:卡片之间 `--xt-gap`,卡片内区块 12~20px。
|
||||
上一版的教训是把卡片写成 `:bordered="false"` + 内容区白底 → 白卡片浮在白底上,整页没有任何边界。
|
||||
|
||||
## 弹窗遮罩(毛玻璃):**只能用 CSS**,别去找主题变量
|
||||
|
||||
遮罩层(弹窗背后那层灰黑)走毛玻璃:`styles/index.scss` 里一条规则覆盖
|
||||
`NModal` / `NDrawer` / `useDialog()` 三种弹层(`useDialog` 内部就是 `NModal`)。
|
||||
|
||||
```scss
|
||||
.n-modal-mask.n-modal-mask,
|
||||
.n-drawer-mask.n-drawer-mask:not(.n-drawer-mask--invisible) {
|
||||
background-color: var(--xt-mask-bg);
|
||||
backdrop-filter: blur(var(--xt-mask-blur)) saturate(130%);
|
||||
-webkit-backdrop-filter: blur(var(--xt-mask-blur)) saturate(130%);
|
||||
}
|
||||
```
|
||||
|
||||
- ⚠️ **为什么不能用 `App.vue` 的 `themeOverrides`**:Naive 把遮罩色**写死在 css-render 里**
|
||||
(`modal/src/styles/index.cssr.mjs` 的 `modal-mask` = `rgba(0,0,0,.4)`、
|
||||
`drawer/src/styles/index.cssr.mjs` 的 `drawer-mask` = `rgba(0,0,0,.3)`),
|
||||
Modal / Drawer 的**主题里没有 mask token** —— 主题变量改不到它。
|
||||
- ⚠️ **双类名**:与 `.n-card.n-card` 同一招(css-render 运行时注入 `<head>`,先后顺序不保证,把权重提到 (0,2,0))。
|
||||
- ⚠️ **`.n-drawer-mask--invisible`**(`show-mask="transparent"`)必须排除:它是"看不见的遮罩",
|
||||
被染色就变成"看得见的挡板"。`.n-modal-mask` 没有对应的隐藏变体。
|
||||
- 自查:`getComputedStyle(document.querySelector('.n-modal-mask')).backdropFilter`
|
||||
与弹层本体的 `backgroundColor`(应为 `rgb(255, 255, 255)`)。
|
||||
|
||||
## 按钮的可见性
|
||||
|
||||
- 页面**主操作**(新建 / 导入 / 批量通过…)用 `type="primary"`,**默认尺寸**(不要 `size="small"`)。
|
||||
|
||||
@@ -100,3 +100,15 @@ pnpm lint # ESLint(@antfu/eslint-config)
|
||||
⚠️ 这个插槽**不接收任何参数**(内部用 `resolveWrappedSlot(slot, wrapper)` = `wrapper(slot())`),
|
||||
所以**没有**「`#feedback="{ status }"` 里顺手渲染错误」这种写法 —— 想内联显示错误只能给
|
||||
`NFormItem` 的 `feedback` **prop** 传渲染函数,那需要自写包装组件,别顺手试。
|
||||
11. **提示(`#feedback` / `__hint`)要在自己的格子里单行放下**:放不下就**改短**,或把这一项改成
|
||||
`:span="24"`。格子的**可用宽度是写死的**(弹窗宽度写死、两列网格、`span 8`),**窗口拉宽没有用**。
|
||||
2026-10 实测过一轮:提示折成 2~3 行时读起来根本不像一句话(最惨的一处被 flex 挤到 191px、折了 3 行)。
|
||||
12px 字的经验值:`span 12`(620px 弹窗)≈ **23 个汉字**、`span 8`(680px 弹窗)≈ **16 个**、
|
||||
`.uf__grid` 两列 ≈ **21 个**、全宽表单项(520px 弹窗)≈ **39 个**
|
||||
(11px 的 `.org-drawer__fallback` 在 160px 定宽列里 ≈ 14 个)。
|
||||
⚠️ **`.n-form-item-blank` 是 flex 行**(`form-item` 是 grid、blank 区是 flex)→ 同一个 `NFormItem`
|
||||
里放两个元素会被并排压缩;要"提示在上、内容在下"必须包一层块级容器
|
||||
(写法见 `src/views/task/TaskFormModal.vue` 的 `__preview-box`)。
|
||||
⚠️ **弹窗遮罩的毛玻璃只能用 CSS**:Naive 把遮罩色写死在 css-render 里,Modal / Drawer 主题
|
||||
**没有 mask token**,`App.vue` 的 `themeOverrides` 改不到它 —— 规则在 `src/styles/index.scss`,
|
||||
细节见 `.agents/skills/code-css/SKILL.md`。
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
--xt-text: #1f2229;
|
||||
--xt-text-2: #667085;
|
||||
--xt-text-3: #98a2b3;
|
||||
|
||||
/* 弹窗遮罩:比 Naive 原生的 .4/.3 浅一档,模糊才透得出来(见下方「弹窗遮罩」) */
|
||||
--xt-mask-bg: rgb(16 24 40 / 26%);
|
||||
--xt-mask-blur: 8px;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -117,6 +121,31 @@ body {
|
||||
border-bottom-color: var(--xt-card-border-weak);
|
||||
}
|
||||
|
||||
/* ── 弹窗遮罩(毛玻璃) ─────────────────────────────── */
|
||||
|
||||
/**
|
||||
* 三种弹层的遮罩层都变毛玻璃:`NModal`、`NDrawer`、以及 `useDialog()` 的提示框。
|
||||
* **弹层本体保持纯白**,只处理背后那一层。
|
||||
*
|
||||
* ⚠️ 为什么只能用 CSS、改不到 `App.vue` 的 themeOverrides:Naive 把遮罩色**写死在
|
||||
* css-render 里**(`modal/src/styles/index.cssr.mjs` 的 `modal-mask` = `rgba(0,0,0,.4)`、
|
||||
* `drawer/src/styles/index.cssr.mjs` 的 `drawer-mask` = `rgba(0,0,0,.3)`),
|
||||
* Modal / Drawer 的主题里**没有 mask token** —— 主题变量改不到它。
|
||||
* ⚠️ 为什么用双类名:理由同上一条(css-render 运行时注入 <head>,先后顺序不保证),
|
||||
* 与 `.n-card.n-card` 是同一招。
|
||||
* ⚠️ `DialogEnvironment` 里 `useDialog()` 走的是 `NModal`(`dialog/src/DialogEnvironment.mjs`),
|
||||
* 所以它和普通弹窗共用 `.n-modal-mask`,一条规则全覆盖。
|
||||
* ⚠️ `.n-drawer-mask--invisible`(`show-mask="transparent"`)必须排除在外:它是
|
||||
* "看不见的遮罩",被这条规则染上颜色就变成"看得见的挡板"。仓里目前没人用这个属性,
|
||||
* 这个排除是给以后用的。`.n-modal-mask` 没有对应的隐藏变体。
|
||||
*/
|
||||
.n-modal-mask.n-modal-mask,
|
||||
.n-drawer-mask.n-drawer-mask:not(.n-drawer-mask--invisible) {
|
||||
background-color: var(--xt-mask-bg);
|
||||
backdrop-filter: blur(var(--xt-mask-blur)) saturate(130%);
|
||||
-webkit-backdrop-filter: blur(var(--xt-mask-blur)) saturate(130%);
|
||||
}
|
||||
|
||||
/* ── 列表页卡片 ─────────────────────────────────────── */
|
||||
|
||||
/**
|
||||
|
||||
@@ -137,7 +137,7 @@ onMounted(loadNormal)
|
||||
<NSpin v-if="loading" :size="16" />
|
||||
<img v-else-if="src" :src="src" class="fp__img" alt="材料预览" />
|
||||
<div v-else class="fp__fail">
|
||||
预览取不到<br>(链接过期或已删除)
|
||||
预览取不到<br>(链接已过期)
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp__name" :title="fileName">{{ fileName }}</div>
|
||||
|
||||
@@ -247,7 +247,7 @@ defineExpose({ add, edit })
|
||||
<NSelect v-model:value="form.templateCode" :options="templateOptions" />
|
||||
<template #feedback>
|
||||
<span class="rf__hint">
|
||||
复制它的权限码与菜单;建好后在右侧授权矩阵里增删。新角色至少要有一项权限与一个菜单
|
||||
复制它的权限码与菜单。新角色至少要有一项权限与一个菜单
|
||||
</span>
|
||||
</template>
|
||||
</NFormItem>
|
||||
|
||||
@@ -365,8 +365,16 @@ defineExpose({ add, edit })
|
||||
</div>
|
||||
|
||||
<NFormItem label="文件名预览">
|
||||
<span class="task-form__preview-note">以「张同学 / 361029200912100015 / 身份证」为例</span>
|
||||
<code class="task-form__preview">{{ namingPreview }}</code>
|
||||
<!--
|
||||
⚠️ 必须包一层块级容器:`.n-form-item-blank` 是 **flex 行**(form-item 是 grid、
|
||||
blank 区是 flex),直接放两个子元素会被排成同一行、各自被压缩 ——
|
||||
实测这行提示被挤到 191px、折成 3 行("样例"根本读不出是一句话)。
|
||||
包成块级容器后 note 独占一行(容器 603px,需要 312px)。
|
||||
-->
|
||||
<div class="task-form__preview-box">
|
||||
<span class="task-form__preview-note">以「张同学 / 361029200912100015 / 身份证」为例</span>
|
||||
<code class="task-form__preview">{{ namingPreview }}</code>
|
||||
</div>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
|
||||
@@ -415,6 +423,15 @@ defineExpose({ add, edit })
|
||||
color: #8a8a8a;
|
||||
}
|
||||
|
||||
/**
|
||||
* 样例提示 + 文件名预览:**必须整体占满 blank 区**。
|
||||
* `.n-form-item-blank` 是 flex 行,不包这一层的话两个子元素会被并排压缩
|
||||
* (实测提示被挤到 191px、折成 3 行)。
|
||||
*/
|
||||
&__preview-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__preview-note {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
|
||||
@@ -619,7 +619,7 @@ defineExpose({ open })
|
||||
<NInput v-model:value="extraForm.key" placeholder="extra_xxx" :maxlength="46" clearable />
|
||||
<template #feedback>
|
||||
<span class="fd__hint fd__hint--inline">
|
||||
只能用小写字母、数字、下划线;改动后导出列名会跟着变
|
||||
只能用小写字母、数字、下划线;导出列名跟着变
|
||||
</span>
|
||||
</template>
|
||||
</NFormItemGi>
|
||||
|
||||
@@ -573,8 +573,7 @@ defineExpose({ open })
|
||||
<NDynamicTags v-model:value="form.aliases" :max="6" />
|
||||
<template #feedback>
|
||||
<span class="mat-form__hint">
|
||||
输入后回车,如:身份证正面、身份证人像面。
|
||||
用于从历史文件名里认出这是哪一项材料 —— 别名不全,该认出来的就认不出来
|
||||
输入后回车,如:身份证正面、身份证人像面。用于从历史文件名认出材料,别名不全就认不出来
|
||||
</span>
|
||||
</template>
|
||||
</NFormItemGi>
|
||||
@@ -622,7 +621,7 @@ defineExpose({ open })
|
||||
<NFormItemGi :span="8" label="压缩档位" path="compressProfile">
|
||||
<NSelect v-model:value="form.compressProfile" :options="profileOptions" />
|
||||
<template #feedback>
|
||||
<span class="mat-form__hint">档位决定分辨率与清晰度,一般不用改</span>
|
||||
<span class="mat-form__hint">影响清晰度,一般不用改</span>
|
||||
</template>
|
||||
</NFormItemGi>
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@ defineExpose({ open })
|
||||
:disabled="readonly"
|
||||
/>
|
||||
<div class="org-drawer__fallback">
|
||||
留空表示不比对,填了才与名册比
|
||||
留空 = 不比对
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -328,7 +328,7 @@ defineExpose({ add, edit })
|
||||
<NFormItem label="登录账号" path="username">
|
||||
<NInput v-model:value="form.username" :disabled="isEdit" placeholder="如 zhangsan" clearable />
|
||||
<template v-if="isEdit" #feedback>
|
||||
<span class="uf__hint">登录名不可修改:改了会让历史记录与导出包里的账号对不上</span>
|
||||
<span class="uf__hint">不可修改:会让历史记录与导出包对不上</span>
|
||||
</template>
|
||||
</NFormItem>
|
||||
|
||||
@@ -340,7 +340,7 @@ defineExpose({ add, edit })
|
||||
<NInput v-model:value="form.phone" placeholder="11 位手机号" :maxlength="11" clearable />
|
||||
<template #feedback>
|
||||
<span class="uf__hint">
|
||||
{{ isEdit ? '列表只返回脱敏值,这里必须重填一次(加密存储)' : '加密存储,列表只显示脱敏值' }}
|
||||
{{ isEdit ? '必须重填一次(列表只给脱敏值)' : '加密存储,列表只显示脱敏值' }}
|
||||
</span>
|
||||
</template>
|
||||
</NFormItem>
|
||||
|
||||
Reference in New Issue
Block a user