u
This commit is contained in:
1 parent
7bd5f1dde9
commit
f3936cec61
70 files changed
+13074
-187
No files matched your search
@@ -70,6 +70,25 @@ description: 高性能虚拟滚动表格,支持排序、固定列、多选、
|
||||
|
||||
编辑器的输入值先写进内部草稿,**失焦才落库并抛出 `change`**。这样每敲一个字符不会触发父级重渲染(否则光标会丢、输入法会断)。用 `editable={false}` 可整体禁用编辑。
|
||||
|
||||
### 编辑器形态:单元格内无外间距、无常驻边框
|
||||
|
||||
默认(`editorSeamless`)下编辑器的**外间距为 0、自身不画常驻边框**,整格铺满、融进表格:
|
||||
|
||||
- 适合整行编辑、键盘连续录入、固定列宽、专业用户(Excel / AG Grid / Handsontable 的表格态);
|
||||
- 单元格几何在进入 / 离开编辑时**完全不变**——编辑器的盒高就等于 `rowHeight`,同一行的纯文本列不会跟着跳;
|
||||
- 水平内边距由单元格统一提供(编辑器和纯文本格用同一套网格),**进出编辑态零位移**;
|
||||
- 视觉提示只在**焦点、hover 与校验**时出现,且都不占用布局空间:
|
||||
- 焦点 → 单元格内嵌的主色焦点环(`td:focus-within` 的内阴影)+ 输入框自身的焦点光晕;
|
||||
- hover → 格内极淡填充;
|
||||
- 校验(`status` = `error` / `warning`)→ 文字色 + 1px 内描边。
|
||||
|
||||
```vue
|
||||
<!-- 回到「格内留一圈间距 + 自身描边」的悬浮卡片观感 -->
|
||||
<G3Table :editor-seamless="false" :columns="columns" :data-source="data" />
|
||||
```
|
||||
|
||||
两种形态的**编辑器盒高都等于 `rowHeight`**,所以切换真值不会改变行高。`textarea` 编辑器在默认的 30px 行里放不下正文,需要显式给足 `row-height`。
|
||||
|
||||
### ⚠ 数据源必须是响应式的
|
||||
|
||||
**行对象必须能被 Vue 追踪**,否则编辑回写不会触发重渲染。典型的踩坑写法是用 `computed` 派生数据:
|
||||
@@ -92,6 +111,15 @@ watch(n, (v) => { rows.value = Array.from({ length: v }, (_, i) => makeRow(i)) }
|
||||
|
||||
开发模式下,进入编辑时会检测并打印一条警告指明改法(每个组件实例只警告一次)。
|
||||
|
||||
### 编辑期的只读列
|
||||
|
||||
某一列没有配 `editor` 时,它在编辑期**不可修改**。为了让人一眼看出「这格点不动」,
|
||||
该行里**没有编辑器的格子会自动铺一层灰底**、文字也降一档亮度——
|
||||
不需要业务额外配任何东西,也不需要加锁图标。
|
||||
|
||||
灰底只出现在**正在编辑的那一行**(其余行保持原样),且用 `--td-bgc` 叠中性色得到,
|
||||
所以亮 / 暗主题自动跟随。
|
||||
|
||||
<demo vue="table/editable.vue" />
|
||||
|
||||
## 操作列
|
||||
@@ -129,6 +157,7 @@ watch(n, (v) => { rows.value = Array.from({ length: v }, (_, i) => makeRow(i)) }
|
||||
| `seq` | `boolean \| { title?, width? }` | `true` | 序号列 |
|
||||
| `activeRowKey` | `string \| number \| null` | `null` | 当前高亮行 |
|
||||
| `editable` | `boolean` | `true` | 行内编辑总开关 |
|
||||
| `editorSeamless` | `boolean` | `true` | 编辑器无缝嵌入单元格:格内无外间距、无常驻边框,只有焦点 / hover / 校验态才给视觉提示。传 `false` 回到留间距 + 自身描边的形态 |
|
||||
| `noDataFull` | `boolean` | `true` | 空态铺满容器高度 |
|
||||
| `showOverflow` | `boolean` | `true` | 内容溢出省略 |
|
||||
| `autoFitWidth` | `boolean` | `false` | 非横向虚拟时铺满容器宽度(开启后关闭列宽拖拽) |
|
||||
@@ -159,6 +188,47 @@ watch(n, (v) => { rows.value = Array.from({ length: v }, (_, i) => makeRow(i)) }
|
||||
| `allowClear` | `boolean` | select 是否可清空,默认 `true` |
|
||||
| `emptyValue` | `unknown` | select 清空后落库的值,默认 `null` |
|
||||
| `onChange` | `(value, row, col) => void` | 本列值变化回调(早于组件的 `change`) |
|
||||
| `validate` | `(value, row, col) => boolean \| string` | 校验规则:**返回 falsy 表示通过**,返回非空 string 表示不通过(该字符串即错误原因)。 |
|
||||
| `errorStyle` | `'cell' \| 'inline'` | 校验失败的展示形态,默认 `'cell'`。见下 |
|
||||
|
||||
### 校验(`validate`)
|
||||
|
||||
给列配 `validate` 即可获得**实时校验**:
|
||||
|
||||
```ts
|
||||
{
|
||||
dataIndex: 'qty',
|
||||
title: '采购数量',
|
||||
editor: {
|
||||
type: 'number',
|
||||
// 返回 falsy = 通过;返回 string = 不通过,字符串即错误原因
|
||||
validate: (value) => Number(value) > 0 || '采购数量必须大于 0',
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
三件需要知道的事:
|
||||
|
||||
- **返回值的语义是「falsy 通过 / string 不通过」**,不是「true 通过」。所以
|
||||
`validate: (v) => Number(v) > 0 || '必须大于 0'` 这种最自然的写法是对的;
|
||||
但若写成 `(v) => Number(v) > 0`(只返回布尔),则**永远不会报错**——
|
||||
布尔 `true` 被当作「通过」、`false` 也被当作「通过」。要让布尔参与判错,
|
||||
必须返回字符串。这一点容易踩,故单独说明。
|
||||
- **校验是派生值,不是一次性状态**:每次渲染都按当前行数据重算,
|
||||
所以用户把空值改成合法值后,红底与角标在下次渲染就自动消失,
|
||||
**不需要**业务手动清状态,组件内部也不维护任何 error 注册表。
|
||||
- **校验函数抛错会被吞掉**并降级为「通过」(仅 DEV 打印警告)。
|
||||
因为它在渲染期被调用,抛出去会让整格渲染失败,比「没有红底」严重得多。
|
||||
|
||||
#### 展示形态 `errorStyle`
|
||||
|
||||
| 值 | 效果 |
|
||||
| --- | --- |
|
||||
| `'cell'`(默认) | 单元格铺**浅红底** + 右上角 **`!` 角标**;聚焦时焦点环转为错误色 |
|
||||
| `'inline'` | 只在编辑器自身画文字色与 1px 内描边,**不**铺底、**不**加角标 |
|
||||
|
||||
浅红底取自设计原型,适合 ERP 明细这类以单元格为视觉单位的场景;
|
||||
若表格本身已经很密、或那一列很窄(角标会挤到文字),用 `'inline'` 更克制。
|
||||
|
||||
### `actions`
|
||||
|
||||
@@ -215,4 +285,5 @@ watch(n, (v) => { rows.value = Array.from({ length: v }, (_, i) => makeRow(i)) }
|
||||
- 没有圆角;`bordered: false` 时四边与格线全部消失。
|
||||
- **`--th-bgc` 等表头/表尾底色**由覆盖层的变量映射提供,不通过行内 style 注入。
|
||||
- **行编辑态存在 `WeakMap` 里**:按行对象存,不给业务数据打标记;行被回收时状态自动释放。行与行之间切换编辑**不翻转** `editingActive`,避免整表列重建。
|
||||
- **编辑器无缝嵌入单元格**(`editorSeamless`):把 `G3Input` / `G3Select` 自带的 `padding` / `border` / `border-radius` / `background` 置零,只保留焦点、hover 与校验提示;编辑器的高度由组件显式钉成 `var(--row-height)`(不能被控件的 `min-height: 32px` 顶高,也不能靠 `height: 100%` —— td 的内容盒不等于行高),因此**编辑前后的行高完全一致**。焦点环画在外层 `td` 的 `inset` 阴影上:不参与盒模型计算,几何零影响。
|
||||
- **RTL**:布局用逻辑属性,带符号位移走 `--g3-dir-sign`;分隔条拖拽与键盘方向由库的 `useDirection` 处理。
|
||||
@@ -10,6 +10,13 @@ import type { G3TableColumn, TableRow } from '@g3soft/ui'
|
||||
// select → 用 G3Select 渲染;options 可传数组,也可传「接收当前行的函数」
|
||||
// checkbox → 点击即切换,无需先激活整行;约定 1 = 选中、0 = 未选中(对齐后端 tinyint)
|
||||
//
|
||||
// 形态(editorSeamless,默认 true):**单元格内无外间距 + 编辑器无常驻边框**。
|
||||
// 编辑器整格铺满、融进表格,只有焦点(格内主色环)、hover(极淡填充)
|
||||
// 和校验(红色文字)才出现视觉提示——这是整行编辑 / 键盘连续录入 /
|
||||
// 固定列宽 / 专业用户(Excel、AG Grid)的表格态。
|
||||
// 传 :editor-seamless="false" 则回到「格内留一圈间距 + 自身描边」的悬浮卡片观感。
|
||||
// 两种形态下编辑器的盒高都等于行高,切换不会改变行高、也不会让同行其它格跳动。
|
||||
//
|
||||
// 关键行为:输入值先写进内部草稿,**失焦才落库并抛出 change**。
|
||||
// 这样每敲一个字符不会触发父级重渲染(否则光标会丢、输入法会断)。
|
||||
const columns: G3TableColumn<TableRow>[] = [
|
||||
@@ -73,7 +80,8 @@ function onChange(row: TableRow, col: G3TableColumn<TableRow>, value: unknown) {
|
||||
最近一次 change:{{ lastChange || '(点击单元格试试)' }}
|
||||
</p>
|
||||
<p style="margin: 0 0 8px; color: #999; font-size: 12px">
|
||||
单击进入编辑 · Enter 确认 · Esc 取消 · Tab / 方向键可换格
|
||||
单击进入编辑 · Enter 确认 · Esc 取消 · Tab / 方向键可换格 ·
|
||||
单元格内无外间距、无常驻边框,焦点时才出现主色焦点环
|
||||
</p>
|
||||
<div style="height: 240px">
|
||||
<G3Table :columns="columns" :data-source="data" :seq="false" @change="onChange" />
|
||||
|
||||
@@ -116,6 +116,7 @@ packages/ui/src/table/
|
||||
| `seq` | `boolean \| object` | `true` | 序号列 |
|
||||
| `activeRowKey` | `string \| number \| null` | `null` | 当前高亮行 |
|
||||
| `editable` | `boolean` | `true` | 行内编辑总开关 |
|
||||
| `editorSeamless` | `boolean` | `true` | 单元格内无外间距 + 无常驻边框(§5.3) |
|
||||
| `noDataFull` / `showOverflow` | `boolean` | `true` | 空态铺满 / 溢出省略 |
|
||||
| `autoFitWidth` | `boolean` | `false` | 非横向虚拟时铺满容器 |
|
||||
| `theme` | `'light' \| 'dark'` | `undefined` | 不传则跟随全局主题(**按 §4.5 从 ConfigProvider 取,不依赖 pinia**) |
|
||||
@@ -214,6 +215,36 @@ const displayColumns = computed(() => {
|
||||
|
||||
---
|
||||
|
||||
## 4.7 编辑器无缝嵌入单元格(`editorSeamless`,默认开启)
|
||||
|
||||
**形态**:单元格内部**无外间距** + 编辑器**无常驻边框**。适合整行编辑、键盘连续
|
||||
录入、固定列宽以及专业用户(Excel / AG Grid / Handsontable 的表格态)。
|
||||
|
||||
**目标**:单元格保持稳定的几何结构,编辑器融进表格,**只有焦点、校验和特殊
|
||||
交互状态才增强视觉提示**。
|
||||
|
||||
三条硬约束(改动这块前必读):
|
||||
|
||||
1. **编辑器盒高 = `var(--row-height)`**。td 是 border-box,指定 `height: 30px` 时
|
||||
内容盒只有 `30 - padding`,而子元素 `height: 100%` 解析的基准是**内容盒**。
|
||||
所以 td 垂直 padding 必须为 0,且编辑器不能靠 `height: 100%` 撑——
|
||||
`G3Select` 的触发器自带 `min-height: 32px`,在 30px 的行里会直接把行顶高 2px。
|
||||
显式 `height: var(--row-height)` + `min-height: 0` 把下限压死。
|
||||
2. **水平内边距只能由 td 提供**(库的 `--cell-padding-x`)。若改成给编辑器加
|
||||
透明边框,编辑器与纯文本格会各用一套逻辑像素取整,窄列下出现「编辑前文字
|
||||
偏移 1px」的漂移;td padding 对两者一视同仁,**进出编辑态零位移**。
|
||||
3. **无边框 ≠ 无提示**。焦点环画在**外层 td 的 `inset` 阴影**上(`:focus-within`):
|
||||
内阴影在 padding box 内侧绘制,不参与 td 自身的盒模型计算,几何绝对稳定,
|
||||
且天然铺满整格;hover 换成格内极淡填充,校验用文字色 + 1px 内描边。
|
||||
三者都不占用布局空间。
|
||||
|
||||
去零的是 `padding` / `border` / `border-radius` / `background`,
|
||||
**绝不动 height**;被移除的内边距由编辑器内层控件补回一次网格间距
|
||||
(`space-1` / `space-2`,与纯文本格同一档),因此「无间距」的收益是
|
||||
**格内不留空白**,而不是让文字失去网格对齐。
|
||||
|
||||
---
|
||||
|
||||
## 5. 与现有约定的对齐(必改项)
|
||||
|
||||
| 参考实现 | 我们必须改成 |
|
||||
|
||||
Reference in new issue
Block a user